/**********************************************************************
*
* Copyright (c) 2004-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: dagmap.c 5537 2006-10-09 03:39:25Z andras $
*
**********************************************************************/

/**********************************************************************
* FILE:         dagmap.c
* DESCRIPTION:  This module implements an hdlc mapper for the DAG3.7T.
*		It provides an api function to send frames and maps the
*		frames into the raw transmit model according to the
*		channel definition for the frame, bit stuffing and 
*		calculating the FCS as needed.
*
* HISTORY:
*       19-07-04 DNH 1.0  Initial version derived from hdlcgen.c
*
**********************************************************************/

/* Endace headers. */
#include "dagapi.h"
#include "dag37t_api.h"
#include "dagmapper.h"
#include "dag_platform.h"
#include "dagutil.h"
#include "dagclarg.h"

/* C Standard Library headers. */
#include <ctype.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#undef USE_MAPPER

/* CVS Header. */
static const char *const kCvsHeader __attribute__ ((unused)) = "$Id: dagmap.c 5537 2006-10-09 03:39:25Z andras $";
static const char *const kRevisionString = "$Revision: 5537 $";

#define DO_FCS		/* Enable FCS output in the ref file */

#define MAX_MAP_CHANNEL 512
#define MAX_CHARS 256          /* Max chars for input file lines */

#define BURST_MAX 0x100000   /* 1 MB */
#define DEFAULT_DEVICE "dag0"  /* default device */

#define UNUSED_TIMESLOT_FILLER 0x00

#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#define ULL     unsigned long long


static int dagstream;
static int run = 1;  /* used by signal handler to exit main loop. */
static int dagfd;


#define BIN_ARGS(data) \
	((data) & (1 << 7)) != 0, \
	((data) & (1 << 6)) != 0, \
	((data) & (1 << 5)) != 0, \
	((data) & (1 << 4)) != 0, \
	((data) & (1 << 3)) != 0, \
	((data) & (1 << 2)) != 0, \
	((data) & (1 << 1)) != 0, \
	((data) & (1 << 0)) != 0

/* Command line configuration */
typedef struct
{
    int argc;
    char **argv;
    char *device;               /* Dag device */
    char *infile;               /* Input file */
    char *chanfile;             /* Channel configuration file */
    char *errfile;              /* Channel error configuration file */
    uint32_t erf_count;
    uint32_t num_lines;
    uint32_t num_timeslots;
    int hex_out;                /* BOOLEAN: Output hex data instead of binary */
    int payload;
    int multiple_ones;
    int increment_start;
    double mo_prob;             /* Probablity of multiple-ones error occuring */
    int fcs_errors;
    double fcs_prob;            /* Probablity of fcs error occuring */
    int frame_size;             /* Maximum size of each frame */
    int frame_count;
    int send_total;				/* Send this many frames or cells per channel and exit, 0=infinate */
    int flag_count;             /* Maximum number of flags between frames */
    uint32_t seed;              /* Seed for random generator */
    int random_frame_size;
    int repeat_mode;
    uint32_t burst_count;       /* Burst buffer size */
    uint32_t delay;
    uint32_t burst_delay;	/* period to wait between sending bursts */
    int generate_ref;           /* Boolean generate erf or out */
    int generate_erf;
} t_config;



/* Channel types */
#define CT_UNDEFINED    0       /* Undefined */
#define CT_CHAN         1       /* One timeslot */
#define CT_HYPER        2       /* Multiple timeslots */
#define CT_SUB          3       /* sub-timeslot */

/* Channel states */
#define CS_OK           0
#define CS_EOD          1       /* No more data for channel */
#define CS_REPEAT       2       /* Channel is repeating output data */

#define MAX_TIMESLOT    31      /* Max timeslots per line */
#define MAX_TS_CHAN     8       /* Max number of channels per timeslot */
#define MIN_LINE        0       /* minimum line id */

typedef struct s_framebuf
{
    uint16_t size;
    uint16_t offset;            /* Final bit offset after stuffing */
    struct s_framebuf *next;
    int error_ind;
    int fcs_error;
    uint8_t data[4];
} t_framebuf;

#define FBUF_HEADER_SIZE 16

t_config config;                /* Command line configuration */
int verbose = 0;                /* verbose output */
int debug = 0;                  /* debug - enable to suppress idle channel output */

#define MAX_FILENAME_LEN 1024
char tmp_filename[MAX_FILENAME_LEN];
#define MAX_ERROR_LEN 256
char error_buf[MAX_ERROR_LEN];

/*
 * HDLC frame generation definitions
 */

#define HDLC_MAX_IDLE   20
#define HDLC_FCS_SIZE   2
#define HDLC_FLAG       0x7E
#define HDLC_MAX_SIZE   4096
#define HDLC_MIN_SIZE 10
#define HDLC_MAX_FRAME_COUNT 10000000

/* Generated and bit-stuffed frame buffer */
uint32_t hdlc_buf_size = 0;     /* Actual size used */

uint8_t hdlc_frame[HDLC_MAX_SIZE];
uint8_t atm_frame[128];
uint32_t min_timeslot = 1;
uint32_t max_timeslot = 31;

/* Data generation schemes */
#define CT_PAY_INCREMENT 1
#define CT_PAY_RANDOM    2
#define CT_PAY_LFSR      3

#define LFSR_ONE  0x40
#define LFSR_TWO  0x01
#define LFSR_SEED 0xFF

#ifdef DO_FCS
/*
 * FCS calculation from RFC1662 
 */

/*
* FCS lookup table as calculated by the table generator.
*/
static uint16_t fcstab[256] = {
    0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
    0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
    0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
    0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
    0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
    0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
    0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
    0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
    0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
    0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
    0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
    0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
    0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
    0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
    0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
    0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
    0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
    0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
    0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
    0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,

    0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
    0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
    0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
    0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
    0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
    0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
    0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
    0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
    0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
    0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
    0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
    0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};
#endif

FILE *stffile = NULL;

#define BUFSIZE 256
static char dagname_buf[DAGNAME_BUFSIZE] = "dag0";
static char dagname[DAGNAME_BUFSIZE];
static uint32_t brstcnt = 0;
static char fname_buf[DAGNAME_BUFSIZE];
static char fcserr_buf[DAGNAME_BUFSIZE];
static char startval_buf[DAGNAME_BUFSIZE];
static char multones_prob[DAGNAME_BUFSIZE];
int32_t framesize;
int32_t nframe;
uint32_t mapdelay;
uint32_t seed;
int32_t payloadtype;
uint32_t msecs;
int32_t nerfs;
uint32_t numlines;
uint32_t numslots;

static uint32_t chan_format[MAX_MAP_CHANNEL];	/* HDLC or ATM */
static int chan_format_count[8] = 
	{ 0, 0, 0, 0, 0, 0, 0, 0 };	/* Number of channels for each format */


/* Internal routines. */
static void print_version(void);
static void print_usage(ClArgPtr);
static void hg_scanInputs(t_config* config, int argc, char* argv[]);
static int hg_readChannelConfig(int dagfd, char* filename, uint32_t lines, uint32_t timeslots);
static int generateFrame(uint8_t* hdlc_frame, t_config* config, int* error_ind, int* fcs_error, FILE* reffile);
static void sighandler(int signal);


enum
{
	CLA_HELP,
	CLA_VERSION,
	CLA_DEVICE,
	CLA_VERBOSE,
	CLA_CHANCFG,
	CLA_NFRAME,
	CLA_SEED,
	CLA_FRAMESIZE,
	CLA_GENSIZE,
	CLA_PAYLOADTYPE,
	CLA_STARTVAL,
	CLA_MULTIPLE1S,
	CLA_FCSERR,
	CLA_GENREF,
	CLA_GENERF,
	CLA_WAIT,
	CLA_BRSTCNT,
	CLA_MAPDELAY,
	CLA_NERFS,
	CLA_REPEAT,
	CLA_NUMLINES,
	CLA_NUMSLOTS,
	CLA_SENDTOTAL
};

/* Implementation of internal routines. */
static void
print_version(void)
{
	printf("dagmap (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}


static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("Usage: dagmap [options]\n");
	dagclarg_display_usage(clarg, stdout);
}


/**********************************************************************
* FUNCTION:     hg_scanInputs(config, argc, argv)
* DESCRIPTION:  Read the command line options and fill in the config info.
* INPUTS:       config - pointer to the configuration info to fill in
*               argc   - number of command line tokens
*               argv   - command line token array
* OUTPUTS:      config populated with command line options
* RETURNS:      none
**********************************************************************/
static void
hg_scanInputs(t_config * config, int argc, char* argv[])
{
    char *endptr;
    struct timeval tv;
	ClArgPtr clarg = NULL;
	int argindex;
	int clarg_result;
	int code;
	FILE *errorfile = NULL;


	config->argc = argc;
    config->argv = argv;
    config->device = dagname;

    config->infile = NULL;
    config->erf_count = 0;
    config->chanfile = "chan.cfg";
    config->errfile = NULL;

    config->num_lines = 16;
    config->num_timeslots = 31;
    min_timeslot = 1;
    max_timeslot = 31;
    config->hex_out = 0;

    config->payload = CT_PAY_INCREMENT;
    config->increment_start = 1;
    config->multiple_ones = 0;
    config->fcs_errors = 0;
    config->frame_size = 100;
    config->frame_count = 10;
    config->flag_count = 1;
    config->generate_erf = 0;
    config->generate_ref = 0;
    config->random_frame_size = 1;
    config->repeat_mode = 0;
    config->burst_count = 2000;
    config->send_total = 0;
    config->delay = 0;
    config->burst_delay = 100000;	/* Default: 100msecs */

    gettimeofday (&tv, NULL);
    config->seed = tv.tv_sec;   /* Default random seed is number of seconds */

	/* 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_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_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add_string(clarg, "channel configuration file [default: chan.cfg]", "--chan_cfg", 'c', "channel_file", fname_buf, BUFSIZE, CLA_CHANCFG);
	dagclarg_add_int(clarg, "number of frames to generate per channel [10] (1 to 100000)", "--nframe", 'n', "frame_count", &nframe, CLA_NFRAME);
	dagclarg_add_uint(clarg, "seed for random generator [def=seconds]", "--seed", 'e', "seed", &seed, CLA_SEED);
	dagclarg_add_int(clarg, "maximum frame size for generated frames [100]", "-framesize", 's', "frame_size", &framesize, CLA_FRAMESIZE);
	dagclarg_add(clarg, "generate all frames of same size", "--gensize", 'g', CLA_GENSIZE);
	dagclarg_add_int(clarg, "1: incrementing, 2: random, 3: lfsr [incr]", "--payloadtype", 'p', "payload_type", &payloadtype, CLA_PAYLOADTYPE);
	dagclarg_add_string(clarg, "start value for -p1, hex 0 to ff [1]", "--startval", 'q', "start_value", startval_buf, BUFSIZE, CLA_STARTVAL);
	dagclarg_add_string(clarg, "inject multiple-ones errors (0.01 to 1.0)", "--multones", 'm', "probability", multones_prob, BUFSIZE, CLA_MULTIPLE1S);
	dagclarg_add_string(clarg, "inject FCS errors (0.01 to 1.0)", "--fcserr", 'f', "probability", fcserr_buf, BUFSIZE, CLA_FCSERR);
	dagclarg_add(clarg, "generate ref file", "--genref", 'j', CLA_GENREF);
	dagclarg_add(clarg, "generate erf file", "--generf", 'u', CLA_GENERF);
	dagclarg_add_uint(clarg, "number of usecs to wait between sending burst of frames [100]", "--wait", 'z', "microsecs", &msecs, CLA_WAIT);
	dagclarg_add_uint(clarg, "mapper thread: number of raw erfs to send per burst", "--brstcnt", 'b', "burst_count", &brstcnt, CLA_BRSTCNT);
	dagclarg_add_int(clarg, "exit after sending this many frames or cells", "--sendtotal", 'x', "sendtotal", &config->send_total, CLA_SENDTOTAL);
	dagclarg_add_uint(clarg, "mapper thread: number of usecs for mapper delay", "--mapdelay", 'k', "microsecs", &mapdelay, CLA_MAPDELAY);
	dagclarg_add_int(clarg, "mapper thread: total number of erfs to output (0 = continuous)", "--nerfs", 'o', "erf_count", &nerfs, CLA_NERFS);
	dagclarg_add(clarg, "mapper thread: repeat frames forever", "--repeat", 'r', CLA_REPEAT);
	dagclarg_add_uint(clarg, "default 16", "--numlines", 'l', "num_lines", &numlines, CLA_NUMLINES);
	dagclarg_add_uint(clarg, "default 31", "--numslots", 't', "num_timeslots", &numslots, CLA_NUMSLOTS);



	clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == clarg_result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg);
				exit(EXIT_SUCCESS);
				break;

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

			case CLA_VERSION:
				print_version();
				exit(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));
				}
				config->device = dagname;
				break;

			case CLA_CHANCFG:
				config->chanfile = fname_buf;
				break;

			case CLA_NFRAME:
				config->frame_count = nframe;
				break;

			case CLA_SEED:
				config->seed = seed;
				break;

			case CLA_FRAMESIZE:
				config->frame_size = framesize;
				break;

			case CLA_GENSIZE:
				config->random_frame_size = 0;
				break;

			case CLA_PAYLOADTYPE:
				config->payload = payloadtype;
				break;

			case CLA_STARTVAL:
				config->increment_start = strtol (startval_buf, &endptr, 16);
				if ((endptr == startval_buf) ||
					(config->increment_start < 0)
					|| (config->increment_start > 255))
				{
					fprintf (stderr,
							"Invalid value %s for option -q.  Must be between 0 and ff.\n",
							startval_buf);
					exit (EXIT_FAILURE);
				}
				break;

			case CLA_MULTIPLE1S:
				config->multiple_ones = 1;
				config->mo_prob = strtod (multones_prob, &endptr);
				if (endptr == multones_prob)
				{
					fprintf (stderr,
							"Invalid probability %s for option -m.  Must be between 0.0 and 1.0\n",
							multones_prob);
					exit (EXIT_FAILURE);
				}
				break;

			case CLA_FCSERR:
				config->fcs_errors = 1;
				config->fcs_prob = strtod (fcserr_buf, &endptr);
				if (endptr == fcserr_buf)
				{
					fprintf (stderr,
							"Invalid probability %s for option -f.  Must be between 0.0 and 1.0\n",
							fcserr_buf);
					exit (EXIT_FAILURE);
				}
				break;

			case CLA_GENREF:
	            config->generate_ref = 1;
				break;

			case CLA_GENERF:
				config->generate_erf = 1;
				break;

			case CLA_WAIT:
				config->burst_delay = msecs;
				break;

			case CLA_BRSTCNT:
				 config->burst_count = brstcnt;
				break;

			case CLA_MAPDELAY:
				config->delay = mapdelay;
				break;

			case CLA_NERFS:
				config->erf_count = nerfs;
				break;

			case CLA_SENDTOTAL:
				/* value already set by clarg function... */
				break;

			case CLA_REPEAT:
				 config->repeat_mode = 1;
				break;

			case CLA_NUMLINES:
				config->num_lines = numlines;
				break;

			case CLA_NUMSLOTS:
				config->num_timeslots = numslots;
                if (config->num_timeslots == 32)
				{
					/* Running unchannelised */
					min_timeslot = 0;
					max_timeslot = 31;
				}
				else
				{
					min_timeslot = 1;
					max_timeslot = config->num_timeslots;
					if (config->num_timeslots > MAX_TIMESLOT)
					{
						fprintf (stderr,
								"Error -t %d is greater than max timeslot %d\n",
								config->num_timeslots, MAX_TIMESLOT);
					}
				}
            	break;

			default:
				if (argv[argindex][0] == '-')
				{
					/* Unknown option. */
					dagutil_error("unknown option %s\n", argv[argindex]); 
					print_usage(clarg);
					exit(EXIT_FAILURE);
				}
				break;
		}
		clarg_result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

	if (-1 == clarg_result)
	{
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]);
		}
		dagclarg_display_usage(clarg, stderr);
		exit(EXIT_FAILURE);
	}

    if (config->chanfile == NULL)
    {
        fprintf (stderr,
                 "Error: missing -c channel configuration file name\n");
        exit(EXIT_FAILURE);
    }

    if ((config->payload < 1) || (config->payload > 3))
    {
        fprintf (stderr, "Error: payload type must be between 1 and 3 (%d)\n",
                 config->payload);
        exit(EXIT_FAILURE);
    }

    if ((config->frame_size < HDLC_MIN_SIZE) || (config->frame_size > 4096))
    {
        fprintf (stderr,
                 "Error: -s frame size must be between %d and 4096 (%d)\n",
                 HDLC_MIN_SIZE, config->frame_size);
        exit(EXIT_FAILURE);
    }

    if ((config->frame_count < 1)
        || (config->frame_count > HDLC_MAX_FRAME_COUNT))
    {
        fprintf (stderr, "Error: frame count must be between 1 and %d (%d)\n",
                 config->frame_count, HDLC_MAX_FRAME_COUNT);
        exit(EXIT_FAILURE);
    }

    if ((config->flag_count < 1) || (config->flag_count > 100))
    {
        fprintf (stderr,
                 "Error: -r flag count must be between 1 and 100 (%d)\n",
                 config->flag_count);
        exit(EXIT_FAILURE);
    }

}


#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

#define STATE_FLAGS     1
#define STATE_IN_FRAME  2
#define STATE_EOF       3

/**********************************************************************
* FUNCTION:     hg_readChannelConfig(filename, lines, timeslots)
* DESCRIPTION:  Read the channel configurations from the channel file, 
*               and fill in the channel array from it.
* INPUTS:       filename  - channel configuration file name
*               lines     - number of lines
*               timeslots - number of timeslots per line
* RETURNS:      number of channels
* GLOBALS:      channel array is populated with channels
**********************************************************************/
static int
hg_readChannelConfig(int dagfd, char *filename, uint32_t lines, uint32_t timeslots)
{
    FILE *cfile;
    char *res;
    char rc;
    int lineno = 0;
    uint32_t ts, line;
    char mask[16];
    uint32_t tsmap;
    uint32_t format;		/* channel format; 0=default, 1=HDLC, 2=ATM */
    int first_timeslot;

    char in[MAX_CHARS];         /* input line */
    int strind, tmpind;         /* Input string index */
    int saw_one = 0;
    uint16_t sub_size;
    uint8_t sub_first_bit;
    dag_channel_t channel_type;

    int chancount = 0;

    if ((cfile = fopen (filename, "r")) == NULL)
    {
        perror ("Failed to open channel config file");
        return -1;
    }

    /* read each channel configuration and add it to the channel list */
    while ((res = fgets (in, MAX_CHARS, cfile)) != NULL)
    {
        lineno++;
        switch (in[0])
        {
        case '#':              /* Comment line */
        case '\n':             /* empty line */
            break;

        case 'c':              /* 8-bit channel: format line timeslot */
            if (in[1] == 'r')
            {
                rc = sscanf (in, "cr %d %d %d ", &format, &line, &ts);
                channel_type = CT_CHAN_RAW;
            }
            else
            {
                rc = sscanf (in, "c %d %d %d ", &format, &line, &ts);
                channel_type = CT_CHAN;
            }
            if (rc != 3)
            {
                fprintf (stderr, "%s:%d - error parsing line (rc=%d)\n",
                         filename, lineno, rc);
                exit(EXIT_FAILURE);
            }

            if ((line < 0) || (line >= lines))
            {
                fprintf (stderr,
                         "%s:%d - invalid line id %d. Must be in the range 0 to %d.\n",
                         filename, lineno, line, lines);
                exit(EXIT_FAILURE);
            }

            if ((ts < min_timeslot) || (ts > max_timeslot))
            {
                fprintf (stderr,
                         "%s:%d - invalid timeslot %d. Must be in the range %d to %d.\n",
                         filename, lineno, ts, min_timeslot, max_timeslot);
                exit(EXIT_FAILURE);
            }

            if ((format < 0) || (format > 7))
            {
                fprintf (stderr,
                         "%s:%d - invalid format %d. Must be in the range 0 to 7.\n",
                         filename, lineno, format);
                exit(EXIT_FAILURE);
            }

	    chan_format[chancount] = format << 16;
	    chan_format_count[format]++;
#ifdef USE_MAPPER
            dag_mapper_add_channel(dagfd, (format<<16) | channel_type, line, ts, 0);
#else
            dag_add_channel (dagfd, DAG_DIR_TRANSMIT, (format<<16) | channel_type, line, ts, 1);
#endif
            chancount++;
            if (verbose >= 1)
                printf ("c-channel %-2d: line %d ts %d\n", chancount, line,
                        ts);
            break;

        case 's':              /* Subchannel: format line timeslot bitmask */
            if (in[1] == 'r')
            {
                rc = sscanf (in, "sr %d %d %d %8s", &format, &line, &ts,
                             mask);
                channel_type = CT_SUB_RAW;
            }
            else
            {
                rc = sscanf (in, "s %d %d %d %8s", &format, &line, &ts, mask);
                channel_type = CT_SUB;
            }
            if (rc != 4)
            {
                fprintf (stderr, "%s:%d - error parsing line (rc=%d)\n",
                         filename, lineno, rc);
                exit(EXIT_FAILURE);
            }

            if ((line < 0) || (line >= lines))
            {
                fprintf (stderr,
                         "%s:%d - invalid line id %d. Must be in the range 0 to %d.\n",
                         filename, lineno, line, lines);
                exit(EXIT_FAILURE);
            }

            if ((ts < min_timeslot) || (ts > max_timeslot))
            {
                fprintf (stderr,
                         "%s:%d - invalid timeslot %d. Must be in the range %d to %d.\n",
                         filename, lineno, ts, min_timeslot, max_timeslot);
                exit(EXIT_FAILURE);
            }

            if ((format < 0) || (format > 7))
            {
                fprintf (stderr,
                         "%s:%d - invalid format %d. Must be in the range 0 to 7.\n",
                         filename, lineno, format);
                exit(EXIT_FAILURE);
            }

            /*
             * convert the input bit string to a binary mask
             */
            tsmap = 0;
            saw_one = 0;
            sub_size = 0;
            sub_first_bit = 0;
            for (strind = 0; strind < 8; strind++)
            {
                tsmap <<= 1;
                if ((mask[strind] != '0') && (mask[strind] != '1'))
                {
                    fprintf (stderr, "%s:%d - invalid subchannel mask %s\n",
                             filename, lineno, mask);
                    exit(EXIT_FAILURE);
                }
                // tsmap |= (mask[strind] == '1');
                if (mask[strind] == '1')
                {
                    tsmap |= 1;
                    sub_size++;
                    sub_first_bit = (7 - strind);       /* want first bit from left */

                    if (saw_one == 0)
                    {
                        saw_one = 1;    /* first one */
                    }
                    else if (saw_one == 2)
                    {
                        /* Error - non-contiguous subchannel mask */
                        fprintf (stderr,
                                 "%s:%d - Error channel %d line %d ts %d.  Non-contiguous subchannel %s\n",
                                 filename, lineno, chancount + 1, line, ts,
                                 mask);
                        return -1;
                    }
                }
                else
                {
                    if (saw_one == 1)
                        saw_one = 2;    /* Saw one followed by a zero */
                }
            }

	    chan_format[chancount] = format << 16;
	    chan_format_count[format]++;
#ifdef USE_MAPPER
            dag_mapper_add_channel(dagfd, (format<<16) | channel_type, line, ts, tsmap);
#else
            dag_add_channel (dagfd, DAG_DIR_TRANSMIT, (format<<16) | channel_type, line,
                             (ts << 16) | tsmap, 1);
#endif
            chancount++;
            if (verbose >= 1)
                printf
                    ("s-channel %-2d: line %d ts %d mask 0x%02x size %d fb %d\n",
                     chancount, line, ts, tsmap, sub_size, 8 - sub_first_bit);
            break;

        case 'h':              /* Hyperchannel: line timeslot_list */
            strind = 0;
            if (in[1] == 'r')
            {
                rc = sscanf (in, "hr %d %d %n", &format, &line, &strind);
                channel_type = CT_HYPER_RAW;
            }
            else
            {
                rc = sscanf (in, "h %d %d %n", &format, &line, &strind);
                channel_type = CT_HYPER;
            }
            if ((rc < 2) || (strind == 0))
            {
                fprintf (stderr,
                         "%s:%d - error parsing line (rc=%d, strind=%d)\n",
                         filename, lineno, rc, strind);
                exit(EXIT_FAILURE);
            }

            if ((line < 0) || (line >= lines))
            {
                fprintf (stderr,
                         "%s:%d - invalid line id %d. Must be in the range 0 to %d.\n",
                         filename, lineno, line, lines);
                exit(EXIT_FAILURE);
            }

            if ((format < 0) || (format > 7))
            {
                fprintf (stderr,
                         "%s:%d - invalid format %d. Must be in the range 0 to 7.\n",
                         filename, lineno, format);
                exit(EXIT_FAILURE);
            }

            /*
             * Read the timeslot list and convert it to a bit mask
             */
            tsmap = 0;
            tmpind = 0;
            first_timeslot = -1;
            while (sscanf (&in[strind], " %d %n", &ts, &tmpind) > 0)
            {
                if ((ts < min_timeslot) || (ts > max_timeslot))
                {
                    fprintf (stderr,
                             "%s:%d - invalid timeslot %d. Must be in the range %d to %d.\n",
                             filename, lineno, ts, min_timeslot,
                             max_timeslot);
                    exit(EXIT_FAILURE);
                }
                if (first_timeslot < 0)
                {
                    first_timeslot = ts;
                }
                tsmap |= (1 << ts);
                if (verbose >= 2)
                    printf ("    ts: %d  map: 0x%08x (strind=%d)\n", ts,
                            tsmap, strind);
                strind += tmpind;
            }

	    chan_format[chancount] = format << 16;
	    chan_format_count[format]++;
#ifdef USE_MAPPER
            dag_mapper_add_channel(dagfd, (format<<16) | channel_type, line,
                                    first_timeslot, tsmap);
#else
            dag_add_channel (dagfd, DAG_DIR_TRANSMIT, (format<<16) | channel_type, line,
                             tsmap, 1);
#endif
            chancount++;

            if (verbose >= 1)
                printf ("h-channel %-2d: line %d ts-map 0x%08x\n", chancount,
                        line, tsmap);
            break;

        case 'l':              /* Line: line */
            if (in[1] == 'r')
            {
                rc = sscanf (in, "lr %d %d", &format, &line);
                channel_type = CT_HYPER_RAW;
            }
            else
            {
                rc = sscanf (in, "l %d %d", &format, &line);
                channel_type = CT_HYPER;
            }
            if (rc != 2)
            {
                fprintf (stderr,
                         "%s:%d - error parsing line (rc=%d, strind=%d)\n",
                         filename, lineno, rc, strind);
                exit(EXIT_FAILURE);
            }

            if ((line < 0) || (line >= lines))
            {
                fprintf (stderr,
                         "%s:%d - invalid line id %d. Must be in the range 0 to %d.\n",
                         filename, lineno, line, lines);
                exit(EXIT_FAILURE);
            }

            if ((format < 0) || (format > 7))
            {
                fprintf (stderr,
                         "%s:%d - invalid format %d. Must be in the range 0 to 7.\n",
                         filename, lineno, format);
                exit(EXIT_FAILURE);
            }

	    chan_format[chancount] = format << 16;
	    chan_format_count[format]++;
#ifdef USE_MAPPER
            dag_mapper_add_channel(dagfd, (format<<16) | channel_type, line, 0,
                                   0xFFFFFFFF);
#else
            dag_add_channel (dagfd, DAG_DIR_TRANSMIT, (format<<16) | channel_type, line,
                             0xFFFFFFFF, 1);
#endif
            chancount++;

            if (verbose >= 1)
                printf ("l-channel %-2d: line %d ts-map 0xffffffff\n",
                        chancount, line);
            break;

        default:
            fprintf (stderr, "%s:%d - unrecognized line\n", filename, lineno);
            exit(EXIT_FAILURE);
            break;
        }
    }

    fclose (cfile);

    return (chancount);
}

static uint8_t incrementing_data = 0;
static uint8_t incrementing_data_atm = 0;

static int generateFrameATM(
    uint8_t *atm_frame,
    t_config *config,
    FILE *reffile)
{
    static int frame_count = 0;

    int ind = 0;
    uint32_t refind = 0;        /* Reference file index */
    int frame_size=53;		/* ATM Frame size */

    uint8_t data;

    int fdcnt;                  /* frame data count */

    /* Output command line for reference */
    if (reffile)
    {
        fprintf (reffile, "#");
        for (ind = 0; ind < config->argc; ind++)
        {
            fprintf (reffile, " %s", config->argv[ind]);
        }
        fprintf (reffile, "\n");
    }


    /* Seed the pseudo-random generator */
    /* Select initial data byte value */
    switch (config->payload)
    {
    case CT_PAY_INCREMENT:
        data = incrementing_data_atm;
        break;
    case CT_PAY_RANDOM:
        data = (uint8_t) (256.0 * rand () / (RAND_MAX + 1.0));
        break;
    case CT_PAY_LFSR:
    default:
        data = LFSR_SEED;
        break;
    }

    /* Frame */
    for (fdcnt = 0; fdcnt < frame_size; fdcnt++)
    {

        /* Bit-stuff the data and add it to the hdlc frame at the
           current bit offset */
        if (verbose > 2)
            printf ("add 0x%02x at %d\n", data, fdcnt);
        atm_frame[fdcnt] = data;


        if (fdcnt < (frame_size - 1))
        {
            if (reffile)
                fprintf (reffile, "%-5d %c%c%c%c %c%c%c%c %02x\n",
                         refind++,
                         '0' + ((data >> 7) & 0x01),
                         '0' + ((data >> 6) & 0x01),
                         '0' + ((data >> 5) & 0x01),
                         '0' + ((data >> 4) & 0x01),
                         '0' + ((data >> 3) & 0x01),
                         '0' + ((data >> 2) & 0x01),
                         '0' + ((data >> 1) & 0x01), '0' + (data & 0x01),
                         data);
        }
        else
        {
            if (reffile)
                fprintf (reffile, "%-5d %c%c%c%c %c%c%c%c %02x EOF %d\n",
                         refind++,
                         '0' + ((data >> 7) & 0x01),
                         '0' + ((data >> 6) & 0x01),
                         '0' + ((data >> 5) & 0x01),
                         '0' + ((data >> 4) & 0x01),
                         '0' + ((data >> 3) & 0x01),
                         '0' + ((data >> 2) & 0x01),
                         '0' + ((data >> 1) & 0x01), '0' + (data & 0x01),
                         data, frame_count);
        }


        /* Generate next data byte */
        switch (config->payload)
        {
        case CT_PAY_INCREMENT:
            data = ++incrementing_data_atm;
            break;
        case CT_PAY_RANDOM:
            data = (uint8_t) (256.0 * rand () / (RAND_MAX + 1.0));
            break;
        case CT_PAY_LFSR:
        default:
            data = (data >> 1) |
                ((((data & LFSR_ONE) || (data & LFSR_TWO)) &&
                  !((data & LFSR_ONE) && (data & LFSR_TWO))) ? 0x80 : 0);
            break;
        }
    }

    return (frame_size);
}

static int generateFrame(uint8_t* hdlc_frame,
              t_config* config,
              int* error_ind, int* fcs_error, FILE* reffile)
{
    static int frame_count = 0;

    int ind = 0;
    uint32_t refind = 0;        /* Reference file index */
    int frame_size;             /* Current random frame size */
    int flag_count;
    int fcs_error_count = 0;
    int mo_error_count = 0;

    uint8_t data;
#ifdef DO_FCS
    uint16_t fcs;
    uint8_t tmp;
#endif

    int fdcnt;                  /* frame data count */
    int add_fcs_error = 0;      /* Boolean */
    int add_mo_error = 0;       /* Boolean */

    frame_count++;

    /* Output command line for reference */
    if (reffile)
    {
        fprintf (reffile, "#");
        for (ind = 0; ind < config->argc; ind++)
        {
            fprintf (reffile, " %s", config->argv[ind]);
        }
        fprintf (reffile, "\n");
        fprintf (reffile, "%-5d 0111 1110 %02x\n", refind++, HDLC_FLAG);
    }


    /* Seed the pseudo-random generator */
    /* Select initial data byte value */
    switch (config->payload)
    {
    case CT_PAY_INCREMENT:
        data = incrementing_data;
        break;
    case CT_PAY_RANDOM:
        data = (uint8_t) (256.0 * rand () / (RAND_MAX + 1.0));
        break;
    case CT_PAY_LFSR:
    default:
        data = LFSR_SEED;
        break;
    }

    /* Select a random number of flags for inter-frame spacing */
    flag_count = 1 + (int) ((double) (config->flag_count - 1) * rand () /
		       (RAND_MAX + 1.0));

    if (config->random_frame_size)
    {
        frame_size = 0;
        while (frame_size == 0)
        {
            /* select a random frame size */
            frame_size = HDLC_MIN_SIZE +
                (uint16_t) (((double) config->frame_size -
                             HDLC_MIN_SIZE) * rand () / (RAND_MAX + 1.0));
        }
    }
    else
    {
        frame_size = config->frame_size;
    }


    /* Output a flag just to the ref file */
    if (reffile)
    {
        if (frame_count == 0)
        {
            fprintf (reffile, "%-5d 0111 1110 %02x Frame %d seed %d",
                     refind++, HDLC_FLAG, frame_count, config->seed);
        }
        else
        {
            fprintf (reffile, "%-5d 0111 1110 %02x Frame %d",
                     refind++, HDLC_FLAG, frame_count);
        }
    }

    /* Calculate probabilities regardless of options to ensure
     * repeatable random payloads for a specific seed */
    add_mo_error = (rand () > (RAND_MAX * config->mo_prob));
    *error_ind = (int) ((double) frame_size * rand () / (RAND_MAX + 1.0));
    /* Check for error injection */
    if ((config->multiple_ones) && (add_mo_error))
    {
        if (verbose)
            printf ("Injecting multiple-ones error in frame %d, byte %d\n",
                    frame_count, *error_ind);
        if (reffile)
            fprintf (reffile, " (multi-one error @ %d)", *error_ind);
        mo_error_count++;
    }
    else
    {
        *error_ind = 0;
    }

    add_fcs_error = (rand () > (RAND_MAX * config->fcs_prob));
    if ((config->fcs_errors) && add_fcs_error)
    {
        if (verbose)
            printf ("Injecting FCS error in frame %d\n", frame_count);
        if (reffile)
            fprintf (reffile, " (fcs error)");
        *fcs_error = 1;
        fcs_error_count++;
    }
    else
    {
        *fcs_error = 0;
    }

    if (reffile)
        fprintf (reffile, "\n");


    /* Frame */
#ifdef DO_FCS
    fcs = 0xFFFF;
#endif
    for (fdcnt = 0; fdcnt < frame_size; fdcnt++)
    {

        /* Bit-stuff the data and add it to the hdlc frame at the
           current bit offset */
        if (verbose > 2)
            printf ("add 0x%02x at %d\n", data, fdcnt);
        hdlc_frame[fdcnt] = data;

#ifdef DO_FCS
        /* calculate fcs on-the-fly */
        fcs = (fcs >> 8) ^ fcstab[(fcs ^ data) & 0xff];
#endif

        if (fdcnt < (frame_size - 1))
        {
            if (reffile)
                fprintf (reffile, "%-5d %c%c%c%c %c%c%c%c %02x\n",
                         refind++,
                         '0' + ((data >> 7) & 0x01),
                         '0' + ((data >> 6) & 0x01),
                         '0' + ((data >> 5) & 0x01),
                         '0' + ((data >> 4) & 0x01),
                         '0' + ((data >> 3) & 0x01),
                         '0' + ((data >> 2) & 0x01),
                         '0' + ((data >> 1) & 0x01), '0' + (data & 0x01),
                         data);
        }
        else
        {
            if (reffile)
                fprintf (reffile, "%-5d %c%c%c%c %c%c%c%c %02x EOF %d\n",
                         refind++,
                         '0' + ((data >> 7) & 0x01),
                         '0' + ((data >> 6) & 0x01),
                         '0' + ((data >> 5) & 0x01),
                         '0' + ((data >> 4) & 0x01),
                         '0' + ((data >> 3) & 0x01),
                         '0' + ((data >> 2) & 0x01),
                         '0' + ((data >> 1) & 0x01), '0' + (data & 0x01),
                         data, frame_count);
        }


        /* Generate next data byte */
        switch (config->payload)
        {
        case CT_PAY_INCREMENT:
            data = ++incrementing_data;
            break;
        case CT_PAY_RANDOM:
            data = (uint8_t) (256.0 * rand () / (RAND_MAX + 1.0));
            break;
        case CT_PAY_LFSR:
        default:
            data = (data >> 1) |
                ((((data & LFSR_ONE) || (data & LFSR_TWO)) &&
                  !((data & LFSR_ONE) && (data & LFSR_TWO))) ? 0x80 : 0);
            break;
        }
    }

#ifdef DO_FCS
    /* add on output */
    fcs ^= 0xffff;              /* complement */

    /* Generate an FCS error? */
    if (*fcs_error)
    {
        fcs += 0x4242;
    }

    /* Output the FCS to the ref file only? */
    tmp = (uint8_t) (fcs & 0xFF);
    if (reffile)
        fprintf (reffile, "%-5d %c%c%c%c %c%c%c%c %02x CRC lo\n",
                 refind++,
                 '0' + ((tmp >> 7) & 0x01), '0' + ((tmp >> 6) & 0x01),
                 '0' + ((tmp >> 5) & 0x01), '0' + ((tmp >> 4) & 0x01),
                 '0' + ((tmp >> 3) & 0x01), '0' + ((tmp >> 2) & 0x01),
                 '0' + ((tmp >> 1) & 0x01), '0' + (tmp & 0x01), tmp);
    tmp = (uint8_t) ((fcs >> 8) & 0xFF);
    if (reffile)
        fprintf (reffile, "%-5d %c%c%c%c %c%c%c%c %02x CRC hi\n",
                 refind++,
                 '0' + ((tmp >> 7) & 0x01), '0' + ((tmp >> 6) & 0x01),
                 '0' + ((tmp >> 5) & 0x01), '0' + ((tmp >> 4) & 0x01),
                 '0' + ((tmp >> 3) & 0x01), '0' + ((tmp >> 2) & 0x01),
                 '0' + ((tmp >> 1) & 0x01), '0' + (tmp & 0x01), tmp);
#endif

    if (reffile)
        fprintf (reffile, "%-5d 0111 1110 %02x (last frame)\n", refind++,
                 HDLC_FLAG);

    return (frame_size);
}


static void
sighandler(int signal)
{
	run = 0;
	dag_mapper_stop(dagfd);
}


int
dagmap_main(int argc, char* argv[])
{
	int count, burst;
	int num_channels;
	int error_ind = 0;
	int fcs_error = 0;
	int frame_size;
	int chan;
	int res;
	int chan_level;
	int active_channel_count=0;
	int sent_one = 0;	/* Debug to trap failure to feed channels */
	uint32_t level, chan_needs[MAX_MAP_CHANNEL], chan_sent[MAX_MAP_CHANNEL];
	FILE* reffile = NULL;

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

	struct sigaction sigact;    /* signal catching */
	
	/* Set up default DAG device. */
	if (-1 == dag_parse_name(DEFAULT_DEVICE, dagname, DAGNAME_BUFSIZE, &dagstream))
	{
		dagutil_panic("dag_parse_name(%s): %s\n", DEFAULT_DEVICE, strerror(errno));
	}

	memset(chan_sent, 0, sizeof(uint32_t) * MAX_MAP_CHANNEL);

	/* Catch signals. */
	sigact.sa_handler = sighandler;
	sigemptyset (&sigact.sa_mask);
	sigact.sa_flags = 0;
	if (sigaction (SIGHUP, &sigact, NULL) < 0)
	{
		fprintf (stderr, "sigaction SIGHUP: %s\n", strerror (errno));
		exit (EXIT_FAILURE);
	}
	if (sigaction (SIGINT, &sigact, NULL) < 0)
	{
		fprintf (stderr, "sigaction SIGINT: %s\n", strerror (errno));
		exit (EXIT_FAILURE);
	}
	if (sigaction (SIGTERM, &sigact, NULL) < 0)
	{
		fprintf (stderr, "sigaction SIGTERM: %s\n", strerror (errno));
		exit (EXIT_FAILURE);
	}
	if (sigaction (SIGPIPE, &sigact, NULL) < 0)
	{
		fprintf (stderr, "sigaction SIGPIPE: %s\n", strerror (errno));
		exit (EXIT_FAILURE);
	}

#elif defined(_WIN32)

	/* Note: under Windows SIGHUP and SIGPIPE don't exist */
	signal (SIGINT, sighandler);
	signal (SIGTERM, sighandler);

#endif /* Platform-specific code. */

	printf("\nEndace HDLC Mapper Version 1.0\n(c) 2004 Endace Technology Ltd.\n\n");
	
	memset (&config, 0, sizeof(config));
	hg_scanInputs (&config, argc, argv);

	srand(config.seed);
	
	dag_mapper_set_verbosity (verbose > 3 ? verbose - 3 : 0);
	
	if (config.generate_erf)
	{
		dag_mapper_set_debug (DM_DEBUG_ERF_FILE);
	}
	
	if ((dagfd = dag_open (config.device)) < 0)
	{
		fprintf (stderr, "dag_open %s: %s\n", config.device,
				 strerror (errno));
		exit (EXIT_FAILURE);
	}

	res = dag_mapper_open (dagfd, config.device, config.burst_count);
	if (res < 0)
	{
		fprintf (stderr, "Failed to open mapper\n");
		dag_close (dagfd);
		exit (EXIT_FAILURE);
	}
	
	num_channels = hg_readChannelConfig(dagfd, config.chanfile,
							config.num_lines, config.num_timeslots);
	
	if (num_channels < 0)
	{
		fprintf (stderr, "Error parsing channel config file %s\n",
				 config.chanfile);
		exit(EXIT_FAILURE);
	}
	
	if (num_channels == 0)
	{
		fprintf (stderr, "Error no channels found in config file %s\n",
				 config.chanfile);
		exit(EXIT_FAILURE);
	}

	active_channel_count = num_channels;
	
	/* send frames */
	printf ("\t%d channels configured from %s\n\n", num_channels,
			config.chanfile);
	if (config.hex_out)
		printf ("\tHexidecimal output format enabled\n");
	
	
	printf ("Starting mapper, burst=%d, delay=%d\n", config.burst_count,
			config.delay);
	dag_mapper_start(dagfd, config.burst_count, config.erf_count,
					  config.delay);
	
	sleep (1);
	
	if (config.generate_ref)
	{
		/* Create reference file name */
		if ((reffile = fopen ("dagmap.ref", "w")) == NULL)
		{
			perror ("Failed to open hdlc reference output file");
			exit(EXIT_FAILURE);
		}
	}
	
	printf ("Mapper running\n");
	
	
	incrementing_data = config.increment_start;
	incrementing_data_atm = config.increment_start;
	if (config.repeat_mode)
	{
		/* The sent frames are repeated forever in order by each channel */
		printf ("Repeat mode\n");
		printf ("Preloading %d frames per channel (%d total)\n",
				config.frame_count, config.frame_count * num_channels);
		dag_mapper_set_debug (DM_DEBUG_REPEAT);
		for (count = 0; count < config.frame_count; count++)
		{
			frame_size = generateFrame(hdlc_frame, &config, &error_ind, &fcs_error, reffile);
			if (chan_format_count[2]) generateFrameATM(atm_frame, &config, reffile);
			for (chan = 0; chan < num_channels; chan++)
			{
			    switch (chan_format[chan]) {
				case CT_FMT_DEFAULT:
				case CT_FMT_HDLC:
				    if (verbose > 1) printf("chan %d: Sending HDLC frame\n", chan+16);
				    dag_mapper_send_debug_frame(dagfd, chan + 16, hdlc_frame,
							     frame_size, error_ind, fcs_error,
							     0, NULL);
				    break;
				case CT_FMT_ATM:
				    if (verbose > 1) printf("chan %d: Sending ATM frame\n", chan+16);
				    dag_mapper_send_debug_frame_atm(dagfd, chan + 16, atm_frame, 0, 0, NULL);
				    break;
			    }
			}
		}
	
		printf ("Sleeping while mapper runs\n");
		while (run)
			sleep (1);
	}
	else
	{
		while (run)
		{
			level = dag_mapper_get_level (dagfd);
			count = num_channels * config.frame_count;

			if (verbose > 1) printf("count=%d, level=%d, send_total=%d, acc=%d\n", count, level, config.send_total, active_channel_count);
			if ((config.send_total) && (active_channel_count == 0)) {
				/* just waiting for all channels to finish sending */
				if (level == 0) {
					if (verbose) printf("Finished sending %d frames per channel\n", config.send_total);
					break;
				} else {
					if (verbose) printf("Finished generating %d frames, waiting send to complete (%d left total)\n",
									config.send_total, level);
				}
			} else {

				if ((signed)level <= count) {
					/* Maintain a level of (frame_count * num_channels) frames */
					if ((verbose > 1) || ((verbose) && (level < (count/2))))
						printf ("Sending %d frames (level=%d)\n", count-level, level);

					count = count - level; /* Absolute number of frames needed */

					/* Work out how many frames each channel needs */
					active_channel_count = 0;
					for (chan = 0; chan < num_channels; chan++) {
						chan_level = dag_mapper_get_channel_level(dagfd, chan+16);

						if (chan_level < 0) printf("ARG! channel level %d\n", chan_level);

						chan_needs[chan] = (config.frame_count - dag_mapper_get_channel_level(dagfd, chan+16));

						/* if the channel is near the send total then ensure it sends exactly send_total
						 * frames */
						if (config.send_total && ((config.send_total - chan_sent[chan]) < chan_needs[chan])) {
							if (verbose) printf("reducing channel %d need from %d to %d\n",
											chan, chan_needs[chan], (config.send_total - chan_sent[chan]));
							chan_needs[chan] = config.send_total - chan_sent[chan];
						}

						if ((verbose > 1) || ((verbose) && (chan_needs[chan] > (config.frame_count/2))))
								printf ("chan %d: needs %d (chan_level %d - %d)\n",
									 chan, chan_needs[chan], config.frame_count, chan_level);

						/* Count each channel that is still being sent frames */
						if ((config.send_total) && (chan_sent[chan] < config.send_total)) active_channel_count++;
					}

					if (verbose) printf("active channel count %d\n", active_channel_count);

					/* Send an identical burst of frames to each channel */
					for (burst = 0; burst < count; ) {
						/* Generate one new frame */
						frame_size = generateFrame(hdlc_frame, &config, &error_ind, &fcs_error, reffile);
						if (chan_format_count[2]) generateFrameATM(atm_frame, &config, reffile);

						/* Send the frame to each channel */
						sent_one = 0;
						for (chan = 0; chan < num_channels; chan++) {
							if (chan_needs[chan]) {
								switch (chan_format[chan]) {
								case CT_FMT_DEFAULT:
								case CT_FMT_HDLC:
									if (verbose > 1) printf("chan %d: Sending HDLC frame\n", chan+16);
									dag_mapper_send_debug_frame(dagfd, chan + 16, hdlc_frame,
												 frame_size, error_ind, fcs_error,
												 0, NULL);
									break;

								case CT_FMT_ATM:
									if (verbose > 1) printf("chan %d: Sending ATM frame\n", chan+16);
									dag_mapper_send_debug_frame_atm(dagfd, chan + 16, atm_frame, 0, 0, NULL);
									break;
								}
								burst++;
								chan_sent[chan]++;
								chan_needs[chan]--;
								sent_one = 1;
							}
						}

						if (sent_one == 0) {
						    if (!config.send_total) {
								printf("ARG! Tried to top up the channels and didn't send a frame\n");
								printf("Recovering by breaking out of feeder loop (%d frames left)\n", count-burst);
						    }
						    break;
						}
					}
				} else {
					if (verbose)
						printf ("Skipping send: level=%d (%d per channel)\n", level, level/num_channels);
				}
			}
		
			/* Sleep until the next burst period */
			usleep (config.burst_delay);
		}
	}

	dag_mapper_stop(dagfd);

	if (reffile)
		fclose (reffile);
	dag_mapper_close (dagfd);
	dag_close (dagfd);

	if (verbose > 1) {
		for (chan = 0; chan < num_channels; chan++) {
			if (chan_sent[chan]) printf("dm-chan %d: sent %d packets\n", chan, chan_sent[chan]);
		}
	}
	
	return EXIT_SUCCESS;
}


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