/*
 *
 * Copyright 2011 Karsten Keil <kkeil@linux-pingi.de>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called LICENSE.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/udp.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <stdint.h>
#include <getopt.h>
#include <time.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <mISDN/mISDNif.h>
#include <mISDN/af_isdn.h>

#define	FRAME_SIZE	32
#define	SMFR_SIZE	(8 * FRAME_SIZE)
#define MFR_SIZE	(2 * SMFR_SIZE)
#define DATA_SIZE_125US	32
#define DATA_SIZE_1MS	(8 * 32)
#define DATA_SIZE_1S	(8000 * 32)


#define MFR_SYNC_VALUE	0x2c
#define MFR_SYNC_MASK	0xfc
#define MFR_SYNC_BIT	0x01
#define MFR_SYNC_BITS	8
#define MFR_SYNC_OFFSET	15

#define FR_FAS_VAL	0xd8
#define FR_FAS_VAL_FAIL	0xc0
#define FR_FAS_MASK	0xfe

#define FR_NFAS_B2	0xfa
#define FR_NFAS_B2_FAIL	0xf8

enum FrameTypes {
	ftNone,
	/* send out AIS (all TS 0xff) */
	ftAIS,
	/* Basic Frames */
	ftFAS,
	ftBIT2,
	/* Sub multi frames */
	ftSMFA,
	ftSMFB,
	/* Multi frames */
	ftMFA,
	ftMFB,
	/* Frames */
	ftFRAME_A,
	ftFRAME_B,
	ftFRAME_C,
	/* Control items */
	ftCtrl_Start,
	ftCtrl_Repeat,
	ftCtrl_End,
	ftCtrl_Stop
};

struct fr_cdesc {
	enum FrameTypes type;
	uint8_t		prop;
	uint8_t		subcnt;
	uint16_t	count;
};

struct fr_flatdesc {
	enum FrameTypes type;	/* only basic frame types */
	uint8_t		prop;
	enum FrameTypes otype;	/* original type from generation */
	uint8_t		mf_pos;	/* 0 - 15 */
	uint32_t	pos;
	uint8_t		*data;
};

#define ftPROP_NONE	0x00
#define ftPROP_FAIL	0x01	/* frame type failure (/FAS or /BIT2) */
#define ftPROP_CRC4	0x02	/* CRC4 failure in submultiframe */
#define ftPROP_MFAS	0x04	/* MFAS failure in multiframe */
#define ftPROP_TS31	0x08	/* simulate BIT2 (FAS in TS 0) and FAS (BIT2 in TS0) in TS 31 */
#define ftPROP_START	0x10	/* Start test */
#define ftPROP_AIS	0x80	/* AIS */

struct fr_data {
	int			count;
	struct fr_flatdesc	*desc;
	size_t			data_size;
	uint8_t			*data;
};

static struct fr_data *TestData;

struct fr_cdesc preamble[] = {
	{ftAIS, ftPROP_AIS, 1, 16000},
	{ftSMFA, ftPROP_NONE, 8, 1100},
	{ftCtrl_Stop, ftPROP_NONE, 0, 0}
};

struct fr_cdesc test0[] = {
	{ftSMFA, ftPROP_NONE, 8, 10000},
	{ftCtrl_Stop, ftPROP_NONE, 0, 0}
};

struct fr_cdesc test1[] = {
	{ftSMFA, ftPROP_NONE, 8, 1500},
	{ftCtrl_Start, ftPROP_START, 0, 0},
	{ftSMFA, ftPROP_NONE, 8, 500},
	{ftSMFB, ftPROP_CRC4, 8, 1},
	{ftSMFA, ftPROP_NONE, 8, 1000},
	{ftSMFB, ftPROP_CRC4, 8, 2},
	{ftSMFA, ftPROP_NONE, 8, 1500},
	{ftSMFB, ftPROP_CRC4, 8, 914},
	{ftSMFA, ftPROP_NONE, 8, 86},
	{ftSMFB, ftPROP_CRC4, 8, 914},
	{ftSMFA, ftPROP_NONE, 8, 2000},
	{ftSMFB, ftPROP_CRC4, 8, 915},
	{ftSMFA, ftPROP_NONE, 8, 85},
	{ftSMFB, ftPROP_CRC4, 8, 915},
	{ftSMFA, ftPROP_NONE, 8, 1500},
	{ftCtrl_Repeat, ftPROP_NONE, 0, 4000},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},
	{ftCtrl_Stop, ftPROP_NONE, 0, 0}
};

struct fr_cdesc test2[] = {
	{ftCtrl_Repeat, ftPROP_NONE, 0, 9000},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftCtrl_Start, ftPROP_START, 0, 0},

	{ftCtrl_Repeat, ftPROP_NONE, 0, 1000},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},

	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_FAIL, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},
	
	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_FAIL, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_FAIL, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_FAIL, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftCtrl_Repeat, ftPROP_NONE, 0, 20},
	{ftBIT2, ftPROP_FAIL, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_FAIL, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftBIT2, ftPROP_FAIL, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftCtrl_Repeat, ftPROP_NONE, 0, 200},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_NONE, 1, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},
	
	{ftBIT2,ftPROP_NONE, 1, 1},
	{ftFRAME_B, ftPROP_TS31, 2, 16000},
	{ftFRAME_C, ftPROP_TS31, 2, 6},
	{ftFRAME_B, ftPROP_TS31, 2, 16000},

	{ftCtrl_Stop, ftPROP_NONE, 0, 0}
};

struct fr_cdesc test3[] = {
	{ftFRAME_B, ftPROP_TS31, 2, 15000},

	{ftCtrl_Start, ftPROP_START, 0, 0},

	{ftFRAME_B, ftPROP_TS31, 2, 1000},

	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 4},
	{ftMFA, ftPROP_NONE, 16, 1},

	{ftMFB, ftPROP_NONE, 16, 37},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 251},

	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftMFB, ftPROP_NONE, 16, 250},
	
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 4},

	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 2},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 2},
	{ftMFA, ftPROP_NONE, 16, 1},
	
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 2},
	{ftMFA, ftPROP_NONE, 16, 2},
	
	{ftCtrl_Repeat, ftPROP_NONE, 0, 500},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftCtrl_Stop, ftPROP_NONE, 0, 0}
};


struct fr_cdesc test4[] = {
	{ftFRAME_B, ftPROP_TS31, 2, 16000},

	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 4},
	{ftMFA, ftPROP_NONE, 16, 1},

	{ftMFB, ftPROP_NONE, 16, 37},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 251},

	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftFAS, ftPROP_FAIL, 1, 1},
	{ftBIT2, ftPROP_NONE, 1, 1},
	{ftMFB, ftPROP_NONE, 16, 250},
	
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 4},

	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 2},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 2},
	{ftMFA, ftPROP_NONE, 16, 1},
	
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftMFB, ftPROP_NONE, 16, 2},
	{ftMFA, ftPROP_NONE, 16, 2},
	
	{ftCtrl_Repeat, ftPROP_NONE, 0, 500},
	{ftMFB, ftPROP_NONE, 16, 1},
	{ftMFA, ftPROP_NONE, 16, 1},
	{ftCtrl_End, ftPROP_NONE, 0, 0},

	{ftCtrl_Stop, ftPROP_NONE, 0, 0}
};


static uint8_t mfr_sync;
static uint8_t startcnt = MFR_SYNC_BITS;

static uint8_t	*inbuf, *ib_p, *ib_end;
static int	ib_size, ib_pos;
static uint8_t	*outbuf, *ob_p, *ob_end;
static int	ob_size, ob_pos;


enum FSyncState {
	FSync_None,
	FSync_FAS,
	FSync_NFAS,
	FSync_AIS,
	FSync_LOS
};
enum MFRSyncState {
	MFR_State_NotSync,
	MFR_State_Sync
};


static enum FSyncState fsync_state = FSync_None;
static enum MFRSyncState mfr_state = MFR_State_NotSync;

static uint8_t	*last_mfrs;

static int good_mfr, bad_mfr;


static int debuglevel = 0;
static int RawReadMode = 0;
static int cardnr = 0;
static char * WriteFileName = NULL;
static int ListMode = 0;
struct fr_cdesc *Test = test0;
static void usage(char *pname)
{
	fprintf(stderr,"Call with %s [options]\n", pname);
	fprintf(stderr,"\n");
	fprintf(stderr,"\n     Valid options are:\n");
	fprintf(stderr,"\n");
	fprintf(stderr,"  --help    -?              Usage ; printout this information\n");
	fprintf(stderr,"  --card   -c <number>      use card number # (default 0)\n");
	fprintf(stderr,"  --flat   -f               list flat test frame description\n");
	fprintf(stderr,"  --list   -l               list test frame description\n");
	fprintf(stderr,"  --raw    -r               rawread only mode\n");
	fprintf(stderr,"  --debug  -d <level>       debuglevel\n");
	fprintf(stderr,"  --test   -t <test #>      generate data for test # (default 0)\n");
	fprintf(stderr,"  --write  -w <file>        write <file>\n");
	fprintf(stderr,"\n");
	fprintf(stderr,"Tests:\n");
	fprintf(stderr,"      0  - 10 seconds normal CRC4 framing\n");
	fprintf(stderr,"      1  - TBR4 B.4.2 (table B.1)\n");
	fprintf(stderr,"      2  - TBR4 B.5.2 (table B.2)\n");
	fprintf(stderr,"      3  - TBR4 B.5.3 (table B.3)\n");
	fprintf(stderr,"\n");
}

static int opt_parse(int ac, char *av[])
{
	int c;

	for (;;) {
		int option_index = 0;
		static struct option long_options[] = {
			{"help", 0, 0, '?'},
			{"card", 1, 0, 'c'},
			{"debug", 1, 0, 'd'},
			{"flat", 0, 0, 'f'},
			{"list", 0, 0, 'l'},
			{"raw", 0, 0, 'r'},
			{"test", 1, 0, 't'},
			{"write", 1, 0, 'w'},
			{0, 0, 0, 0}
		};

		c = getopt_long(ac, av, "?c:d:flrt:w:", long_options, &option_index);
		if (c == -1)
			break;
		switch (c) {
		case 0:
			fprintf(stderr, "option %s", long_options[option_index].name);
			if (optarg)
				fprintf(stderr, " with arg %s", optarg);
			fprintf(stderr, "\n");
			break;
		case 'c':
			if (optarg)
				cardnr = atoi(optarg);
			else {
				fprintf(stderr, "option -c but no card number\n");
				return -2;
			}
			break;
		case 'w':
			if (optarg)
				WriteFileName = strdup(optarg);
			else {
				fprintf(stderr, "option -w but no filename\n");
				return -2;
			}
			break;
		case 'd':
			if (optarg) {
				debuglevel = atoi(optarg);
			} else {
				fprintf(stderr, "option -d but no value for debug level\n");
				return -3;
			}
			break;
		case 'l':
			ListMode = 1;
			break;
		case 'f':
			ListMode = 2;
			break;
		case 'r':
			RawReadMode = 1;
			break;
		case 't':
			if (optarg) {
				switch(*optarg) {
				case '0':
					Test = test0;
					break;
				case '1':
					Test = test1;
					break;
				case '2':
					Test = test2;
					break;
				case '3':
					Test = test3;
					break;
				default:
					fprintf(stderr, "Unknown test %s\n", optarg);
					return -4;
				}
			} else {
				fprintf(stderr, "option -t but no value for test\n");
				return -3;
			}
			break;
		case '?':
			usage(av[0]);
			return -1;
		}
	}
	c = ac - optind;
	if (c != 0) {
		fprintf(stderr, "unknown options: %s\n", av[optind]);
		return -2;
	}
	return 0;
}




static void printbinary(char *name, uint32_t val, uint8_t bits)
{
	uint32_t m;

	if (bits > 32)
		bits = 32;

	printf("%s: ", name);

	m = 1 << (bits -1);
	while (m) {
		printf("%c", val & m ? '1' : '0');
		m >>= 1;
	}
}

static void printhex(unsigned char *p, uint16_t idx, int len, int head)
{
	int i, j;

	for (i = 1; i <= len; i++) {
		printf(" %02x", p[idx++]);
		if ((i!=len) && !(i % 4) && (i % 16))
			printf(" ");
		if ((i!=len) && !(i % 16)) {
			printf("\n");
			for (j = 0; j < head; j++)
				printf(" ");
		}
	}
	printf("\n");
}

/* table for bit swap */
uint8_t bitswap_tbl[256];

/* table for CRC4 (x^4 + x + 1, Generator poly 0x13) */
uint8_t crc4_tbl[16] = { 0x0, 0x3, 0x6, 0x5, 0xc, 0xf, 0xa, 0x9, 0xb, 0x8, 0xd, 0xe, 0x7, 0x4, 0x1, 0x2 };

void calc_bitswap_table(void)
{
	uint8_t v = 0;

	do {
		bitswap_tbl[v] =  (((v & 0x01) << 7) | ((v & 0x02) << 5) | ((v & 0x04) << 3) | ((v & 0x08) << 1) |
			((v & 0x10) >> 1) | ((v & 0x20) >> 3) | ((v & 0x40) >> 5) | ((v & 0x80) >> 7));
		v++;
	} while (v != 0);
}

static uint8_t calc_crc4(uint8_t *data, uint16_t start, int len)
{
	uint16_t i, idx = start;
	uint8_t  b, crc4 = 0;

	for (i = 0; i < len; i++) {
		b = bitswap_tbl[data[idx]];
		crc4 ^= ((b >> 4) & 0xf);
		crc4 = crc4_tbl[crc4];
		crc4 ^= (b & 0xf);
		crc4 = crc4_tbl[crc4];
		idx++;
	}
	return crc4;
};

static uint8_t calc_and_set_crc4_smf(uint8_t *smf, uint8_t crc)
{
	uint16_t i;
	uint8_t  d, b, crc4 = 0;

	for (i = 0; i < SMFR_SIZE; i++) {
		d = smf[i];
		if (!(i % 64)) { /* Cx bit */
			d &= 0xfe;
			b = bitswap_tbl[d];
			if (crc != 0xff) { /* no set if ff */
				if (crc & 8)
					d |= 1;
				crc <<= 1;
				smf[i] = d;
			}
		} else
			b = bitswap_tbl[d];
		crc4 ^= ((b >> 4) & 0xf);
		crc4 = crc4_tbl[crc4];
		crc4 ^= (b & 0xf);
		crc4 = crc4_tbl[crc4];
	}
	return crc4 & 0xf;
}

#ifdef NOT_USED_YET
static uint8_t get_crc4_smf(uint8_t *smf)
{
	uint16_t i;
	uint8_t crc4 = 0;

	for (i = 0; i < 4; i++) {
		crc4 <<= 1;
		crc4 |= (smf[64 * i] & 1);
	}
	return crc4;
}
#endif

static int cur_Err = 0;// cur_RAI = 0;
static int analyse_mfr(uint8_t *p)
{
	uint16_t i, t0idx;
	uint8_t d, crcfr[16];
	uint8_t A = 0;
	uint8_t E = 0;
	uint8_t C = 0;
	uint8_t cr0, cr1;
	const char *stim;
	struct fr_flatdesc *dsc;

	size_t ipos;

	ob_pos = ob_p - outbuf;
	ipos = p - inbuf;
	if (ipos < ((MFR_SYNC_OFFSET + 12) * FRAME_SIZE)) {
		stim = "VOID";
	} else {
		ipos -= ((MFR_SYNC_OFFSET + 12) * FRAME_SIZE);
		ipos /= FRAME_SIZE;
		dsc = &TestData->desc[ipos];
		switch (dsc->otype) {
		case ftAIS:
			stim = "AIS";
			break;
		/* Basic Frames */
		case ftFAS:
			if (dsc->prop & ftPROP_FAIL)
				stim = "/FAS";
			else
				stim = "FAS";
			break;
		case ftBIT2:
			if (dsc->prop & ftPROP_FAIL)
				stim = "/BIT 2";
			else
				stim = "BIT 2";
			break;
		/* Sub multi frames */
		case ftSMFA:
			stim = "SMF A";
			break;
		case ftSMFB:
			stim = "SMF B";
			break;
		/* Multi frames */
		case ftMFA:
			stim = "MF A";
			break;
		case ftMFB:
			stim = "MF B";
			break;
		/* Frames */
		case ftFRAME_A:
			stim = "FRAME A";
			break;
		case ftFRAME_B:
			stim = "FRAME B";
			break;
		case ftFRAME_C:
			stim = "FRAME C";
			break;
		default:
			stim = "VOID";
			break;
		}
	}
	
	printf("Time:%6d ms {TX: %-7s} MFR %5d Error %3d ", ob_pos/DATA_SIZE_1MS, stim, good_mfr, bad_mfr);
	last_mfrs = p;
	p -= MFR_SYNC_OFFSET * FRAME_SIZE;
	for (i = 0; i < 16; i++) {
		t0idx = i * 32;
		d = p[t0idx];
		crcfr[i] = d;
		if (i & 1) {
			A <<= 1;
			if (d & 0x04)
				A |= 1;
			if (i >= 13) {
				E <<= 1;
				if (d & 0x01)
					E |= 1;
			}
		} else {
			C <<= 1;
			C |= ( 0x1 & d);
		}
	}
	cr0 = calc_and_set_crc4_smf(p, 0xff);
	cr1 = calc_and_set_crc4_smf(p + 256, 0xff);

	switch (E) {
	case 0:
		cur_Err += 2;
		break;
	case 1:
		cur_Err++;
		break;
	case 2:
		cur_Err = 1;
		break;
	case 3:
		cur_Err = 0;
		break;
	}
	printbinary("A", A, 8);
	printbinary(" C", C, 8);
	printbinary(" CR0", cr0, 4);
	printbinary(" CR1", cr1, 4);
	printbinary(" E", E, 2);
	printf(" Cerr %4d", cur_Err);
	printf("    ");
	printhex(crcfr, 0, 16, 0);
	if (E == 1)
		cur_Err = 0;
	return 0;
}


/*
 * Write one multiframe 16 * 2048 bit ; 512 bytes
 * returns the CRC4 for the next sub multiframe
 */
uint8_t fill_outframe(uint8_t *of, uint8_t A, uint8_t E, uint8_t S, uint8_t F, uint8_t cr0, uint8_t MFRSW, uint8_t *dch, uint8_t def)
{
	uint16_t i, tidx;
	static unsigned char p;
	uint8_t c0, c1, j;

	tidx = 0;
	for (i = 0; i < 16; i++) {
		if (i & 1) { /* NFAS */
			p = 0xf8 & S;
			if (A & 0x80)
				p |= 4;
			A <<= 1;
			p |= 2;
			if (i >= 13) {
				if (E & 0x2)
					p |= 1;
				E <<= 1;
			} else {
				if (MFRSW & 0x80)
					p |= 1;
				MFRSW <<= 1;
			}
		} else {
			p = F & 0xfe;
		}
		of[tidx] = p;
		tidx++;
		for (j = 1; j < 32; j++) {
			if (j == 16) { /* D channel */
				of[tidx] = *dch;
				dch++;
			} else
				of[tidx] = def;
			tidx++;
		}
	}
	c0 = calc_crc4(of, 0, 256);
	c1 = calc_crc4(of, 256, 256);
	for (i = 0; i < 16; i += 2) {
		tidx = 32 * i;
		p = of[tidx];
		if (i < 8) {
			if (cr0 & 0x8)
				p |= 1;
			cr0 <<= 1;
		} else {
			if (c0 & 0x8)
				p |= 1;
			c0 <<= 1;
		}
		of[tidx] = p;
	}
	return c1;
}

static uint8_t *ib_cp;

static int process_data(int cnt)
{
	uint16_t dist, nidx;
	uint8_t i;
	static uint8_t fr[5];
	static enum FSyncState frT[5];

	if (debuglevel == 2)
		printf("Start %d  %p/%p ", cnt, ib_cp, ib_p);
	while (1) {
		dist = ib_p - ib_cp;
		if (dist <= 400)
			break;
		switch (fsync_state) {
		case FSync_None:
		case FSync_AIS:
		case FSync_LOS:
			/* hunt for basic sync */
			fr[0] = ib_cp[0];
			if ((fr[0] & FR_FAS_MASK) != FR_FAS_VAL) {
				ib_cp++;
				continue;
			} else {
				frT[0] = FSync_FAS;
				if (debuglevel == 2)
					printf("F");
				nidx = 32;
				fr[1] = ib_cp[nidx];
				if (!(fr[1] & 0x2)) {
					ib_cp++;
					continue;
				} else
					frT[1] = FSync_NFAS;
				if (debuglevel == 2)
					printf("N");
				nidx = 64;
				fr[2] = ib_cp[nidx];
				if ((fr[2] & FR_FAS_MASK) != FR_FAS_VAL) {
					ib_cp++;
					continue;
				} else
					frT[2] = FSync_FAS;
				if (debuglevel == 2)
					printf("f %p/%d", ib_cp, dist);
				for (i = 3; i < 5; i++) {
					nidx = i * 32;
					fr[i] = ib_cp[nidx];
					if ((fr[i] & FR_FAS_MASK) == FR_FAS_VAL) {
						frT[i] = FSync_FAS;
					} else if (fr[i] & 0x2) {
						frT[i] = FSync_NFAS;
					} else
						frT[i] = FSync_None;
				}
			}
			break;
		default:
			for (i = 0; i < 4; i++) {
				fr[i] = fr[i + 1];
				frT[i] = frT[i + 1];
			}
			/* i = 4 */
			nidx = i * 32;
			fr[i] = ib_cp[nidx];
			if ((fr[i] & FR_FAS_MASK) == FR_FAS_VAL) {
				frT[i] = FSync_FAS;
			} else if (fr[i] & 0x2) {
				frT[i] = FSync_NFAS;
			} else
				frT[i] = FSync_None;
			break;
		}
		switch (frT[0]) {
		case FSync_FAS:
			switch (fsync_state) {
			case FSync_None:
			case FSync_AIS:
			case FSync_LOS:
				fsync_state = FSync_FAS;
				startcnt = MFR_SYNC_BITS;
				mfr_sync = 0;
				break;
			case FSync_NFAS:
				fsync_state = FSync_FAS;
				break;
			case FSync_FAS:
				if ((frT[2] != FSync_NFAS) && (frT[4] != FSync_NFAS)) {
					/* 3 wrong NFAS received - sync lost */
					fsync_state = FSync_LOS;
				} else {
					/* assume we did receive a NFAS, with wrong Bit 2 */
					fsync_state = FSync_NFAS;
				}
				break;
			}
			break;
		case FSync_NFAS:
			switch (fsync_state) {
			case FSync_None:
			case FSync_AIS:
			case FSync_LOS:
				/* cannot happen, since sa new sync always starts with FAS */
				fprintf(stderr, "Line %d: Wrong state %d while processing NFAS\n", __LINE__, fsync_state);
				exit(1);
				break;
			case FSync_FAS:
				fsync_state = FSync_NFAS;
				break;
			case FSync_NFAS:
				if ((frT[2] != FSync_FAS) && (frT[4] != FSync_FAS)) {
					/* 3 wrong FAS received - sync lost */
					fsync_state = FSync_LOS;
				} else {
					/* assume we did receive a wrong coded FAS */
					fsync_state = FSync_FAS;
				}
				break;
			}
			break;
		case FSync_None:
			switch (fsync_state) {
			case FSync_None:
			case FSync_AIS:
			case FSync_LOS:
				/* cannot happen, since a new sync always starts with FAS */
				fprintf(stderr, "Line %d: Wrong state %d\n", __LINE__, fsync_state);
				exit(1);
				break;
			case FSync_FAS:
				if ((frT[2] != FSync_NFAS) && (frT[4] != FSync_NFAS)) {
					/* 3 wrong NFAS received - sync lost */
					fsync_state = FSync_LOS;
				} else {
					/* assume we did receive a NFAS, with wrong Bit 2 */
					fsync_state = FSync_NFAS;
				}
				break;
			case FSync_NFAS:
				if ((frT[2] != FSync_FAS) && (frT[4] != FSync_FAS)) {
					/* 3 wrong FAS received - sync lost */
					fsync_state = FSync_LOS;
				} else {
					/* assume we did receive a wrong coded FAS */
					fsync_state = FSync_FAS;
				}
				break;
			}
			break;
		default: /* not possible here */
			fprintf(stderr, "Line %d: Wrong state %d\n", __LINE__, frT[0]);
			exit(1);
			break;
		}
		/* check for multi frame sync now */
		switch (fsync_state) {
		case FSync_LOS:
			/* start new sync hunting */
			continue;
		case FSync_NFAS:
			mfr_sync <<= 1;
			mfr_sync |= (fr[0] & MFR_SYNC_BIT);
			if (startcnt == 0) {
				dist = ib_cp - last_mfrs;
				if ((mfr_sync & MFR_SYNC_MASK) == MFR_SYNC_VALUE) {
					/* sync found */
					if (mfr_state == MFR_State_NotSync) {
						mfr_state = MFR_State_Sync;
						good_mfr = 1;
						analyse_mfr(ib_cp);
					} else  if (dist == 16 * 32) {
						good_mfr++;
						analyse_mfr(ib_cp);
					}
				} else {
					if (mfr_state == MFR_State_Sync) {
						if (dist == 16 * 32) {
							bad_mfr++;
						} else if (dist == 32 * 32) {
							mfr_state = MFR_State_NotSync;
						}
					}
				}
			} else
				startcnt--;
			break;
		case FSync_FAS:
			break;
		default:
			fprintf(stderr, "Wrong state %d\n", fsync_state);
			exit(1);
			break;
		}
		ib_cp += 32;
	}
	if (debuglevel == 2)
		printf("\n");
	return 0;
}

static int fill_buffer(unsigned char *p, int len)
{

	ib_pos = ib_p - inbuf;
	if ((ib_pos + len) >= ib_size)
		len = ib_size - ib_pos;
	memcpy(ib_p, p, len);
	ib_p += len;
	return len;
}

/* returns next start frame */
static uint8_t *fill_timeslot(uint8_t *p0, int ts, uint8_t *data, int datalen, int repeat)
{
	uint8_t *p = p0;
	int i, cnt = repeat;

	while (cnt) {
		for (i = 0; i < datalen; i++) {
			p[ts] = data[i];
			p += FRAME_SIZE;
		}
		cnt--;
	}
	return p;
}

uint8_t *fill_timeslot_ts0_smf(uint8_t *p0, int ts, int smf, uint8_t fas_err, uint8_t nfas_err, uint8_t mfsw,
	uint8_t S, uint8_t A, uint8_t C, uint8_t E, int repeat)
{
	uint8_t *p = p0;
	uint8_t i, d[8], a, c, e, n, f, m;

	while (repeat) {
		e = E;
		if (smf & 1) {
			a = A & 0xf;
			c = C & 0xf;
			n = nfas_err & 0xf;
			f = fas_err & 0xf;
			m = mfsw & 0xf;
		} else {
			a = A >> 4;
			c = C >> 4;
			n = nfas_err >> 4;
			f = fas_err >> 4;
			m = mfsw >> 4;
		}
		for (i = 0; i < 8; i++) {
			if (i & 1) { /* NFAS */
				if (n & 8) /* nfas error */
					d[i] = 0;
				else
					d[i] = 2;
				n <<= 1;
				if (i > 4 && (smf & 1)) {
					if (e & 2)
						d[i] |= 1;
					e <<= 1;
				} else {
					if (m & 8)
						d[i] |= 1;
					m <<= 1;
				}
				d[i] |= (S & 0xF8);
				if (a & 8)
					d[i] |= 4;
				a <<= 1;
			} else { /* FAS */
				if (f & 8) /* FAS error */
					d[i] = 0xc8;
				else
					d[i] = FR_FAS_VAL;
				f <<= 1;
				if (c & 8)
					d[i] |= 1;
				c <<= 1;
			}
		}
		p = fill_timeslot(p, ts, d, 8, 1);
		smf++;
		repeat--;
	}
	return p;
}

static uint8_t *calc_and_set_crc4_ts(uint8_t *start, uint8_t ts, uint8_t *first_last_crc, int smf_count)
{
	uint8_t crc = *first_last_crc & 0xf;
	uint8_t wrong_crc = *first_last_crc & 0xf0;
	uint8_t *p = start;

	while (smf_count) {
		if (wrong_crc)
			crc = crc + 2;
		crc = calc_and_set_crc4_smf(&p[ts], crc & 0xf);
		p += SMFR_SIZE;
		smf_count--;
	}
	*first_last_crc = crc;
	return p;
}

static int transmit(int sock, uint8_t *ob, uint16_t len)
{
	struct msghdr	mh;
	struct iovec	iov[2];
	int ret;
	struct mISDNhead hh = {PH_DATA_REQ, 1};

	mh.msg_name = NULL;
	mh.msg_namelen = 0;
	mh.msg_iov = iov;
	mh.msg_iovlen = 2;
	mh.msg_control = NULL;
	mh.msg_controllen = 0;
	mh.msg_flags = 0;
	iov[0].iov_base = &hh;
	iov[0].iov_len = MISDN_HEADER_LEN;
	iov[1].iov_base = ob;
	iov[1].iov_len = len;
	ret = sendmsg(sock, &mh, 0);
	if (ret != (len + MISDN_HEADER_LEN)) {
		fprintf(stderr, "Send error %d (%d + %d) - %s\n", ret,
			(int)MISDN_HEADER_LEN, len, strerror(errno));
		ret = 0;
	} else
		ret = len;
	return ret;
}

#define MAX_RECV_BUFFER_SIZE	4200
static int start_transmit = 1;
static int last_dlen = 64;

static int receive_ts0dch(int socket)
{
	int ret, cnt, head;
	uint8_t	buffer[MAX_RECV_BUFFER_SIZE];
	struct mISDNhead *hh = (struct mISDNhead *)buffer;

	ret = recv(socket, buffer, MAX_RECV_BUFFER_SIZE, 0);
	if (ret < 0) {
		fprintf(stderr, "read error %s\n", strerror(errno));
		return ret;
	}
#if 0
	if (cts.cmsg_type == MISDN_TIME_STAMP) {
		mt = localtime((time_t *)&cts.tv.tv_sec);
		head = printf("%02d.%02d.%04d %02d:%02d:%02d.%06ld", mt->tm_mday, mt->tm_mon + 1, mt->tm_year + 1900,
			mt->tm_hour, mt->tm_min, mt->tm_sec, cts.tv.tv_usec);
	} else {
		cts.tv.tv_sec = 0;
		cts.tv.tv_usec = 0;
	}
#endif
	head = 0;
	switch (hh->prim) {
	case PH_DATA_IND:
		last_dlen = ret - MISDN_HEADER_LEN;
		if (!RawReadMode) {
			if (start_transmit && RawReadMode == 0) {
				ob_p += transmit(socket, ob_p, last_dlen);
				start_transmit = 0;
			}
			cnt = fill_buffer(&buffer[MISDN_HEADER_LEN], last_dlen);
			process_data(cnt);
			if ((mfr_state == MFR_State_NotSync && debuglevel == 3) || debuglevel == 4) {
				head = printf("Got %3d bytes ", last_dlen);
				printhex(buffer, MISDN_HEADER_LEN, cnt, head);
			}
		} else {
			head = printf("Got %3d bytes ", last_dlen);
			printhex(buffer, MISDN_HEADER_LEN, last_dlen, head);
		}
		break;
	case PH_DATA_CNF:
		if (!RawReadMode) {
			ob_pos = ob_p - outbuf;
			if ((ob_pos + last_dlen) >= ob_size) {
				ret = -1; /* stop */
				last_dlen = ob_size - ob_pos;
			}
			if (debuglevel == 5) {
				head = printf("Send %3d bytes ", last_dlen);
				printhex(ob_p, 0, last_dlen, head);
			}
			ob_p += transmit(socket, ob_p, last_dlen);
		}
		break;
	}
	return ret;
}

/* test description sub routines */
static const char *printhead[] = {
	"",
	"    ",
	"        ",
	"            ",
	"                ",
	"                    ",
	"                        ",
	"                            ",
	"                                "
};

static void print_testdescription(struct fr_cdesc *arg)
{
	struct fr_cdesc *dsc = arg;
	int head = 0;

	while (dsc) {
		if (dsc->type == ftNone) {
			fprintf(stdout, "unexpected end of description\n");
			break;
		} else if (dsc->type == ftCtrl_Start) {
			fprintf(stdout, "Start of test sequence\n");
		} else if (dsc->type == ftCtrl_Stop) {
			fprintf(stdout, "End of test\n");
			break;
		} else if (dsc->type == ftCtrl_Repeat) {
			fprintf(stdout, "%sdo %d times {\n", printhead[head], dsc->count);
			head++;
		} else if (dsc->type == ftCtrl_End) {
			head--;
			fprintf(stdout, "%s}\n", printhead[head]);
		} else {
			fprintf(stdout, "%s%4d ", printhead[head], dsc->count);
			switch (dsc->type) {
			case ftAIS:
				fprintf(stdout, "%-8s", "AIS");
				break;
			/* Basic Frames */
			case ftFAS:
				if (dsc->prop & ftPROP_FAIL)
					fprintf(stdout, "%-8s", "/FAS");
				else
					fprintf(stdout, "%-8s", "FAS");
				break;
			case ftBIT2:
				if (dsc->prop & ftPROP_FAIL)
					fprintf(stdout, "%-8s", "/BIT 2");
				else
					fprintf(stdout, "%-8s", "BIT 2");
				break;
			/* Sub multi frames */
			case ftSMFA:
				fprintf(stdout, "%-8s", "SMF A");
				break;
			case ftSMFB:
				fprintf(stdout, "%-8s", "SMF B");
				break;
			/* Multi frames */
			case ftMFA:
				fprintf(stdout, "%-8s", "MF A");
				break;
			case ftMFB:
				fprintf(stdout, "%-8s", "MF B");
				break;
			/* Frames */
			case ftFRAME_A:
				fprintf(stdout, "%-8s", "FRAME A");
				break;
			case ftFRAME_B:
				fprintf(stdout, "%-8s", "FRAME B");
				break;
			case ftFRAME_C:
				fprintf(stdout, "%-8s", "FRAME C");
				break;
			default:
				fprintf(stdout, "%-8s", "UNKNOWN");
				break;
			}
			if (dsc->prop & ftPROP_CRC4)
				fprintf(stdout, " (CRC4 Error)");
			if (dsc->prop & ftPROP_MFAS)
				fprintf(stdout, " (MFAS Error)");
			if (dsc->prop & ftPROP_TS31)
				fprintf(stdout, " (TS 31)");
			if (dsc->prop & ftPROP_START)
				fprintf(stdout, " (Start Test)");
			fprintf(stdout, "\n");
		}
		dsc++;
	}
}

static void print_flatdescription(struct fr_flatdesc *dsc)
{
	fprintf(stdout, "Start test\n");
	while (dsc) {
		if (dsc->type == ftNone) {
			fprintf(stdout, "unexpected end of description\n");
			break;
		} else if (dsc->type == ftCtrl_Stop) {
			fprintf(stdout, "End of test\n");
			break;
		} else if (dsc->type ==  ftAIS) {
				fprintf(stdout, "%7d %2d %-6s", dsc->pos, dsc->mf_pos, "AIS");
		} else if (dsc->type == ftFAS) {
			if (dsc->prop & ftPROP_FAIL)
				fprintf(stdout, "%7d %2d %-6s", dsc->pos, dsc->mf_pos, "/FAS");
			else
				fprintf(stdout, "%7d %2d %-6s", dsc->pos, dsc->mf_pos, "FAS");
		} else if (dsc->type == ftBIT2) {
			if (dsc->prop & ftPROP_FAIL)
				fprintf(stdout, "%7d %2d %-6s", dsc->pos, dsc->mf_pos, "/BIT 2");
			else
				fprintf(stdout, "%7d %2d %-6s", dsc->pos, dsc->mf_pos, "BIT 2");
		}
		switch (dsc->otype) {
		case ftAIS:
			fprintf(stdout, "[%-7s]", "AIS");
			break;
		/* Basic Frames */
		case ftFAS:
			if (dsc->prop & ftPROP_FAIL)
				fprintf(stdout, "[%-7s]", "/FAS");
			else
				fprintf(stdout, "[%-7s]", "FAS");
			break;
		case ftBIT2:
			if (dsc->prop & ftPROP_FAIL)
				fprintf(stdout, "[%-7s]", "/BIT 2");
			else
				fprintf(stdout, "[%-7s]", "BIT 2");
			break;
		/* Sub multi frames */
		case ftSMFA:
			fprintf(stdout, "[%-7s]", "SMF A");
			break;
		case ftSMFB:
			fprintf(stdout, "[%-7s]", "SMF B");
			break;
		/* Multi frames */
		case ftMFA:
			fprintf(stdout, "[%-7s]", "MF A");
			break;
		case ftMFB:
			fprintf(stdout, "[%-7s]", "MF B");
			break;
		/* Frames */
		case ftFRAME_A:
			fprintf(stdout, "[%-7s]", "FRAME A");
			break;
		case ftFRAME_B:
			fprintf(stdout, "[%-7s]", "FRAME B");
			break;
		case ftFRAME_C:
			fprintf(stdout, "[%-7s]", "FRAME C");
			break;
		default:
			fprintf(stdout, "[%-7s]", "VOID");
			break;
		}
		if (dsc->prop & ftPROP_CRC4)
			fprintf(stdout, " (CRC4 Error)");
		if (dsc->prop & ftPROP_MFAS)
			fprintf(stdout, " (MFAS Error)");
		if (dsc->prop & ftPROP_TS31)
				fprintf(stdout, " (TS 31)");
		if (dsc->prop & ftPROP_START)
			fprintf(stdout, " (Start Test)");
		fprintf(stdout, "\n");
		dsc++;
	}
}

static int calc_flatcount(struct fr_cdesc *arg)
{
	struct fr_cdesc *dsc = arg;
	int count[8], repeat[8];
	int cnt, csp = 0;

	count[0] = 0;
	while (dsc) {
		if (dsc->type == ftNone) {
			fprintf(stderr, "unexpected end of description\n");
			csp = 0;
			count[0] = -1;
			break;
		}
		if (dsc->type == ftCtrl_Stop)
			break;
		if (dsc->type == ftCtrl_Start) {
			/* nothing to do */
		} else if (dsc->type == ftCtrl_Repeat) {
			csp++;
			if (csp >= 8) {
				fprintf(stderr, "To deep nesting of loops\n");
				csp = 0;
				count[0] = -1;
				break;
			}
			repeat[csp] = dsc->count;
			count[csp] = 0;
		} else if (dsc->type == ftCtrl_End) {
			if (csp < 1) {
				fprintf(stderr, "End of description, but no Repeatblock\n");
				csp = 0;
				count[0] = -1;
				break;
			}
			cnt = repeat[csp] * count[csp];
			csp--;
			count[csp] += cnt;
		} else {
			count[csp] += (dsc->subcnt * dsc->count);
		}
		dsc++;
	}
	if (csp != 0) {
		fprintf(stderr, "End of description, but repeat level %d (not 0)\n", csp);
		csp = 0;
		count[0] = -1;
	}
	return count[0];
}

static struct fr_flatdesc *make_flat_description(struct fr_cdesc *test, struct fr_flatdesc *begin)
{
	struct fr_cdesc *dsc = test;
	struct fr_flatdesc *flat = begin;
	int count[8], repeat[8];
	struct fr_flatdesc *seq, *end, *start[8];
	int cnt, i, csp = 0, pos, mfpos;

	pos = flat->pos;
	mfpos = flat->mf_pos;
	while (dsc) {
		if (dsc->type == ftNone) {
			fprintf(stderr, "unexpected end of description\n");
			flat = NULL;
			break;
		}
		if (dsc->type == ftCtrl_Stop) {
			flat->pos = pos;
			flat->type = dsc->type;
			break;
		}
		if (dsc->type == ftCtrl_Start) {
			/* nothing to do */
			flat->prop = ftPROP_START;
		} else if (dsc->type == ftCtrl_Repeat) {
			csp++;
			if (csp >= 8) {
				fprintf(stderr, "To deep nesting of loops\n");
				csp = 0;
				flat = NULL;
				break;
			}
			repeat[csp] = dsc->count;
			start[csp] = flat;
			count[csp] = 0;
		} else if (dsc->type == ftCtrl_End) {
			if (csp < 1) {
				fprintf(stderr, "End of description, but no Repeatblock\n");
				flat = NULL;
				break;
			}
			end = flat;
			repeat[csp]--; /* one already done */
			while (repeat[csp]) {
				seq = start[csp];
				while (seq != end) {
					*flat = *seq;
					flat->pos = pos;
					flat->prop &= ~ftPROP_START;
					seq++;
					flat++;
					pos++;
				} 
				--repeat[csp];
			}
			csp--;
		} else {
			for (cnt = 0; cnt < dsc->count; cnt++) {
				switch (dsc->type) {
				case ftAIS:
				/* Basic Frames */
				case ftFAS:
				case ftBIT2:
					flat->pos = pos;
					flat->type = dsc->type;
					flat->otype = dsc->type;
					flat->prop |= dsc->prop;
					flat++;
					pos++;
					break;
				/* Sub multi frames */
				case ftSMFA:
					for (i = 0; i < 8; i++) {
						flat->pos = pos;
						flat->otype = dsc->type;
						if (i & 1) {
							flat->type = ftBIT2;
						} else {
							flat->type = ftFAS;
						}
						flat++;
						pos++;
					}
					break;
				case ftSMFB:
					for (i = 0; i < 8; i++) {
						flat->pos = pos;
						flat->otype = dsc->type;
						if (i & 1) {
							flat->type = ftBIT2;
						} else {
							flat->type = ftFAS;
						}
						flat->prop |= dsc->prop;
						flat++;
						pos++;
					}
					break;
				/* Multi frames */
				case ftMFA:
					for (i = 0; i < 16; i++) {
						flat->pos = pos;
						flat->otype = dsc->type;
						if (i & 1) {
							flat->type = ftBIT2;
						} else {
							flat->type = ftFAS;
						}
						flat++;
						pos++;
					}
					break;
				case ftMFB:
					for (i = 0; i < 16; i++) {
						flat->pos = pos;
						flat->otype = dsc->type;
						if (i & 1) {
							flat->type = ftBIT2;
						} else {
							flat->type = ftFAS;
						}
						flat->prop |= ftPROP_MFAS;
						flat++;
						pos++;
					}
					break;
				/* Frames */
				case ftFRAME_A:
					flat->pos = pos;
					flat->otype = dsc->type;
					flat->type = ftFAS;
					flat++;
					pos++;
					flat->pos = pos;
					flat->otype = dsc->type;
					flat->type = ftBIT2;
					flat++;
					pos++;
					break;
				case ftFRAME_B:
					flat->pos = pos;
					flat->otype = dsc->type;
					flat->type = ftFAS;
					flat->prop |= ftPROP_TS31;
					flat++;
					pos++;
					flat->pos = pos;
					flat->otype = dsc->type;
					flat->type = ftBIT2;
					flat->prop |= ftPROP_TS31;
					flat++;
					pos++;
					break;
				case ftFRAME_C:
					flat->pos = pos;
					flat->otype = dsc->type;
					flat->type = ftFAS;
					flat->prop |= ftPROP_FAIL | ftPROP_TS31;
					flat++;
					pos++;
					flat->pos = pos;
					flat->otype = dsc->type;
					flat->type = ftBIT2;
					flat->prop |= ftPROP_TS31;
					flat++;
					pos++;
					break;
				default:
					/* never */
					fprintf(stderr, "Got wrong type %x\n", dsc->type);
					exit(1);
				}
			}
		}
		dsc++;
	}
	if (!flat)
		return flat;
	flat = begin;
	mfpos = flat->mf_pos;
	while (flat) {
		if (flat->type == ftNone) {
			fprintf(stdout, "unexpected end of description\n");
			flat = NULL;
			break;
		} else if (flat->type == ftCtrl_Stop) {
			flat->mf_pos = mfpos;
			break;
		} else if (flat->type == ftAIS) {
			flat->mf_pos = 0xff;
			flat->mf_pos = 14;
		} else if (flat->type == ftFAS) {
			if (mfpos & 1) /* reset */
				mfpos = 0;
		} else if (flat->type == ftBIT2) {
			 if (!(mfpos & 1))
			 	mfpos = 1;
		}
		flat->mf_pos = mfpos;
		mfpos++;
		if (mfpos > 15)
			mfpos = 0;
		flat++;
	}
	return flat;
}

static struct fr_data *gen_flat_frame_desc(struct fr_cdesc *dsc)
{
	struct fr_data		loc_frd, *frd;
	struct fr_flatdesc	*flat_dsc;
	int			cnt, sum, pre;

	pre = calc_flatcount(preamble);
	cnt = calc_flatcount(dsc);
	sum = pre + cnt;
	loc_frd.count = sum;
	flat_dsc = calloc(sum + 2, sizeof(*flat_dsc)); /* reserve for stop */
	if (!flat_dsc) {
		fprintf(stderr, "No memory to allocate %d * %zd bytes for flat description\n", loc_frd.count, sizeof(*flat_dsc));
		return NULL;
	}
	loc_frd.desc = flat_dsc;
	flat_dsc = make_flat_description(preamble, flat_dsc);
	if (!flat_dsc) {
		fprintf(stderr, "Error while generating flat description of preamble\n");
		free(flat_dsc);
		return NULL;
	}
	flat_dsc = make_flat_description(dsc, flat_dsc);
	if (!flat_dsc) {
		fprintf(stderr, "Error while generating flat description\n");
		free(flat_dsc);
		return NULL;
	}
	frd = malloc(sizeof(*frd));
	if (!frd) {
		fprintf(stderr, "No memory to allocate %zd bytes\n", sizeof(*frd));
		free(flat_dsc);
		return NULL;
	}
	*frd = loc_frd;
	return frd;
}

static int gen_flat_frame_data(struct fr_data *frd) {
	struct fr_flatdesc *flat;
	size_t size;
	int cnt, ret = 0;
	uint8_t crc, *p, dch_flags = 0x7e;

	flat = frd->desc;
	size = frd->count;
	size *= FRAME_SIZE;
	frd->data_size = size;
	frd->data = malloc(size + 100 * FRAME_SIZE); /* Reserve to avoid crash when manipulate frames on the end */
	if (!frd->data) {
		fprintf(stderr, "No memory to allocate %zd + %d bytes\n", size, 100 * FRAME_SIZE);
		flat = NULL;
		ret = -ENOMEM;
	}
	memset(frd->data, 0xff, size + 100 * FRAME_SIZE);
	p = frd->data;
	fill_timeslot(p, 16, &dch_flags, 1, frd->count);
	while (flat) {
		flat->data = p;
		if (flat->type == ftNone) {
			fprintf(stdout, "unexpected end of description\n");
			free(frd->data);
			frd->data = NULL;
			ret = -EINVAL;
			break;
		} else if (flat->type == ftCtrl_Stop) {
			fprintf(stdout, "End of test\n");
			break;
		} else if (flat->type == ftAIS) {
			memset(p, 0xff, FRAME_SIZE);
		} else if (flat->type == ftFAS) {
			if (flat->prop & ftPROP_FAIL)
				*p = FR_FAS_VAL_FAIL;
			else
				*p = FR_FAS_VAL;
			if (flat->prop & ftPROP_TS31)
				p[31] = FR_NFAS_B2;
		} else if (flat->type == ftBIT2) {
			if (flat->prop & ftPROP_FAIL)
				*p = FR_NFAS_B2_FAIL;
			else
				*p = FR_NFAS_B2;
			if ((flat->mf_pos & 0xf) == 5) {
				if (!(flat->prop & ftPROP_MFAS))
					*p |= 1;
			} else if ((flat->mf_pos & 0xf) > 7) {
				*p |= 1;
			}
			if (flat->prop & ftPROP_TS31)
				p[31] = FR_FAS_VAL;
		}
		p += FRAME_SIZE;
		flat++;
	}
	p = frd->data;
	cnt = frd->count/8;
	crc = 0; /* start value */
	flat = frd->desc;
	while (flat) {
		if (flat->type == ftNone) {
			fprintf(stdout, "unexpected end of description\n");
			free(frd->data);
			frd->data = NULL;
			ret = -EINVAL;
			break;
		} else if (flat->type == ftCtrl_Stop) {
			fprintf(stdout, "End of test\n");
			break;
		} else if (flat->type == ftAIS) {
			crc = 0;
		} else {
			if ((flat->mf_pos & 0x7) == 0) {
				calc_and_set_crc4_ts(flat->data, 0, &crc, 1);
			}
			if ((flat->mf_pos & 0x7) == 2) {
				if (flat->prop & ftPROP_CRC4) {
					/* insert a bit failure in TS 3 (0xFF -> 0xEF) */
					flat->data[3] = 0xef;
				}
			}
		}
		flat++;
	}	
	return ret;		
}

int
main(argc, argv)
int argc;
char *argv[];
{
	int	i, channel;
	int	log_socket;
	struct sockaddr_mISDN  log_addr;
	int	buflen = 4104;
	u_char	buffer[buflen];
	int	result;
	int	cnt, dlen;
	struct mISDN_devinfo	di;
	struct mISDNhead 	*hh;
	struct mISDNversion	ver;
	struct pollfd pfd[8];
	int pfd_nr;
	struct mISDN_ctrl_req	 creq;

	result = opt_parse(argc, argv);
	if (result) {
		exit(1);
	}
	calc_bitswap_table();

	cnt = calc_flatcount(preamble);
	fprintf(stdout, "preamble: %d test: %d\n", cnt, calc_flatcount(Test));

	if (ListMode == 1) {
		print_testdescription(Test);
		return 0;
	}
	TestData = gen_flat_frame_desc(Test);
	if (!TestData) {
		fprintf(stdout, "Did not generate flat description\n");
		exit(1);
	}
	if (ListMode == 2) {
		print_flatdescription(TestData->desc);
		return 0;
	}
	result = gen_flat_frame_data(TestData);
	if (result) {
		fprintf(stderr,"Error generating test data\n");
		exit(1);
	}

	if (cardnr < 0) {
		fprintf(stderr,"card nr may not be negative\n");
		exit(1);
	}

	if ((log_socket = socket(PF_ISDN, SOCK_RAW, 0)) < 0) {
		printf("could not open socket %s\n", strerror(errno));
		exit(1);
	}

	result = ioctl(log_socket, IMGETVERSION, &ver);
	if (result < 0) {
		printf("ioctl error %s\n", strerror(errno));
		exit(1);
	}
	printf("mISDN kernel version %d.%02d.%d found\n", ver.major, ver.minor, ver.release);
	printf("mISDN user   version %d.%02d.%d found\n", MISDN_MAJOR_VERSION, MISDN_MINOR_VERSION, MISDN_RELEASE);

	if (ver.major != MISDN_MAJOR_VERSION) {
		printf("VERSION incompatible please update\n");
		exit(1);
	}

	result = ioctl(log_socket, IMGETCOUNT, &cnt);
	if (result < 0) {
		printf("ioctl error %s\n", strerror(errno));
		exit(1);
	} else
		printf("%d controller%s found\n", cnt, (cnt==1)?"":"s");

	di.id = cardnr;
	result = ioctl(log_socket, IMGETDEVINFO, &di);
	if (result < 0) {
		printf("ioctl error %s\n", strerror(errno));
	} else {
		printf("	id:		%d\n", di.id);
		printf("	Dprotocols:	%08x\n", di.Dprotocols);
		printf("	Bprotocols:	%08x\n", di.Bprotocols);
		printf("	protocol:	%d\n", di.protocol);
		printf("	channelmap:	");
		for (i = MISDN_CHMAP_SIZE - 1; i >= 0; i--)
			printf("%02x", di.channelmap[i]);
		printf("\n");
		printf("	nrbchan:	%d\n", di.nrbchan);
		printf("	name:		%s\n", di.name);
	}

	close(log_socket);
	outbuf = TestData->data;
	ob_size = TestData->data_size;
	ob_end = outbuf + ob_size;
	ib_size = ob_size;
	ib_size += (5 * MFR_SIZE);
	inbuf = malloc(ib_size + (5 * MFR_SIZE));
	if (!inbuf) {
		fprintf(stderr, "Could not allocate in buffer (%d bytes)\n", ib_size + (5 * MFR_SIZE));
		exit(1);
	} else if (debuglevel)
		printf("Allocated in buffer %d bytes (Reserve:%d)\n", ib_size, 10 * MFR_SIZE);

	ib_p = inbuf;
	ib_cp = inbuf;
	ib_end = outbuf + ib_size;
	
	/* Ready for transmit */
	ob_p = outbuf;
	
	if ((log_socket = socket(PF_ISDN, SOCK_DGRAM, ISDN_P_B_RAW)) < 0) {
		printf("could not open log socket %s\n", strerror(errno));
		exit(1);
	}

	log_addr.family = AF_ISDN;
	log_addr.dev = cardnr;

	result = -1;
	channel = 0;

	log_addr.channel = (unsigned char)channel;
	result = bind(log_socket, (struct sockaddr *) &log_addr, sizeof(log_addr));
	printf("log bind ch(%i) return %d\n", log_addr.channel, result);
	if (result < 0) {
		printf("log bind error %s\n", strerror(errno));
		close(log_socket);
		exit(1);
	}

	result = fcntl(log_socket, F_SETFL, O_NONBLOCK);
	if (result < 0) {
		printf("log F_SETFL O_NONBLOCK error %s\n", strerror(errno));
		close(log_socket);
		exit(1);
	}
	pfd[0].fd = log_socket;
	pfd[0].events = POLLIN;
	pfd[0].revents = 0;
	pfd_nr = 1;

	hh = (struct mISDNhead *)buffer;

	/* This set the register values in the card so, that the TS0  is not in full transparent mode,
	 * so the receiver can syncronize - this allows to get the TS0 data on byte boundaries, so bit schifting all
	 * incoming data is not needed. After we did detect syncron state for 3 times, will will switch off
	 * autosync and put TS0 into full transparent mode */

	creq.op = MISDN_CTRL_L1_TS0_MODE;
	creq.channel = 0;
	creq.p1 = 0x06; /* R_RX_SL0_CFG0 =  (V_AUTOSYNC | V_AUTO_RECO) */
	creq.p2 = 0x31; /* R_TX_SL0_CFG1 = (V_TX_MF | V_TX_E | V_INV_E) */
	creq.p3 = 0;
	if (debuglevel)
		fprintf(stdout,"L1 TS0  ioctl R_RX_SL0_CFG0=%02x R_TX_SL0_CFG1=%02x\n", creq.p1, creq.p2);
	result = ioctl(log_socket, IMCTRLREQ, &creq);
	if (debuglevel)
		fprintf(stdout,"L1 TS0  ioctl old register values R_RX_SL0_CFG0=%02x R_TX_SL0_CFG1=%02x\n", creq.p1, creq.p2);
	if (result < 0) {
		fprintf(stdout, "Error on MISDN_CTRL_L1_TS0_MODE ioctl - %s\n", strerror(errno));
		close(log_socket);
		exit(1);
	}

	hh->prim = PH_ACTIVATE_REQ;
	hh->id   = MISDN_ID_ANY;
	result = sendto(log_socket, buffer, MISDN_HEADER_LEN, 0, NULL, 0);

	if (result < 0) {
		fprintf(stdout, "could not send ACTIVATE_REQ %s\n", strerror(errno));
		close(log_socket);
		exit(1);
	}

	/* Disable RX for the sync search time */
	creq.op = MISDN_CTRL_RX_OFF;
	creq.channel = 0;
	creq.p1 = 1;
	creq.p2 = 0;
	creq.p3 = 0;
	result = ioctl(log_socket, IMCTRLREQ, &creq);
	if (result < 0) {
		fprintf(stdout, "Error on MISDN_CTRL_RX_OFF ioctl - %s\n", strerror(errno));
		close(log_socket);
		exit(1);
	}
	if (debuglevel)
		fprintf(stdout,"RX_OFF  result %d ioctl p1=%02x p2=%02x p3=%02x\n",
			result, creq.p1, creq.p2, creq.p3);

	creq.op = MISDN_CTRL_L1_GET_SYNC_INFO;
	creq.channel = 0;
	cnt = 0;
	/* Wait for sync - this will make sure that we do not need bit shifting incomming data */ 
	for (i = 0; i < 5000; i++) {
		result = ioctl(log_socket, IMCTRLREQ, &creq);
		if (result < 0) {
			fprintf(stdout, "Error on MISDN_CTRL_L1_GET_SYNC_INFO ioctl - %s\n", strerror(errno));
			close(log_socket);
			exit(1);
		}
		if (debuglevel)
			fprintf(stdout,"L1 GET_SYNC_INFO ioctl p1=%02x p2=%02x p3=%02x\n",
				creq.p1, creq.p2, creq.p3);
		if ((creq.p1 & 0xff07) == 0x2701) {
			cnt++;
			if (cnt == 3)
				break;
			i--;
		} else
			cnt = 0;
		usleep(100000);
	};

	if (cnt != 3) {
		fprintf(stdout,"L1 ts0 sync state not reached\n");
		close(log_socket);
		exit(1);
	} else
		fprintf(stdout,"L1 ts0 sync state reached (need %d iterations)\n", i);

	/* reenable receive */
	creq.op = MISDN_CTRL_RX_OFF;
	creq.channel = 0;
	creq.p1 = 0;
	creq.p2 = 0;
	creq.p3 = 0;
	result = ioctl(log_socket, IMCTRLREQ, &creq);
	if (result < 0) {
		fprintf(stdout, "Error on MISDN_CTRL_RX_OFF ioctl - %s\n", strerror(errno));
		close(log_socket);
		exit(1);
	}
	if (debuglevel)
		fprintf(stdout,"RX_OFF  ioctl p1=%02x p2=%02x p3=%02x\n", creq.p1, creq.p2, creq.p3);

	creq.op = MISDN_CTRL_L1_TS0_MODE;
	creq.channel = 0;
	creq.p1 = 0x01; /* R_RX_SL0_CFG0 = (V_NO_INSYNC)  */
	creq.p2 = 0x03; /* R_TX_SL0_CFG1 = (V_TX_MF | V_TRP_SL0) */
	creq.p3 = 0;
	if (debuglevel)
		fprintf(stdout,"L1 TS0  ioctl R_RX_SL0_CFG0=%02x R_TX_SL0_CFG1=%02x\n", creq.p1, creq.p2);
	result = ioctl(log_socket, IMCTRLREQ, &creq);
	if (debuglevel)
		fprintf(stdout,"L1 TS0  ioctl old register values R_RX_SL0_CFG0=%02x R_TX_SL0_CFG1=%02x\n", creq.p1, creq.p2);
	if (result < 0) {
		fprintf(stdout, "Error on MISDN_CTRL_L1_TS0_MODE ioctl - %s\n", strerror(errno));
		close(log_socket);
		exit(1);
	}

	dlen = 64;
	while (1) {
		result = poll(pfd, pfd_nr, 1000);
		if (result < 0) {
			fprintf(stderr, "Poll error %s\n", strerror(errno));
			continue;
		} else if (result == 0) {
			fprintf(stderr, "Poll timeout restart\n");
			continue;
		}
		if (pfd[0].revents) {
			dlen = receive_ts0dch(log_socket);
			if (dlen < 1) /* end of data */
				break;
		}
	}
	close(log_socket);
	return 0;
}
