/*++
*******************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   isdntrac.c

Abstract:

   Isdn Trace utility.  Command-line application which captures, decodes and displays
	layer 2 and layer 3 ISDN messages for DataFire PRIme, Houston and London adapters.

Author:

	Unknown, edited by Michael Zarns

--*/

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

#include "DsIoctl.h"
#include "dxbtypes.h"
#include "dsuser.h"

#ifndef	MAXIOCBSZ
#define MAXIOCBSZ 1024
#endif

#include "il3_lmi.h"

#include "lapd_control.h"
#include "isdn_proto.h"
#include "isdn_trace.h"
#include "il3_config.h"
#include "il3_sm.h"


#define	SUPERVISORY	0x01
#define	I_FRAME		0x02
#define	UNNUMBERED	0x03

#define	OCT_ADDRESS_1		0x00
#define	OCT_ADDRESS_2		0x01
#define	OCT_CONTROL_1		0x02
#define	OCT_CONTROL_2		0x03
#define	OCT_DATA		0x04

#define	OCT_UNNUM_DATA		0x03

 /*
  * The following is only valid with IL3
  */

#define	PD_Q931 	0x08		/* 0000 1000 */
#define	PD_N0   	0x40		/* 0100 0000 */
#define	PD_N1   	0x41		/* 0100 0001 */

#define	MASK_CS 	0x07

#ifndef NAT_NOT_BRITISH
#define NAT_NOT_BRITISH	(NAT_ALL & ~NAT_BRITISH)
#endif

#define	OCT_PD		0x00
#define	OCT_Cref1	0x01
#define	OCT_Cref2	0x02
#define	OCT_IE_TYPE	0x00
#define	OCT_IE_LEN	0x01
#define	OCT_IE_VALUE	0x02

#define   DISP_COLS 16

int             getopt(int argc, char **argv, char *opts);
extern char    *optarg;
extern int      optind;


#define InitializeListHead(ListHead) (\
    (ListHead)->Flink = (ListHead)->Blink = (ListHead))


#define IsListEmpty(ListHead) \
    ((ListHead)->Flink == (ListHead))


#define RemoveHeadList(ListHead) \
    (ListHead)->Flink;\
    {RemoveEntryList((ListHead)->Flink)}


#define RemoveTailList(ListHead) \
    (ListHead)->Blink;\
    {RemoveEntryList((ListHead)->Blink)}


#define RemoveEntryList(Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_Flink;\
    _EX_Flink = (Entry)->Flink;\
    _EX_Blink = (Entry)->Blink;\
    _EX_Blink->Flink = _EX_Flink;\
    _EX_Flink->Blink = _EX_Blink;\
    }


#define InsertTailList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Blink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Blink = _EX_ListHead->Blink;\
    (Entry)->Flink = _EX_ListHead;\
    (Entry)->Blink = _EX_Blink;\
    _EX_Blink->Flink = (Entry);\
    _EX_ListHead->Blink = (Entry);\
    }


#define InsertHeadList(ListHead,Entry) {\
    PLIST_ENTRY _EX_Flink;\
    PLIST_ENTRY _EX_ListHead;\
    _EX_ListHead = (ListHead);\
    _EX_Flink = _EX_ListHead->Flink;\
    (Entry)->Flink = _EX_Flink;\
    (Entry)->Blink = _EX_ListHead;\
    _EX_Flink->Blink = (Entry);\
    _EX_ListHead->Flink = (Entry);\
    }

#define LAPD_PROTO   0x00000001
#define IL3_PROTO    0x00000002

#define PrintUsage() \
   printf( "Digi ISDN PRI Trace - Version 1.03\n" \
           "Copyright 1997, Digi International Inc. All rights reserved.\n\n" \
           "usage:\n"   \
           "   isdntrac [-il3 | -lapd] [-v] [-te] <device> <snid>\n"   \
           "      e.g. isdntrac -lapd -v DigiDxb2 1\n" );

#define OPT_LIST "i:l:t:v?"

typedef struct _PRINTF_QUEUE_INFO 
{
	LIST_ENTRY ListEntry;
	OVERLAPPED Ol;
	DWORD      QueryThreadId;

	UCHAR      ControlBuffer[512];	// GetMsg control buffer
	ULONG      cbControl;				// Count of control bytes returned
	UCHAR      DataBuffer[4096];		// GetMsg data buffer
	ULONG      cbData;					// Count of data bytes returned
} PRINTF_QUEUE_INFO, *PPRINTF_QUEUE_INFO;


CHAR       DeviceName[32];
UCHAR      unknown[14];
HANDLE     hFile;
HANDLE     hStream;
UCHAR      Snid;
ULONG      Proto = LAPD_PROTO;
BOOL       ExitProgram = FALSE;
ULONG      NoMoreProcessing = 0;
ULONG      MessageCount = 0;
BOOL       Verbose = FALSE;
BOOL	   te = FALSE;
HANDLE     ProtectQueuesSemaphore;
LIST_ENTRY PrintfQueue;
HANDLE     PrintfQueueEvent;
LIST_ENTRY QueryQueue;
HANDLE     QueryQueueEvent;

#define P_ONLY	1
#define F_ONLY	2
#define P_OR_F	P_ONLY|F_ONLY

static struct hd {
	int  value;
	char *name;
	int  pf_bit;
} frame_list[] =
{
	{	0x00, "I", P_ONLY },
	{	0x01, "RR", P_OR_F	},
	{	0x05, "RNR", P_OR_F	},
	{	0x09, "REJ", P_OR_F	},
	{	0x7F, "SABME", P_ONLY	},
	{	0x6F, "SABME", P_ONLY	},
	{	0x0F, "DM", F_ONLY	},
	{	0x1F, "DM", F_ONLY	},
	{	0x13, "UI", P_ONLY	},
	{	0x03, "UI", P_ONLY	},
	{	0x53, "DISC", P_ONLY	},
	{	0x43, "DISC", P_ONLY	},
	{	0x63, "UA", F_ONLY	},
	{	0x73, "UA", F_ONLY	},
	{	0x87, "FRMR", F_ONLY	},
	{	0x97, "FRMR", F_ONLY	},
	{	0xBF, "XID", P_OR_F	},
	{	0xAF, "XID", P_OR_F	},
	{	0, 0	}
};


#define	CC_PD_N0	0x400	/* Otherwise N1 for 1TR6 */


 /*
  * Message Type Print-Out Defines
  */

static struct msgie_lst {
	int             key;
	int             info;
	char           *name;
};

static struct msgie_lst msg_type_list[] =
{

	{0x01, INFO_NORM, "MT_ALERTING",},
	{0x02, INFO_NORM, "MT_CALL_PROCEEDING",},
	{0x07, INFO_NORM, "MT_CONNECT",},
	{0x0f, INFO_NORM, "MT_CONNECT_ACK",},
	{0x03, NAT_NOT_GERMAN | CS_0, "MT_PROGRESS",},
	{0x05, INFO_NORM, "MT_SETUP",},
	{0x0d, NAT_ALL | CS_0, "MT_SETUP_ACK",},
	{0x26, NAT_ALL | CS_0, "MT_RESUME",},
	{0x2e, NAT_ALL | CS_0, "MT_RESUME_ACK",},
	{0x22, NAT_ALL | CS_0, "MT_RESUME_REJ",},
	{0x25, NAT_ALL | CS_0, "MT_SUSPEND",},
	{0x2d, NAT_ALL | CS_0, "MT_SUSPEND_ACK",},
	{0x21, NAT_ALL | CS_0, "MT_SUSPEND_REJ",},
	{0x20, NAT_NOT_BRITISH | CS_0, "MT_USER_INFO",},
	{0x40, NAT_BRITISH | CS_0, "MT_DETACH",},
	{0x45, NAT_ALL | CS_0, "MT_DISCONNECT",},
	{0x4d, INFO_NORM, "MT_RELEASE",},
	{0x5a, INFO_NORM, "MT_RELEASE_COMPLETE",},
	{0x46, NAT_NOT_GERMAN | CS_0, "MT_RESTART",},
	/*
	 * Index 20 got left out.
	 */
	{0x4e, NAT_NOT_GERMAN | CS_0, "MT_RESTART_ACK",},
	{0x60, NAT_FRENCH | NAT_ETSI | CS_0, "MT_SEGMENT",},
	{0x79, NAT_FRENCH | NAT_ETSI | CS_0, "MT_CONGESTION_CTRL",},
	{0x7b, NAT_NOT_GERMAN | CS_0, "MT_INFORMATION",},
	{0x62, NAT_FRENCH | NAT_ETSI | CS_0, "MT_FACILITY",},
	{0x6e, NAT_NOT_GERMAN | CS_0, "MT_NOTIFY",},
	{0x6e, NAT_GERMAN | CS_0, "MT_CANC_ACK",},
	{0x67, NAT_GERMAN | CS_0, "MT_CANC_REJ",},
	{0x69, NAT_GERMAN | CS_0, "MT_CON_CTRL",},
	{0x60, NAT_GERMAN | CS_0, "MT_FAC",},
	{0x68, NAT_GERMAN | CS_0, "MT_FAC_ACK",},
	{0x66, NAT_GERMAN | CS_0, "MT_FAC_CAN",},
	{0x64, NAT_GERMAN | NAT_FRENCH | CS_0, "MT_FAC_REG",},
	{0x65, NAT_GERMAN | CS_0, "MT_FAC_REJ",},
	{0x6d, NAT_GERMAN | CS_0, "MT_INFO",},
	{0x6c, NAT_GERMAN | NAT_FRENCH | CS_0, "MT_REG_ACK",},
	{0x6f, NAT_GERMAN | CS_0, "MT_REG_REJ",},
	{0x63, NAT_GERMAN | CS_0, "MT_STAT",},
	{0x7d, NAT_NOT_GERMAN | CS_0, "MT_STATUS",},
	{0x75, NAT_NOT_GERMAN | CS_0, "MT_STATUS_ENQ",},
	{0x6a, NAT_FRENCH | CS_0, "MT_FACILE_ACK",},
	{0x72, NAT_FRENCH | CS_0, "MT_FACILE_REJ",},
	{0x74, NAT_FRENCH | CS_0, "MT_REGIS_REJ",},
	/*
	 * PD_N0
	 */
	{0x61, NAT_GERMAN | CC_PD_N0, "MT_REGISTER_IND",},
	{0x62, NAT_GERMAN | CC_PD_N0, "MT_CANCEL_IND",},
	{0x63, NAT_GERMAN | CC_PD_N0, "MT_FAC_STATUS",},
	{0x64, NAT_GERMAN | CC_PD_N0, "MT_STATUS_ACK",},
	{0x65, NAT_GERMAN | CC_PD_N0, "MT_STATUS_REJ",},
	{0x66, NAT_GERMAN | CC_PD_N0, "MT_FAC_INFO",},
	{0x67, NAT_GERMAN | CC_PD_N0, "MT_INFO_ACK",},
	{0x68, NAT_GERMAN | CC_PD_N0, "MT_INFO_REJ",},
	{0x75, NAT_GERMAN | CC_PD_N0, "MT_CLOSE",},
	{0x77, NAT_GERMAN | CC_PD_N0, "MT_CLOSE_ACK",},
	{0, 0, 0}
};

static struct msgie_lst ie_type_list[] =
{
	/*
	 * Codeset 0
	 */
	{0x00, CS_0, "IE_SEGMENTED_MESSAGE"},
	{0x04, CS_0, "IE_BEARER_CAP"},
	{0x08, CS_0, "IE_CAUSE"},
	{0x0c, CS_0, "IE_CONNECTED_ADD"},
	{0x10, CS_0, "IE_CALL_IDENTITY"},
	{0x14, CS_0, "IE_CALL_STATE"},
	{0x18, CS_0, "IE_CHANNEL_ID"},
	{0x1c, CS_0, "IE_FACILITY"},
	{0x1e, CS_0, "IE_PROGRESS_IND"},
	{0x20, CS_0, "IE_NET_SPEC_FAC"},
	{0x27, CS_0, "IE_NOTIFICATION_IND"},
	{0x28, CS_0, "IE_DISPLAY"},
	{0x29, CS_0, "IE_DATE_TIME"},
	{0x2c, CS_0, "IE_KEYPAD_FAC"},
	{0x32, CS_0, "IE_INFO_REQUEST"},
	{0x34, CS_0, "IE_SIGNAL"},
	{0x3a, CS_0, "IE_SPID"},
	{0x3b, CS_0, "IE_ENDPOINT_IDENTIFIER"},
	{0x40, CS_0, "IE_INFORMATION_RATE"},
	{0x42, CS_0, "IE_TRANSIT_DELAY"},
	{0x43, CS_0, "IE_TRANSIT_SEL_IND"},
	{0x44, CS_0, "IE_P_LAY_PARAMS"},
	{0x45, CS_0, "IE_P_LAY_WIN"},
	{0x46, CS_0, "IE_PACKET_SIZE"},
	{0x6c, CS_0, "IE_CALLING_PAR_NO"},
	{0x6d, CS_0, "IE_CALLING_PAR_SUBADD"},
	{0x70, CS_0, "IE_CALLED_PAR_NO"},
	{0x71, CS_0, "IE_CALLED_PAR_SUBADD"},
	{0x74, CS_0, "IE_REDIRECTING_NO"},
	{0x78, CS_0, "IE_TRANSIT_NET_SEL"},
	{0x79, CS_0, "IE_RESTART_IND"},
	{0x7c, CS_0, "IE_LOW_LAYER_COMPAT"},
	{0x7d, CS_0, "IE_HIGH_LAYER_COMPAT"},
	{0x7e, CS_0, "IE_USER_USER_INFO"},
	{0x7f, CS_0, "IE_ESCAPE_FOR_EXTN"},
	/*
	 * NOT used anymore
	 */
	{0x37, /* 011 0110 */ CS_0, "IE_SWITCHHOOK"},
	{0x78, /* 011 1000 */ CS_0, "IE_FEATURE_ACT"},
	{0x79, /* 011 1001 */ CS_0, "IE_FEATURE_IND"},

	/*
	 * Codeset 6
	 */
	{0x01, CS_6, "IE_SERVICE_IND"},
	{0x02, CS_6, "IE_CHARGING_INFO"},
	{0x03, CS_6, "IE_DATE"},
	{0x05, CS_6, "IE_FACILITY_SEL"},
	{0x06, CS_6, "IE_STATS_FAC"},
	{0x07, CS_6, "IE_STATS_CALLED_USER"},
	{0x08, CS_6, "IE_ADD_TX_ATTRIB"},
	{0x24, CS_6, "IE_TERMINAL_CAP"},
	{0x7a, CS_6, "IE_USER_USER_FAC"},
	{0, 0, 0}
};

DWORD WINAPI KeyboardMonitor( LPVOID Foo )
{
   UCHAR CharacterBuffer[100];
   DWORD CharactersRead = 0, n, NumberOfEvents = 0, ConsoleMode;
   HANDLE   hConsoleInput;

   hConsoleInput = GetStdHandle(STD_INPUT_HANDLE);

   if (hConsoleInput == INVALID_HANDLE_VALUE)
	{
      ExitProgram = TRUE;
      NoMoreProcessing = 1;
   }

   GetConsoleMode(hConsoleInput, &ConsoleMode);
   SetConsoleMode(hConsoleInput, ConsoleMode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT));

   while (!ExitProgram)
   {
      ReadConsole(hConsoleInput, &CharacterBuffer, 100, &CharactersRead, NULL);
      for (n = 0; n < CharactersRead; n++)
      {
		 printf( "c = %c (0x%x)\n",
                       CharacterBuffer[n],
                       CharacterBuffer[n] );
      }
   }
   return(0);
}

BOOL 
MyCtrlHandler(
	DWORD dwCtrlType )
{
	BOOL            bSuccess;

	printf( "In the Ctrl - C Handler (0x%x)\n", dwCtrlType );
	switch (dwCtrlType) 
	{
		case CTRL_C_EVENT:
		case CTRL_BREAK_EVENT:

			printf( "Shutting down...\n" );
			SetEvent(PrintfQueueEvent);
			NoMoreProcessing = 1;
			switch ( Proto )
			{
				case IL3_PROTO:
				{
					il3_trioc_t il3_t = {0};
					il3_t.l_type        = IL3_TRACE_OFF;
					il3_t.l_snid        = Snid;
					il3_t.l_all_snworks = FALSE;
					il3_t.l_level       = ISDN_IL3_DAT;

					bSuccess = DsIoctl( hFile, hStream, IL3_TRACE_OFF, &il3_t, sizeof il3_t );
					if ( !bSuccess )
					{
						// Maybe a DataFire PRIme.  If so, use legacy-style request
						ULONG Foo = Snid;
						printf( "IL3_TRACE_OFF Ioctl failed, trying il3_set request.\n" );
						if ( il3_set( hFile, hStream, IL3_TRACE_OFF, (char *)&Foo, sizeof Foo, (int *)&il3_t, sizeof il3_t ) )
							printf( "il3_set request failed!\n" );
					}
					break;
				}

				case LAPD_PROTO:
				{
					lapd_trioc_t trace_reg = {0};
					trace_reg.l_type        = LAPD_TRACE;
					trace_reg.l_level       = ISDN_LAPD_DAT;
					trace_reg.l_all_snworks = FALSE;
					trace_reg.l_snid        = 0x000000ff & Snid;
					(void)DsIoctl( hFile, hStream, LAPD_TRACEOFF, &trace_reg, sizeof trace_reg );
				}
					break;

				default:
					break;
			}

			(void)DsPurge( hFile, hStream, DS_PURGE_ALL );
			while(1)
			{
				if(NoMoreProcessing > 2)  // Get's all done ?
					break;
				printf( "." );
				Sleep(50);
			}

			(void)DsPurge( hFile, hStream, DS_PURGE_ALL );
			(void)DsClose( hFile, hStream );

			CloseHandle(hFile);
			printf("Closed device %s\n", DeviceName);
			fflush( stdout );
			ExitProgram = TRUE;	// indicate user hit ^C
			break;

		default:
			break;
	}

	return FALSE;
}


static void     
hex_dump( 
	unsigned char * buf, 
	int len )
{
	int i = 0;

	while( i < len ) {
		if( !(len % DISP_COLS) )
			printf( "\n                  : " );
		printf( "%02x ", buf[i] ); 
		i++;
	}
	printf( "\n" );
}


/**************************************/
static int 
get_lapd_frame_type(
	UCHAR byte )
/**************************************/
{
	if ((byte & 0x01) == 0x00)
		return (I_FRAME);
	if ((byte & 0x03) == 0x01)
		return (SUPERVISORY);
	if ((byte & 0x03) == 0x03)
		return (UNNUMBERED);
	return (0);
}


/**************************************/
static int 
decode_frame(
	int cr_bit, 
	int header, 
	int poll, 
	int is_te, 
	int tx)
/**************************************/
/*
 * Decodes the given frame header by searching a list of known values.
 * Returns TRUE if found.
 */

{
	int pf = 0;
	int i = 0;
	int found = -1;
	char frame_name[20];
	char *name = NULL;

	for (; frame_list[i].name != NULL; ++i) {
		if (frame_list[i].value == header) {
			found = i;
		}
	}

	printf("                  : Frame");

	if (found == -1) {
		printf("%-12s", "UNKNOWN\n");
	} else {
		name = frame_list[found].name;
		pf = frame_list[found].pf_bit;

		strcpy(frame_name, name);

		/*
		 * Work out if the frame is a command or a response.
		 */
		switch (pf) {
		case P_ONLY:
			strcat(frame_name, "_CMD");
			break;

		case F_ONLY:
			strcat(frame_name, "_RSP");
			break;

		case P_OR_F:
			{
				int             rx = !tx;
				int             is_nt = !is_te;
				int             nt_to_te = (is_nt && tx) || (is_te && rx);

				if ((cr_bit && nt_to_te) || (!cr_bit && !nt_to_te)) {
					strcat(frame_name, "_CMD");
				} else {
					strcat(frame_name, "_RSP");
				}
			}
			break;
		}
		printf(" = %s\n", frame_name);
	}
	return (0);
}

/**************************************/
static unsigned char *
show_il3_message_type(
	unsigned char byte, 
	int           msg_PD )
/**************************************/
{
	int i = 0;

	if ((msg_PD == PD_N1) || (msg_PD == PD_Q931)) {
		while (msg_type_list[i].name != NULL) {
			if ((byte == msg_type_list[i].key) &&
			    !(msg_type_list[i].info & CC_PD_N0))
				return ((unsigned char *) msg_type_list[i].name);
			i++;
		}
		sprintf((char *) unknown[10], "Unknown 0x%03d", byte);
		return ((unsigned char *) unknown);
	}
	while (msg_type_list[i].name != NULL) {
		if ((byte == msg_type_list[i].key) &&
		    (msg_type_list[i].info & CC_PD_N0))
			return ((unsigned char *) msg_type_list[i].name);
		i++;
	}
	sprintf((char *) unknown[10], "Unknown 0x%03d", byte);
	return ((unsigned char *) unknown);
}

/*****************************************/
static unsigned char *
show_il3_ie(
	unsigned char * message, 
	int           * ie_CS, 
	int           * len )
/*****************************************/
{
	unsigned char   byte;
	int             i = 0;

	byte = message[OCT_IE_TYPE];
	if (*len <= 0)
		return (0);
	--(*len);

	printf("Information Element    : ");

	/*
	 * check for Codeset 0 single octet information elements
	 */

	if (byte & 0x80) {	/* 1000 ---- */
		if (byte == 0xa0)	/* 1010 0000 */
			printf("IE_MORE_DATA");

		else if (byte == 0xa1)	/* 1010 0001 */
			printf("IE_SENDING_COMPLETE");

		else if ((byte & 0xf0) == 0xb0)	/* 1011 ---- */
			printf("IE_CONGEST_LEVEL = %d", byte & 0x0f);

		else if ((byte & 0xf0) == 0xd0)	/* 1101 ---- */
			printf("IE_REPEAT_IND    = %d", byte & 0x0f);

		else if (byte == 0xa0)	/* 1010 0000 */
			printf("IE_MORE_DATA");

		else if (byte == 0xa1)	/* 1010 0001 */
			printf("IE_SENDING_COMPLETE");

		else if ((byte & 0xf0) == 0xb0)	/* 1011 ---- */
			printf("IE_CONGEST_LEVEL = %d", byte & 0x0f);

		else if ((byte & 0xf0) == 0xd0)	/* 1101 ---- */
			printf("IE_REPEAT_IND    = %d", byte & 0x0f);

		else if ((byte & 0xf1) == 0xd8) {	/* 1001 0--- */
			*ie_CS = CS_LOCKED;
			if ((byte & MASK_CS) == 0x00)
				*ie_CS |= CS_0;
			if ((byte & MASK_CS) == 0x06)
				*ie_CS |= CS_6;

			printf("IE_LOCKING_SHIFT -\n                       :CODESET = %d",
			       byte & 0x07);

		} else if ((byte & 0xf1) == 0xd0) {	/* 1001 1--- */
			*ie_CS = 0;
			if ((byte & MASK_CS) == 0x00)
				*ie_CS |= CS_0;
			if ((byte & MASK_CS) == 0x06)
				*ie_CS |= CS_6;

			printf("IE_NON_LOCKING_SHIFT -\n                       :CODESET = %d",
			       byte & 0x07);

		} else		/* 1XXX ---- */
			printf("IE_SNGL_RESERVED = 0x%02x", byte);

		printf("\n");
		fflush(stdout);
		return (message + 1);
	}
	while (ie_type_list[i].name != NULL) {
		/*
		 * if ( (byte  == ie_type_list[i].key) &&
		 * (ie_type_list[i].info & CC_PD_N0) ) cf CS_6 IAL
		*/
		if ((byte == ie_type_list[i].key)) {
			printf("%s\n", ie_type_list[i].name);
			break;
		}
		i++;
	}

	if (ie_type_list[i].name == NULL) {
		printf("Unknown = 0x%02x\n", byte);
	}
	byte = message[OCT_IE_LEN];
	if (*len <= 0)
		return (&message[OCT_IE_LEN]);
	--(*len);

	if (*len >= (int) byte) {
		hex_dump(&message[OCT_IE_VALUE], (int) byte);
	} else if (*len > 0) {
		printf("\nLength Error \n");
		hex_dump(&message[OCT_IE_VALUE], *len);
		*len = 0;
		return (&message[OCT_IE_VALUE]);
	}
	*len -= byte;

	return (&message[OCT_IE_VALUE + byte]);

}


void 
print_lapd(
	void *        pControl, 
	unsigned long cbControl,
	void *        pData, 
	unsigned long cbData,
	int           is_te, 
	int           tx )
{
	unsigned char *frame;
	int len;
	int cr_bit;
	int             frame_type;
	unsigned char  *hex_frame;
	int             oct_1 = 0;
	int             oct_2 = 0;


	frame = pData;
	len = cbData;

	/*
	 * Decode SAPI
	 */
	printf("LAPD: SAPI=%-3d ",
	       (unsigned char) ((frame[OCT_ADDRESS_1] & 0xFC)) >> 2);

	cr_bit = (unsigned char) ((frame[OCT_ADDRESS_1] & 0x02)) >> 1;

	if (--len == 0)
		return;

	/*
	 * Decode TEI
	 */
	printf("TEI=%-3d ",
	       (unsigned char) ((frame[OCT_ADDRESS_2] & 0xFE)) >> 1);

	printf("C/R=%d  ", cr_bit);
	if (--len == 0)
		return;

	/*
	 * Decode Frame Type
	 */
	frame_type = get_lapd_frame_type(frame[OCT_CONTROL_1]);

	oct_1 = (frame[OCT_CONTROL_1] & 0xFF);
	oct_2 = (frame[OCT_CONTROL_2] & 0xFF);

	switch (frame_type) {
	case I_FRAME:
		printf("P/F=%d  NR=%-3d NS=%-3d\n",
		       (oct_2 & 0x01), (oct_2 >> 1), (oct_1 >> 1));
		(void) decode_frame(cr_bit, 0x00, oct_2 & 0x01, is_te, tx);

		hex_frame = frame + OCT_DATA;

		if (--len == 0)
			return;
		break;

	case SUPERVISORY:
		printf("P/F=%d  NR=%-3d\n", (oct_2 & 0x01), (oct_2 >> 1));
		(void) decode_frame(cr_bit, oct_1, oct_2 & 0x01, is_te, tx);

		hex_frame = frame + OCT_DATA;

		if (--len == 0)
			return;
		break;

	case UNNUMBERED:
		printf("P/F=%d\n", ((oct_1 & 0x10) >> 4));
		(void) decode_frame(cr_bit, oct_1, (oct_1 & 0x10) >> 4, is_te, tx);

		hex_frame = frame + OCT_UNNUM_DATA;
		break;

	default:
		printf("UNKNOWN - %d\n", oct_1);
		break;
	}

	if (--len == 0)
		return;

	hex_dump(hex_frame, len);
		return;

}

#ifndef MAX_PRI_CREF
#define MAX_PRI_CREF 3
#endif

void 
print_il3(
	void *        pControl, 
	unsigned long cbControl,
	void *        pData, 
	unsigned long cbData )
{
	unsigned char *message;
	int len = cbData;

	int             i = 0;
	int             msg_PD, ie_CS = CS_0 | CS_LOCKED;
	unsigned char  *hex_message = message = pData;
	unsigned char  *op_str;

	cref_t          cref;
	int             read_ind = 0;

	len = cbData;

	len--;
	printf("Protocol Discriminator : ");
	msg_PD = message[OCT_PD] & 0xFF;
	if ((msg_PD) == PD_N0)
		printf("N0  ");
	else if ((msg_PD) == PD_N1)
		printf("N1  ");
	else if ((msg_PD) == PD_Q931)
		printf("Q931");
//	else if ((msg_PD) == PD_41449)
//		printf("ATT_Q931");
	else {
		printf("Unknown %d\n", msg_PD);
		hex_dump(hex_message, len);
		return;
	}

	/*
	 * Decode Call Reference
	 */
	len--;

	printf("   Call Reference : 0x");

	if ((cref.cref_len = message[OCT_Cref1]) < MAX_PRI_CREF)
		for (i = 0; i < (int) (cref.cref_len); i++) {
			printf("%02x", (cref.cref_val[i] = message[OCT_Cref2 + i]));
		}
	else {
		for (i = 0; ((i < (int) (cref.cref_len - 1)) &&
			     ((int) (cref.cref_len) <= len)); i++) {
			printf("%02x", (message[OCT_Cref2 + i]));
		}

		printf("\n");
		hex_dump((hex_message + OCT_Cref2 + i), len);
		return;
	}

	len -= cref.cref_len + 1;
	read_ind = cref.cref_len + 2;

	/*
	 * Decode Message  Group/Type
	 */
	printf("\nMessage Type           : %s\n",
	       show_il3_message_type(message[read_ind++], msg_PD));

	op_str = &message[read_ind];
	i = ie_CS = 0;
	fflush(stdout);
	while (len) {
		if (!(ie_CS & CS_LOCKED)) {
			op_str = show_il3_ie(op_str, &ie_CS, &len);
			if ((!len) || (op_str == (unsigned char *) NULL))
				break;
			if (ie_CS & CS_LOCKED)
				i = ie_CS;
			else
				ie_CS = i;	/** NON_LOCK. + NON_LOCK. => ? **/
		} else {/* ie_CS == CS_LOCKED */
			i = ie_CS;
			op_str = show_il3_ie(op_str, &ie_CS, &len);
			if ((!len) || (op_str == (unsigned char *) NULL))
				break;
		}
	}
	if (len <= 0)
		return;
	/*
	 * Attempt to show remaining contents of message.
	 */
	hex_dump(hex_message, len);
}


DWORD WINAPI 
PrintfThread(
	LPVOID ThreadContext )
{
	PLIST_ENTRY PQueue = &PrintfQueue;
	PLIST_ENTRY QQueue = &QueryQueue;
	PPRINTF_QUEUE_INFO pInfo;
	isdn_trc_types_t *TraceMsg;

	ULONG TotalSeconds, Hundredths, Hours, Minutes, Seconds;

	while (TRUE) 
	{
		// Wait for notification of something to do.
		WaitForSingleObject(PrintfQueueEvent, INFINITE);
		if (ExitProgram)// user hit ^c
			break;

		ResetEvent(PrintfQueueEvent);
		while (!IsListEmpty(PQueue)) 
		{
			// Okay, we should have something to do!
			pInfo = CONTAINING_RECORD(PQueue->Flink, PRINTF_QUEUE_INFO, ListEntry);
			TraceMsg = (PVOID)pInfo->ControlBuffer;

			TotalSeconds = TraceMsg->l_trc_generic.l_trc_time / 100;
			Hundredths = TraceMsg->l_trc_generic.l_trc_time % 100;
			Hours = TotalSeconds / 3600;
			Minutes = (TotalSeconds - (Hours * 3600)) / 60;
			Seconds = TotalSeconds % 60;

			printf("\n%06d %02d:%02d:%02d.%02d Snid %d %s ",
			    TraceMsg->l_trc_generic.l_trc_seq,
			    Hours, Minutes, Seconds, Hundredths,
			    TraceMsg->l_trc_generic.l_trc_snid,
			    (TraceMsg->l_trc_generic.l_trc_rcv) ? "Rx" : "Tx");

			if ( Verbose ) 
			{
				switch ( TraceMsg->l_trc_generic.l_trc_prim )
				{
					case ISDN_IL3_DAT:
						print_il3( pInfo->ControlBuffer, pInfo->cbControl, 
						           pInfo->DataBuffer, pInfo->cbData );
						break;

					case ISDN_LAPD_DAT:
						print_lapd( pInfo->ControlBuffer, pInfo->cbControl, 
						            pInfo->DataBuffer, pInfo->cbData, 
						            te, !TraceMsg->l_trc_generic.l_trc_rcv );
						break;

					default:
						break;
				}
			}

			// Okay, we have printed out the information, take it off the
			// printf queue and put on the query queue.
			WaitForSingleObject(ProtectQueuesSemaphore, INFINITE);
			RemoveHeadList(PQueue);
			InsertTailList(QQueue, &pInfo->ListEntry);
			MessageCount++;
			ReleaseSemaphore(ProtectQueuesSemaphore, 1, NULL);
		}
	}

	return (TRUE);
}


VOID
NewQueueEntry()
{
	PPRINTF_QUEUE_INFO pInfo;
	pInfo = malloc(sizeof(PRINTF_QUEUE_INFO));

	ZeroMemory( pInfo, sizeof *pInfo );
	pInfo->Ol.hEvent = CreateEvent( NULL, TRUE, FALSE, NULL );

	WaitForSingleObject(ProtectQueuesSemaphore, INFINITE);
	InsertTailList(&QueryQueue, &pInfo->ListEntry);
	MessageCount++;
	ReleaseSemaphore(ProtectQueuesSemaphore, 1, NULL);
}

void _cdecl 
main(int argc, char *argv[])
{
	char  **device = NULL;
	int   errflg = 0;
	int   c;
	BOOL  bSuccess;
	DWORD ThreadId,ThreadID2;
	UCHAR *pName;
	int   i;

	if (argc == 1) {
		PrintUsage();
		return;
	}
	while ((c = getopt(argc, argv, OPT_LIST)) != -1) {
		switch (c) {
		case 'i':
			{
				Proto = IL3_PROTO;
				break;
			}

		case 'l':
			{
				Proto = LAPD_PROTO;
				break;
			}

		case 'v':
			Verbose = TRUE;
			break;

        case 't':
		    te = TRUE;
			break;

		case '?':
		default:
			errflg++;
			break;
		}
	}

	if ( argc < optind+2 )
	{
		PrintUsage();
		return;
	}

	device = &argv[optind];
	Snid = (UCHAR)atoi( argv[optind+1] );

	if (errflg || (device == NULL)) {
		PrintUsage();
		return;
	} else if (device[0] == NULL) {
		PrintUsage();
		return;
	}
	SetConsoleCtrlHandler(MyCtrlHandler, TRUE);

	printf("   device = %s\n", *device);
	printf("   Snid   = %d\n", Snid);

	printf("Trace type = %s\n", Proto == IL3_PROTO ? "IL3" : "LAPD");

	sprintf( DeviceName, "\\\\.\\%s", *device );
	if ((hFile = CreateFile(DeviceName, GENERIC_READ | GENERIC_WRITE,
				FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 
				FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL)) == INVALID_HANDLE_VALUE) {
		printf("Couldn't open %s\n", DeviceName);
		return;
	}
	switch ( Proto )
	{
		case IL3_PROTO:  pName = "IL3";  break;
		case LAPD_PROTO: pName = "LAPD"; break;
		default:         
			PrintUsage(); 
			return;
	}

	bSuccess = DsOpen( hFile, pName, &hStream );
	if (!bSuccess) {
		printf("DsOpen failed, GetLastError = 0x%x\n",GetLastError());
		return;
	}

	ProtectQueuesSemaphore = CreateSemaphore(NULL, 1, 1, NULL);
	QueryQueueEvent  = CreateEvent(NULL, TRUE, FALSE, NULL);
	PrintfQueueEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	CreateThread( NULL, 0, PrintfThread,    NULL, 0, &ThreadId);
	CreateThread( NULL, 0, KeyboardMonitor, NULL, 0, &ThreadID2);

	if (Proto == IL3_PROTO) 
	{
		il3_trioc_t il3_t = {0};
		il3_t.l_type        = IL3_TRACE_ON;
		il3_t.l_snid        = Snid;
		il3_t.l_all_snworks = FALSE;
		il3_t.l_level       = ISDN_IL3_DAT;

		if ( !DsIoctl( hFile, hStream, IL3_TRACE_ON, &il3_t, sizeof il3_t ) )
		{
			// Maybe a DataFire PRIme.  If so, use legacy-style request
			ULONG Foo = Snid;
			printf( "IL3_TRACE_ON Ioctl failed, trying il3_set request!\n" );
			if ( il3_set( hFile, hStream, IL3_TRACE_ON, (char *)&Foo, sizeof Foo, (int *)&il3_t, sizeof il3_t ) )
			{
				printf( "il3_set request failed!\n" );
				// TODO: Need to cleanup here!  Close stream, Kill threads, free memory.
				return ;
			}
		} 
		printf( "IL3 tracing active on subnet %d\n", il3_t.l_active[0] );
	} 
	else if (Proto == LAPD_PROTO) 
	{
		struct lapd_trioc trace_reg = {0};
		trace_reg.l_snid = (DWORD) (0x000000ff & Snid);

		bSuccess = DsIoctl( hFile, hStream, LAPD_TRACEON, &trace_reg, sizeof trace_reg );
		if (!bSuccess) 
		{
			printf("LAPD_TRACEON Ioctl failed, GetLastError = 0x%x\n", GetLastError());
			// TODO: Need to cleanup here!  Close stream, Kill threads, free memory.
			return;
		}
		if (trace_reg.l_snid) {
			printf("LAPD tracing active on subnet %d\n", trace_reg.l_active[0] );
		}
	}

	//
	// Okay, lets prime the QueryQueue
	//
	InitializeListHead(&QueryQueue);
	MessageCount = 0;
	InitializeListHead(&PrintfQueue);

	for (i = 0; i < 50; i++) 
		NewQueueEntry();

	// Now, get trace messages from the adapter
	for (;;) 
	{
		PPRINTF_QUEUE_INFO pInfo;
		while (!IsListEmpty(&QueryQueue)) 
		{
			pInfo = CONTAINING_RECORD(QueryQueue.Flink, PRINTF_QUEUE_INFO, ListEntry);
			bSuccess = DsGetMsg( hFile, hStream, 
				pInfo->ControlBuffer, sizeof pInfo->ControlBuffer, &pInfo->cbControl,
			   pInfo->DataBuffer, sizeof pInfo->DataBuffer, &pInfo->cbData, DS_PRIORITY_ANY );

			if(NoMoreProcessing == 1)
			{
				NoMoreProcessing = 2;
				break;
			}

			// Got a frame, place it on the printf queue to be decoded later
			WaitForSingleObject(ProtectQueuesSemaphore, INFINITE);
			RemoveHeadList(&QueryQueue);
			MessageCount--;
			InsertTailList(&PrintfQueue, &pInfo->ListEntry);
			ReleaseSemaphore(ProtectQueuesSemaphore, 1, NULL);
			SetEvent(PrintfQueueEvent);
		}

		if (NoMoreProcessing == 2) // user hit ^C
		{
			NoMoreProcessing = 3;
			break;
		}

		// The QueryQueue went empty, allocate another
		// PRINTF_QUEUE_INFO for use.
		NewQueueEntry();
	}

	while(!ExitProgram)
		Sleep( 10 );
}


