/*++
*******************************************************************************
* Copyright (c) 1990-2000 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:

   IsdnTrace.cpp

Abstract:

   Isdn trace implementation.

Author:

   Michael Zarns

--*/
#include "stdafx.h"
#include "Diagnostic.h"
#include "DataSource.h"
#include "Tracer.h"
#include "IsdnTrace.h"
#include "TraceStatView.h"

// TODO: Somehow fix this!  Firmware headers collide in definitions of the following
// ll_control.h #defines them, while lapd_control uses them as fields in a struct.
// One solution is to pull sync stuff out of config.h so it doesn't get included here.
#undef RR_rx_cmd
#undef RR_rx_rsp
#undef RR_tx_cmd
#undef RR_tx_rsp
#undef RR_tx_cmd_p
#undef RNR_rx_cmd
#undef RNR_rx_rsp
#undef RNR_tx_cmd
#undef RNR_tx_rsp
#undef RNR_tx_cmd_p
#undef REJ_rx_cmd
#undef REJ_rx_rsp
#undef REJ_tx_cmd
#undef REJ_tx_rsp
#undef REJ_tx_cmd_p
#undef I_tx_cmd
#undef I_rx_cmd
#undef SABME_rx_cmd
#undef SABME_tx_cmd
#undef DISC_rx_cmd
#undef DISC_tx_cmd
#undef UA_rx_rsp
#undef UA_tx_rsp
#undef DM_rx_rsp
#undef DM_tx_rsp
#undef UI_rx_cmd
#undef UI_tx_cmd
#undef FRMR_rx_rsp
#undef FRMR_tx_rsp
#undef XID_rx_cmd
#undef XID_rx_rsp
#undef XID_tx_cmd
#undef XID_tx_rsp
#undef rx_badlen
#undef rx_unknown
#undef rx_bad
#undef rx_dud
#undef rx_ign
#undef tx_rtr
#undef tx_ign
#undef T200_exp
#undef T200_N200_exp
#undef rx_mgmt_dud
#undef rx_mgmt_badlen
#undef rx_mgmt_ign
#undef rx_mgmt_unknown
#undef rx_tei_assigned
#undef rx_tei_denied
#undef rx_tei_check
#undef rx_tei_remove
#include "lapd_control.h"
#include "IsdnTraceStats.h"

/////////////////////////////////////////////////////////////////////////////
// CIsdnTraceSheet

class CIsdnTraceSheet : public CTraceSheet
{
	DECLARE_DYNAMIC(CIsdnTraceSheet)
	
	CTraceStatView     m_BufferStats;
	CIsdnTraceLapdView m_LapdStats;
	CIsdnTraceIl3View  m_Il3Stats;

	virtual void AddTracePages();	

// Construction
public:
	CIsdnTraceSheet(CTraceData *, UINT nIDCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);
	CIsdnTraceSheet(CTraceData *, LPCTSTR pszCaption, CWnd* pParentWnd = NULL, UINT iSelectPage = 0);

// Operations
public:

// Overrides

// Implementation
public:
	virtual ~CIsdnTraceSheet() {};

	// Generated message map functions
protected:
	//{{AFX_MSG(CIsdnTraceSheet)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

IMPLEMENT_DYNAMIC(CIsdnTraceSheet, CTraceSheet)

CIsdnTraceSheet::CIsdnTraceSheet(CTraceData *td, UINT nIDCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CTraceSheet(td, nIDCaption, pParentWnd, iSelectPage)
{
}

CIsdnTraceSheet::CIsdnTraceSheet(CTraceData *td, LPCTSTR pszCaption, CWnd* pParentWnd, UINT iSelectPage)
	:CTraceSheet(td, pszCaption, pParentWnd, iSelectPage)
{
}


BEGIN_MESSAGE_MAP(CIsdnTraceSheet, CTraceSheet)
	//{{AFX_MSG_MAP(CIsdnTraceSheet)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CIsdnTraceSheet message handlers

void CIsdnTraceSheet::AddTracePages()
{
	AddPage(&m_BufferStats);
	AddPage(&m_LapdStats);
	AddPage(&m_Il3Stats);
}

IMPLEMENT_SERIAL(CIsdnTraceData, CTraceData, 3);


/////////////////////////////////////////////////////////////////////////////
// CIsdnTraceData

CIsdnTraceData::CIsdnTraceData(DWORD baud, CDWordArray& circuits) : CTraceData(baud)
{
	m_ScratchFrame = CreateFrame();
	ClearTraceStats();
}

CIsdnTraceData::~CIsdnTraceData()
{
}

CTraceSheet *CIsdnTraceData::CreateTraceSheet()
{
	return new CIsdnTraceSheet(this, _T(""));
}

void CIsdnTraceData::Serialize(CArchive& ar)
{
	CTraceData::Serialize(ar);
}

void CIsdnTraceData::ProcessFrame(const strbuf& control, const strbuf& data)
{
	CTraceData::ProcessFrame(control, data);
	CIsdnFrame *frame = (CIsdnFrame *)m_ScratchFrame;
	PUCHAR Payload = frame->GetData();
	DWORD dir = frame->GetTxRx();

	switch ( Payload[2] & 0x01 )
	{
		case 0:
		{
			// LAPD Info Frame contains a layer 3 frame
			frame->SetFilterMask( fmIl3 );
			nInfo[dir]++;	// Bump LAPD info frame counter

			int TypeOffset = 6 + (Payload[5] & 0x0F);
			if ( frame->GetDataLen() < TypeOffset )
				break;

			int ProtocolDiscriminator = Payload[4];
			switch ( ProtocolDiscriminator )
			{
				// AT&T Maintenance Messages
				case 0x03:
					switch ( Payload[TypeOffset] )
					{
						case 0x07:											// Service Ack
						case 0x0F:	nMaintenance[dir]++;	break;	// Service
						default:		nL3Unknown[dir]++;	break;
					}
					break;

				// Q931 Messages
				case 0x08:
					switch ( Payload[TypeOffset] )
					{
						// Call establishment messages
						case 0x01:	nAlerting[dir]++;				break;
						case 0x02:	nCallProceeding[dir]++;		break;
						case 0x07:	nConnect[dir]++;				break;
						case 0x0f:	nConnectAck[dir]++;			break;
						case 0x03:	nProgress[dir]++;				break;
						case 0x05:	nSetup[dir]++;					break;
						case 0x0d:	nSetupAck[dir]++;				break;
						// Call information phase messages
						case 0x26:	nResume[dir]++;				break;
						case 0x2e:	nResumeAck[dir]++;			break;
						case 0x22:	nResumeReject[dir]++;		break;
						case 0x25:	nSuspend[dir]++;				break;
						case 0x2d:	nSuspendAck[dir]++;			break;
						case 0x21:	nSuspendReject[dir]++;		break;
						case 0x20:	nUserInfo[dir]++;				break;
						// Call clearing messages
						case 0x45:	nDisconnect[dir]++;			break;
						case 0x4d:	nRelease[dir]++;				break;
						case 0x5a:	nReleaseComplete[dir]++;	break;
						case 0x46:	nRestart[dir]++;				break;
						case 0x4e:	nRestartAck[dir]++;			break;
						// Miscellaneous messages
						case 0x60:	nSegment[dir]++;				break;
						case 0x79:	nCongestionControl[dir]++;	break;
						case 0x7b:	nInformation[dir]++;			break;
						case 0x62:	nFacility[dir]++;				break;
						case 0x6e:	nNotify[dir]++;				break;
						case 0x7d:	nStatus[dir]++;				break;
						case 0x75:	nStatusEnquiry[dir]++;		break;
						default:		nL3Unknown[dir]++;			break;
					}
					break;

				default:
					nL3Unknown[dir]++;
					break;
			}
			break;
		}

		case 1:
			// Lapd Supervisory or Unnumbered Frame
			frame->SetFilterMask( fmLapd );
			switch ( Payload[2] & 0xEF )
			{
				case 0x01:  nRR[dir]++;				break;
				case 0x03:  nUI[dir]++;
								// Sometimes SETUP messages can come in UI frames.  If SETUP, override frametype filtermask
								if ( (Payload[0] & 0xFC) == 0 && (Payload[3] == 8 || Payload[3] == 0x41))
									frame->SetFilterMask(fmIl3);
								break;
				case 0x05:  nRNR[dir]++;			break;
				case 0x09:  nREJ[dir]++;			break;
				case 0x0F:  nDM[dir]++;				break;
				case 0x43:  nDISC[dir]++;			break;
				case 0x63:  nUA[dir]++;				break;
				case 0x6F:  nSABME[dir]++;			break;
				case 0x87:  nFRMR[dir]++;			break;
				default:    nL2Unknown[dir]++;	break;
			}
			break;
	}
}

BOOL CIsdnTraceData::DoFilterOptions(DWORD& mask, CDWordArray *circuit)
{
	CIsdnFilter dlg;
	DWORD OldMask = mask;
	switch ( mask )
	{
		default:
		case fmALL:
			dlg.m_FilterRadio = 0;
			break;

		case fmLapd:
			dlg.m_FilterRadio = 1;
			break;

		case fmIl3:
			dlg.m_FilterRadio = 2;
			break;
	}

	BOOL ok = dlg.DoModal() == IDOK;
	if (ok) 
		switch ( dlg.m_FilterRadio )
		{
			default:
			case 0:
				mask = fmALL;
				break;

			case 1:
				mask = fmLapd;
				break;

			case 2:
				mask = fmIl3;
				break;

		}
	return ok && (OldMask != mask );
}

void CIsdnTraceData::ClearTraceStats()
{
	CTraceData::ClearTraceStats();

	// Layer 2 frame counts
	nInfo[0] = nInfo[1] = 0;
	nRR[0] = nRR[1] = 0;
	nRNR[0] = nRNR[1] = 0;
	nREJ[0] = nREJ[1] = 0;
	nSABME[0] = nSABME[1] = 0;
	nDM[0] = nDM[1] = 0;
	nDISC[0] = nDISC[1] = 0;
	nUA[0] = nUA[1] = 0;
	nFRMR[0] = nFRMR[1] = 0;
	nUI[0] = nUI[1] = 0;
	nL2Unknown[0] = nL2Unknown[1] = 0;

	// Layer 3 frame counts
	nAlerting[0] = nAlerting[1] = 0;
	nCallProceeding[0] = nCallProceeding[1] = 0;
	nConnect[0] = nConnect[1] = 0;
	nConnectAck[0] = nConnectAck[1] = 0;
	nProgress[0] = nProgress[1] = 0;
	nSetup[0] = nSetup[1] = 0;
	nSetupAck[0] = nSetupAck[1] = 0;
	nResume[0] = nResume[1] = 0;
	nResumeAck[0] = nResumeAck[1] = 0;
	nResumeReject[0] = nResumeReject[1] = 0;
	nSuspend[0] = nSuspend[1] = 0;
	nSuspendAck[0] = nSuspendAck[1] = 0;
	nSuspendReject[0] = nSuspendReject[1] = 0;
	nUserInfo[0] = nUserInfo[1] = 0;
	nDisconnect[0] = nDisconnect[1] = 0;
	nRelease[0] = nRelease[1] = 0;
	nReleaseComplete[0] = nReleaseComplete[1] = 0;
	nRestart[0] = nRestart[1] = 0;
	nRestartAck[0] = nRestartAck[1] = 0;
	nSegment[0] = nSegment[1] = 0;
	nCongestionControl[0] = nCongestionControl[1] = 0;
	nFacility[0] = nFacility[1] = 0;
	nInformation[0] = nInformation[1] = 0;
	nNotify[0] = nNotify[1] = 0;
	nStatus[0] = nStatus[1] = 0;
	nStatusEnquiry[0] = nStatusEnquiry[1] = 0;
	nL3Unknown[0] = nL3Unknown[1] = 0;
}


/////////////////////////////////////////////////////////////////////////////
// CIsdnFrame

CString
DecodeBitMask( 
	ULONG Bits,
	ULONG Mask )
{
	CString msg;
	for ( ULONG Bit = 0x80; Bit; Bit >>= 1 )
		msg += (Mask & Bit) ? ( (Bit & Bits) ? "1" : "0" ) : ".";
	return msg;
}


CString
ToBitMaskDisplayString(
	CString    Label,		// Name of field
	ULONG      Value,		// Value being displayed
	ULONG      Mask,		// Bitmask to apply
	CString&   Decode )	// String definition of value
{
	CString msg = Label + ": " + DecodeBitMask( Value, Mask ) + " = " + Decode;
	return msg;
}


CString
ToDecDisplayString( 
	ULONG Value )
{
	CString msg;
	msg.Format( "%d", Value );
	return msg;
}


CString
ToDecHexDisplayString(
	CString    Label,		// Name of field
	ULONG      Value )	// Value being displayed
{
	CString msg;
	msg.Format( "%s: %d (0x%x)", Label, Value, Value );
	return msg;
}


CString
ToLengthString( 
	ULONG Length )
{
	return ToDecHexDisplayString( "Length", Length );
}


LPCSTR CIsdnFrame::GetProtocol() const
{
	return FilterTest(fmLapd) ? "Q921" : "Q931";
}

void CIsdnFrame::GetDescription(CString& txt) const
{
	if ( FilterTest(fmIl3) )
		AddIl3MessageType( txt );
	else
		AddLapdFrameType( txt );
}


void CIsdnFrame::DisplayTreeData(CTreeCtrl& tree, const CTraceData *td, CFrame *pFramePrev)
{
	CFrame::DisplayTreeData(tree, td, pFramePrev);	 
	TreeDataLapd(tree, TVI_ROOT);
}


#define LAPD_FRAMETYPE 0x03

DWORD
LapdFrameLength( DWORD FrameType )
{
	DWORD Length;
	switch ( FrameType & LAPD_FRAMETYPE )
	{
		case 0:
		case 1:	
		case 2:	Length = 4;		break;
		case 3:	Length = 3;		break;
	}
	return Length;
}

void CIsdnFrame::TreeDataLapd(CTreeCtrl& tree, HTREEITEM hParent)
{
	PUCHAR Payload = GetData();
	CString msg = "Q921 - ";
	AddLapdFrameType( msg );
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, 0, LapdFrameLength(Payload[2]));
	TreeDataLapdAddress( tree, item );

	switch ( Payload[2] & LAPD_FRAMETYPE )
	{
		case 1:	// Supervisory frame
			TreeDataLapdSupervisoryFrame( tree, item );
			break;

		case 3:	// Unnumbered frame
			TreeDataLapdUnnumberedFrame( tree, item, hParent );
			break;

		case 0:
		case 2:	// Information frame
			TreeDataLapdInfoFrame( tree, item );
			switch ( Payload[4] )
			{
				case 0x08:	
					TreeDataIl3Frame( tree, hParent, 4 );	
					break;

				case 0x03:
					TreeDataServiceMessage( tree, hParent, 4 );
					break;

				default:
					ULONG cb = GetDataLen() - 4;
					msg.Format( "Remaining Data, length %d (0x%x)", cb, cb );
					(void)InsertTreeItem( tree, msg, item, 4, 4+cb );
					break;
			}
			break;
	}
}


void CIsdnFrame::TreeDataLapdAddress(CTreeCtrl& tree, HTREEITEM hParent)
{
	PUCHAR Payload = GetData();
	CString msg;

	msg.Format( "Service Access Point Identifier (SAPI): %d", (Payload[0] & 0xFC) >> 2 );
	(void)InsertTreeItem( tree, msg, hParent, 0, 1 );

	msg.Format( "Command Response bit (C/R): %d", (Payload[0] & 0x02) >> 1 );
	(void)InsertTreeItem( tree, msg, hParent, 0, 1 );

	msg.Format( "Terminal Endpoint Identifier (TEI): %d", (Payload[1] & 0xFE) >> 1 );
	(void)InsertTreeItem( tree, msg, hParent, 1, 2 );
}


void CIsdnFrame::AddLapdFrameType( CString& msg ) const
{
	PUCHAR Payload = GetData();
	if ( (Payload[2] & 1) == 0 )	// Info Frame
		msg += "Information Frame";
	else
	{
		switch ( Payload[2] & 0xEF )
		{
			case 0x01:	msg += "RR (receive ready)";				break;
			case 0x05:	msg += "RNR (receive not ready)";		break;
			case 0x09:	msg += "REJ (reject)";						break;
			case 0x6f:	msg += "SABME (set asynchronous balance mode extended)";	break;
			case 0x0f:	msg += "DM (disconnect mode)";			break;
			case 0x03:	msg += "UI (unnumbered information)";	break;
			case 0x43:	msg += "DISC (disconnect)";				break;
			case 0x63:	msg += "UA (unnumbered acknowledge)";	break;
			case 0x87:	msg += "FRMR (frame reject)";				break;
			case 0xaf:	msg += "XID";									break;
			default:		msg += "Unknown";								break;
		}
	}
}


void CIsdnFrame::TreeDataLapdSupervisoryFrame(CTreeCtrl& tree, HTREEITEM hParent)
{
	PUCHAR Payload = GetData();
	CString msg = "Frame Type: ";
	AddLapdFrameType( msg );
	(void)InsertTreeItem(tree, msg, hParent, 2, 3);

	msg.Format( "Poll Final bit, (P/F): %d", Payload[3] & 0x01 );
	(void)InsertTreeItem( tree, msg, hParent, 3, 4 );

	msg.Format( "Receive sequence number, N(R): %d", (Payload[3] & 0xFE) >> 1 );
	(void)InsertTreeItem( tree, msg, hParent, 3, 4 );
}


CString
DecodeActionIndicator( 
	ULONG MessageType,
	ULONG Action )
{
	CString msg;
	msg.Format( "0x%x - ", Action );
	switch ( MessageType )
	{
		case 1:	msg += Action == 127 ? "Any TEI acceptable" : "Preferred TEI value";									break;
		case 2:	msg += "Assigned TEI value";																						break;
		case 3:	msg += "Denied TEI value";																							break;
		case 4:	msg += "TEI value to be checked";																					break;
		case 5:	msg += "TEI value in use";																							break;
		case 6:	msg += Action == 127 ? "Request for removal of all TEI values" : "TEI value to be removed";	break;
		default:	msg += "Unknown";																										break;
	}
	return msg;
}


void CIsdnFrame::TreeDataLapdUnnumberedFrame(CTreeCtrl& tree, HTREEITEM hParent, HTREEITEM hRootNode)
{
	ULONG cbRemain = GetDataLen();
	PUCHAR Payload = GetData();
	CString msg = "Frame Type: ";
	AddLapdFrameType( msg );
	(void)InsertTreeItem(tree, msg, hParent, 2, 3);

	msg.Format( "Poll Final bit (P/F): %d", (Payload[2] & 0x10) >> 4 );
	(void)InsertTreeItem( tree, msg, hParent, 2, 3 );

	switch ( Payload[2] & 0xEC )
	{
		case 0x00:	// UI
		{
			if ( ((Payload[0] & 0xFC) >> 2) == 63 && ((Payload[1] & 0xFE) >> 1) == 127)
			{
				// SAPI 63 and TEI 127 means TEI negotiation procedures
				msg = "TEI Management";
				HTREEITEM h = InsertTreeItem( tree, msg, hParent, 3, 8 );

				msg.Format( "Management entity identifier: %d (0x%x)", Payload[3], Payload[3] );
				(void)InsertTreeItem( tree, msg, h, 3, 4 );

				ULONG Reference = (Payload[4] << 8) + Payload[5];
				msg.Format( "Reference number: %d (0x%x)", Reference, Reference );
				(void)InsertTreeItem( tree, msg, h, 4, 6 );

				ULONG MessageType = Payload[6];
				msg.Format( "Message type: %d (0x%x) = ", MessageType, MessageType );
				switch ( MessageType )
				{
					case 1:	msg += "Identity request (user to network)";				break;
					case 2:	msg += "Identity assigned (network to user)";			break;
					case 3:	msg += "Identity denied (network to user)";				break;
					case 4:	msg += "Identity check request (network to user)";		break;
					case 5:	msg += "Identity check response (user to network)";	break;
					case 6:	msg += "Identity remove (network to user)";				break;
					default: msg += "Unknown";
				}
				(void)InsertTreeItem( tree, msg, h, 6, 7 );

				ULONG Action = (Payload[7] & 0xFE) >> 1;
				msg = ToBitMaskDisplayString( "Action indicator", Payload[7], 0xFE, DecodeActionIndicator(MessageType, Action) );
				(void)InsertTreeItem( tree, msg, h, 7, 8 );
				cbRemain -= 8;
			}
			else if ( (Payload[0] & 0xFC) == 0 && (Payload[3] == 8 || Payload[3] == 0x41))
			{
				// Sapi=0, ProtocolDescriminator=Q931 or ProtocolDescriminator=German/1TR6 means IL3 SETUP message
				TreeDataIl3Frame( tree, hRootNode, 3 );
				cbRemain = 0;
			}
			else
				cbRemain -= 3;
			break;
		}

		case 0x84:	// FRMR
		{
			msg = "FRMR";
			HTREEITEM hFrmr = InsertTreeItem( tree, msg, hParent, 3, GetDataLen() );
			msg.Format( "Rejected frame control field: 0x%x 0x%x", Payload[3], Payload[4] );
			(void)InsertTreeItem( tree, msg, hFrmr, 3, 5 );

			msg.Format( "Send state variable, V(S): %d", (Payload[5] & 0xFE) >> 1 );
			(void)InsertTreeItem( tree, msg, hFrmr, 5, 6 );

			msg.Format( "Receive state variable, V(R): %d", (Payload[6] & 0xFE) >> 1 );
			(void)InsertTreeItem( tree, msg, hFrmr, 6, 7 );

			msg.Format( "Command Response bit (C/R): %d", Payload[6] & 0x01 );
			(void)InsertTreeItem( tree, msg, hFrmr, 6, 7 );

			msg.Format( "Flags ....xxxx: 0x%x", Payload[7] & 0x0F );
			{
				HTREEITEM hFlags = InsertTreeItem( tree, msg, hFrmr, 7, 8 );
				msg.Format( "....%d... Invalid N(R)", Payload[7] & 0x08 );
				(void)InsertTreeItem( tree, msg, hFlags, 7, 8 );

				msg.Format( ".....%d.. Information field exceeded maximum size", Payload[7] & 0x04 );
				(void)InsertTreeItem( tree, msg, hFlags, 7, 8 );

				msg.Format( "......%d. Invalid or incorrect data length for control field", Payload[7] & 0x02 );
				(void)InsertTreeItem( tree, msg, hFlags, 7, 8 );

				msg.Format( ".......%d Control field undefined or not implemented", Payload[7] & 0x01 );
				(void)InsertTreeItem( tree, msg, hFlags, 7, 8 );
			}
			cbRemain -= 8;
			break;
		}

		default:
			cbRemain -= 3;
			break;
	}

	if ( cbRemain )
	{
		msg.Format( "Remaining data, length %d (0x%x)", cbRemain, cbRemain );
		(void)InsertTreeItem( tree, msg, hParent, 8, GetDataLen() );
	}
}


void CIsdnFrame::TreeDataLapdInfoFrame(CTreeCtrl& tree, HTREEITEM hParent)
{
	PUCHAR Payload = GetData();
	CString msg;
	msg.Format( "Send sequence number, N(S): %d", (Payload[2] & 0xFE) >> 1 );
	(void)InsertTreeItem(tree, msg, hParent, 2, 3);

	msg.Format( "Receive sequence number, N(R): %d", (Payload[3] & 0xFE) >> 1 );
	(void)InsertTreeItem(tree, msg, hParent, 3, 4);

	msg.Format( "Poll Final bit, (P/F): %d", Payload[3] & 0x01 );
	(void)InsertTreeItem( tree, msg, hParent, 3, 4 );

	ULONG cb = GetDataLen() - 4;	// size - header
	msg.Format( "Data: Number of data bytes remaining = %d (0x%x)", cb, cb );
	(void)InsertTreeItem( tree, msg, hParent, 4, GetDataLen() );
}


CString
DecodeQ931MessageType( ULONG Type )
{
	CString msg;
	switch ( Type )
	{
		case 0x00:	msg = "National specific";		break;

		// Call establishment messages
		case 0x01:	msg = "ALERTING";					break;
		case 0x02:	msg = "CALL PROCEEDING";		break;
		case 0x07:	msg = "CONNECT";					break;
		case 0x0f:	msg = "CONNECT ACKNOWLEDGE";	break;
		case 0x03:	msg = "PROGRESS";					break;
		case 0x05:	msg = "SETUP";						break;
		case 0x0d:	msg = "SETUP ACKNOWLEDGE";		break;
		// Call information phase messages
		case 0x26:	msg = "RESUME";					break;
		case 0x2e:	msg = "RESUME ACKNOWLEDGE";	break;
		case 0x22:	msg = "RESUME REJECT";			break;
		case 0x25:	msg = "SUSPEND";					break;
		case 0x2d:	msg = "SUSPEND ACKNOWLEDGE";	break;
		case 0x21:	msg = "SUSPEND REJECT";			break;
		case 0x20:	msg = "USER INFORMATION";		break;
		// Call clearing messages
		case 0x45:	msg = "DISCONNECT";				break;
		case 0x4d:	msg = "RELEASE";					break;
		case 0x5a:	msg = "RELEASE COMPLETE";		break;
		case 0x46:	msg = "RESTART";					break;
		case 0x4e:	msg = "RESTART ACKNOWLEDGE";	break;
		// Miscellaneous messages
		case 0x60:	msg = "SEGMENT";					break;
		case 0x79:	msg = "CONGESTION CONTROL";	break;
		case 0x7b:	msg = "INFORMATION";				break;
		case 0x62:	msg = "FACILITY";					break;
		case 0x6e:	msg = "NOTIFY";					break;
		case 0x7d:	msg = "STATUS";					break;
		case 0x75:	msg = "STATUS ENQUIRY";			break;
		default:		msg = "Unknown";					break;
	}
	return msg;
}


CString
DecodeMaintenanceMessageType( ULONG Type )
{
	CString msg;
	switch ( Type )
	{
		case 0x07:	msg = "SERVICE ACKNOWLEDGE";	break;
		case 0x0F:	msg = "SERVICE";					break;
		default:		msg = "Unknown";					break;
	}
	return msg;
}


void CIsdnFrame::AddIl3MessageType( CString& msg ) const
{
	if ( GetDataLen() < 5 )
	{
		msg += "Invalid";
		return;
	}

	PUCHAR Payload = GetData();
	int TypeOffset = 6 + (Payload[5] & 0x0F);

	if ( GetDataLen() < TypeOffset )
	{
		msg += "Invalid";
		return;
	}

	ULONG ProtocolDiscriminator = Payload[4];
	switch ( ProtocolDiscriminator )
	{
		case 0x03:	msg += DecodeMaintenanceMessageType( Payload[TypeOffset] );	break;
		case 0x08:	msg += DecodeQ931MessageType( Payload[TypeOffset] );			break;
		default:		msg += "Reserved";														break;
	}
}


void CIsdnFrame::TreeDataServiceMessage(CTreeCtrl& tree, HTREEITEM hParent, ULONG Start)
{
	PUCHAR Payload = GetData();
	ULONG Offset = Start;
	ULONG CallRefLength = Payload[Start+1] & 0x0F;
	CString msg;

	ULONG MessageType = Payload[Start+2+CallRefLength];
	msg = "Maintenance - " + DecodeMaintenanceMessageType(MessageType);
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+CallRefLength+3 );

	ULONG Protocol = Payload[Offset];
	msg.Format(  "Protocol descriminator: 0x%x", Payload[Offset] );
	if ( Payload[Offset] == 0x03 )
		msg += " = Maintenance message";
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
	Offset++;

	msg.Format( "Call reference length: %d (0x%x)", CallRefLength, CallRefLength );
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
	Offset++;

	msg = "Call reference value: ";
	for ( ULONG i = 0; i < CallRefLength; i++ )
	{
		CString data;
		data.Format( " %02x", Payload[Offset+i] );
		msg += data;
	}
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+CallRefLength );
	Offset += CallRefLength;

	msg = "Message type: " + DecodeMaintenanceMessageType( MessageType );
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
	Offset++;

	// Display Info Elements
	TreeDataIl3Ie( tree, hParent, Offset );
}


void CIsdnFrame::TreeDataIl3Frame(CTreeCtrl& tree, HTREEITEM hParent, ULONG Start)
{
	PUCHAR Payload = GetData();
	ULONG Offset = Start;
	ULONG CallRefLength = Payload[Start+1] & 0x0F;
	CString msg;

	ULONG MessageType = Payload[Start+2+CallRefLength];
	msg = "Q931 - " + DecodeQ931MessageType( MessageType );
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+CallRefLength+3 );

	ULONG Protocol = Payload[Offset];
	msg.Format(  "Protocol descriminator: 0x%x", Payload[Offset] );
	if ( Payload[Offset] == 0x08 )
		msg += " = Q931";
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
	Offset++;

	msg.Format( "Call reference length: %d (0x%x)", CallRefLength, CallRefLength );
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
	Offset++;

	msg = "Call reference value: ";
	for ( ULONG i = 0; i < CallRefLength; i++ )
	{
		CString data;
		data.Format( " %02x", Payload[Offset+i] );
		msg += data;
	}
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+CallRefLength );
	Offset += CallRefLength;

	msg = "Message type: " + DecodeQ931MessageType( MessageType );
	(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
	Offset++;

	// Display Info Elements
	TreeDataIl3Ie( tree, hParent, Offset );
}


#if 0
static BOOL IsQ931Note12Diagnostic( ULONG Diag )
{
	BOOL Valid;
	switch ( Diag )
	{
		case 0x80:	Valid = TRUE;	break;
		case 0x81:	Valid = TRUE;	break;
		case 0x82:	Valid = TRUE;	break;
		default:		Valid = FALSE;	break;
	}
	return Valid;
}

static CString& Q931Note12Diagnostic( ULONG Diag )
{
	CString msg;
	switch ( Diag )
	{
		case 0x80:	msg = "Unknown";		break;
		case 0x81:	msg = "Permanent";	break;
		case 0x82:	msg = "Transient";	break;
		default:		msg = "Invalid";		break;
	}
	return msg;
}
#endif


typedef enum
{
	DIAG_TYPE_NONE,
	DIAG_TYPE_Q931NOTE12,
	DIAG_TYPE_USER_OR_Q931NOTE12,
	DIAG_TYPE_TRANSIT_NETWORK_IDENTITY,
	DIAG_TYPE_NEW_DESTINATION,
	DIAG_TYPE_FACILITY_IDENTIFICATION,
	DIAG_TYPE_IE,
	DIAG_TYPE_BEARER_CAP_ERR,
	DIAG_TYPE_CHANNEL_TYPE,
	DIAG_TYPE_CHANNEL_IDENTIFICATION,
	DIAG_TYPE_CLEARING_CAUSE,
	DIAG_TYPE_MESSAGE_TYPE,
	DIAG_TYPE_TIMER_NUMBER,
	DIAG_TYPE_COUNT
} DIAG_TYPE;


typedef struct CAUSE_DECODE
{
	ULONG     Code;
	PCSTR     Name;
	DIAG_TYPE DiagType;
} CAUSE_DECODE;

static CAUSE_DECODE CauseDecoder[] =
{
	{1,	"Unallocated (unassigned) number",	DIAG_TYPE_Q931NOTE12 },
	{2,	"No route to specified transit network",	DIAG_TYPE_TRANSIT_NETWORK_IDENTITY },
	{3,	"No route to destination",	DIAG_TYPE_Q931NOTE12 },
	{6,	"Channel unacceptable",	DIAG_TYPE_NONE },
	{7,	"Call awarded and being delivered in an established channel",	DIAG_TYPE_NONE },
	{16,	"Normal call clearing",	DIAG_TYPE_Q931NOTE12 },
	{17,	"User busy",	DIAG_TYPE_NONE },
	{18,	"No user responding",	DIAG_TYPE_NONE },
	{19,	"No answer from user (user alerted)",	DIAG_TYPE_NONE },
	{21,	"Call rejected",	DIAG_TYPE_USER_OR_Q931NOTE12 },
	{22,	"Number changed",	DIAG_TYPE_NEW_DESTINATION },
	{26,	"Non-selected user clearing",	DIAG_TYPE_NONE },
	{27,	"Destination out of order",	DIAG_TYPE_NONE },
	{28,	"Invalid number format",	DIAG_TYPE_NONE },
	{29,	"Facility rejected",	DIAG_TYPE_FACILITY_IDENTIFICATION },
	{30,	"Response to STATUS ENQUIRY",	DIAG_TYPE_NONE },
	{31,	"Normal, unspecified",	DIAG_TYPE_NONE },
	{34,	"No circuit/channel available",	DIAG_TYPE_NONE },
	{38,	"Network out of order",	DIAG_TYPE_NONE },
	{41,	"Temporary failure",	DIAG_TYPE_NONE },
	{42,	"Switching equipment congestion",	DIAG_TYPE_NONE },
	{43,	"Access information discarded",	DIAG_TYPE_IE },
	{44,	"Requested circuit/channel not available",	DIAG_TYPE_NONE },
	{47,	"Resources unavailable, unspecified",	DIAG_TYPE_NONE },
	{49,	"Quality of service unavailable",	DIAG_TYPE_Q931NOTE12 },
	{50,	"Requested facility not subscribed",	DIAG_TYPE_FACILITY_IDENTIFICATION },
	{57,	"Bearer capability not authorized",	DIAG_TYPE_BEARER_CAP_ERR },
	{58,	"Bearer capability not presently available",	DIAG_TYPE_BEARER_CAP_ERR },
	{63,	"Service or option not available, unspecified",	DIAG_TYPE_NONE },
	{65,	"Bearer capability not implemented",	DIAG_TYPE_BEARER_CAP_ERR },
	{66,	"Channel type not implemented",	DIAG_TYPE_CHANNEL_TYPE },
	{69,	"Requested facility not implemented",	DIAG_TYPE_FACILITY_IDENTIFICATION },
	{70,	"Only restricted digital information bearer capability is available",	DIAG_TYPE_NONE },
	{79,	"Service or option not implemented, unspecified",	DIAG_TYPE_NONE },
	{81,	"Invalid call reference value",	DIAG_TYPE_NONE },
	{82,	"Identified channel does not exist",	DIAG_TYPE_CHANNEL_IDENTIFICATION },
	{83,	"A suspended call exists, but this call identity does not",	DIAG_TYPE_NONE },
	{84,	"Call identity in use",	DIAG_TYPE_NONE },
	{85,	"No call suspended",	DIAG_TYPE_NONE },
	{86,	"Call having the requested call identity has been cleared",	DIAG_TYPE_CLEARING_CAUSE },
	{88,	"Incompatible destination",	DIAG_TYPE_IE },
	{91,	"Invalid transit network selection",	DIAG_TYPE_NONE },
	{95,	"Invalid message, unspecified",	DIAG_TYPE_NONE },
	{96,	"Mandatory information element is missing",	DIAG_TYPE_IE },
	{97,	"Message type non-existent or not implemented",	DIAG_TYPE_MESSAGE_TYPE },
	{98,	"Message not compatible with call state or message type non-existent or not implemented",	DIAG_TYPE_MESSAGE_TYPE },
	{99,	"Information element non-existent or not implemented",	DIAG_TYPE_IE },
	{100,	"Invalid information element contents",	DIAG_TYPE_IE },
	{101,	"Message not compatible with call state",	DIAG_TYPE_MESSAGE_TYPE },
	{102,	"Recovery on timer expiry",	DIAG_TYPE_TIMER_NUMBER },
	{111,	"Protocol error, unspecified",	DIAG_TYPE_NONE },
	{127,	"Interworking, unspecified",	DIAG_TYPE_NONE }
};


CString GetCauseName( ULONG Code )
{
	CString msg = "Invalid";
	ULONG i;
	for ( i = 0; i < sizeof CauseDecoder / sizeof CauseDecoder[0]; i++ )
	{
		if ( Code == CauseDecoder[i].Code )
		{
			msg = CauseDecoder[i].Name;
			break;
		}
	}
	return msg;
}


DIAG_TYPE GetCauseDiagType( ULONG Code )
{
	DIAG_TYPE Type = DIAG_TYPE_NONE;	
	for ( ULONG i = 0; i < sizeof CauseDecoder / sizeof CauseDecoder[0]; i++ )
	{
		if ( Code == CauseDecoder[i].Code )
		{
			Type = CauseDecoder[i].DiagType;
			break;
		}
	}
	return Type;
}

CString
DecodeNumberingType( 
	ULONG Type )
{
	CString msg;
	switch ( Type )
	{
		case 0x00:	msg = "Unknown";				break;
		case 0x01:	msg = "International";		break;
		case 0x02:	msg = "National";				break;
		case 0x03:	msg = "Network specific";	break;
		case 0x04:	msg = "Subscriber";			break;
		case 0x06:	msg = "Abbreviated";			break;
		default:		msg = "Reserved"; 			break;
	}
	return msg;
}

CString
DecodeNumberingPlan( 
	ULONG Plan )
{
	CString msg;
	switch ( Plan )
	{
		case 0x00:	msg = "Unknown";				break;
		case 0x01:	msg = "ISDN";					break;
		case 0x03:	msg = "Data";					break;
		case 0x04:	msg = "Telex";					break;
		case 0x08:	msg = "National standard";	break;
		case 0x09:	msg = "Private";				break;
		default:		msg = "Reserved"; 			break;
	}
	return msg;
}

CString
DecodeCodingStandard( 
	ULONG Coding )
{
	CString msg;
	switch ( Coding )
	{
		case 0x00:	msg = "CCITT";									break;
		case 0x01:  msg = "Other International standard";	break;
		case 0x02:  msg = "National standard";					break;
		case 0x03:  msg = "Local standard";						break;
	}
	return msg;
}

CString
DecodeLocation( 
	ULONG Location )
{
	CString msg;
	switch ( Location )
	{
		case 0x00:	msg = "User";												break;
		case 0x01:	msg = "Local private network";						break;
		case 0x02:	msg = "Local public network";							break;
		case 0x04:	msg = "Remote public network";						break;
		case 0x05:	msg = "Remote private network";						break;
		case 0x0A:	msg = "Network beyond internetworking point";	break;
		default:		msg = "Reserved";											break;
	}
	return msg;
}


CString
DecodeProgress( 
	ULONG Progress )
{
	CString msg;
	switch ( Progress )
	{
		case 0x01:	msg = "Call is not end-to-end ISDN";			break;
		case 0x02:	msg = "Destination address is non-ISDN";		break;
		case 0x03:	msg = "Origination address is non-ISDN";		break;
		case 0x04:	msg = "Call has returned to the ISDN";			break;
		case 0x08:	msg = "In-band information now available";	break;
		default:		msg = "Reserved";										break;
	}
	return msg;
}


void
DisplayProgressIndicator( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Offset, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Progress indicator";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );
	
	ULONG Current = Offset + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	ULONG Coding = (Payload[Current] & 0x60) >> 5;
	msg = ToBitMaskDisplayString( "Coding standard", Payload[Current], 0x60, DecodeCodingStandard(Coding) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	ULONG Location = Payload[Current] & 0x0F;
	msg = ToBitMaskDisplayString( "Location", Payload[Current], 0x0F, DecodeLocation(Location) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	Current++;
	ULONG Progress = Payload[Current] & 0x7F;
	msg = ToBitMaskDisplayString( "Progress description", Payload[Current], 0x7F, DecodeProgress(Progress) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
}


void
DisplaySignal( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Offset, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Signal";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );
	
	ULONG Current = Offset + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	ULONG Signal = Payload[Current];
	msg = ToDecHexDisplayString( "Signal value", Signal );
	msg += " = ";
	switch ( Signal )
	{
		case 0x00:	msg += "dial tone on";					break;
		case 0x01:	msg += "ring back tone on";			break;
		case 0x02:	msg += "intercept tone on";			break;
		case 0x03:	msg += "network congestion tone on";break;
		case 0x04:	msg += "busy tone on";					break;
		case 0x05:	msg += "confirm tone on";				break;
		case 0x06:	msg += "answer tone on";				break;
		case 0x07:	msg += "call waiting tone on";		break;
		case 0x08:	msg += "off-hook warning tone on";	break;
		case 0x3F:	msg += "tones off";						break;
		case 0x40:	msg += "alerting on - pattern 0";	break;
		case 0x41:	msg += "alerting on - pattern 1";	break;
		case 0x42:	msg += "alerting on - pattern 2";	break;
		case 0x43:	msg += "alerting on - pattern 3";	break;
		case 0x44:	msg += "alerting on - pattern 4";	break;
		case 0x45:	msg += "alerting on - pattern 5";	break;
		case 0x46:	msg += "alerting on - pattern 6";	break;
		case 0x47:	msg += "alerting on - pattern 7";	break;
		case 0x4F:	msg += "alerting off";					break;
		default:    msg += "Undefined";						break;
	}
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
}


CString
DecodeCallState( 
	ULONG CallState )
{
	CString msg;
	switch ( CallState )
	{
		case 0x00:	msg = "Null";								break;
		case 0x01:	msg = "Call initiated";					break;
		case 0x02:	msg = "Overlap sending";				break;
		case 0x03:	msg = "Outgoing call proceeding";	break;
		case 0x04:	msg = "Call delivered";					break;
		case 0x06:	msg = "Call present";					break;
		case 0x07:	msg = "Call received";					break;
		case 0x08:	msg = "Connect request";				break;
		case 0x09:	msg = "Incoming call proceeding";	break;
		case 0x0a:	msg = "Active";							break;
		case 0x0b:	msg = "Disconnect request";			break;
		case 0x0c:	msg = "Disconnect indication";		break;
		case 0x0f:	msg = "Suspend request";				break;
		case 0x11:	msg = "Resume request";					break;
		case 0x13:	msg = "Release request";				break;
		case 0x16:	msg = "Call abort";						break;
		case 0x19:	msg = "Overlap receiving";				break;
		case 0x3d:	msg = "Restart request";				break;
		case 0x3e:	msg = "Restart";							break;
		default:		msg = "Reserved";							break;
	}
	return msg;
}


void
DisplayCallState( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Offset, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Call state";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );
	
	ULONG Current = Offset + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	ULONG Coding = (Payload[Current] & 0x60) >> 5;
	msg = ToBitMaskDisplayString( "Coding standard", Payload[Current], 0x60, DecodeCodingStandard(Coding) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	ULONG CallState = Payload[Current] & 0x3F;
	msg = ToBitMaskDisplayString( "CallState", Payload[Current], 0x3F, DecodeCallState(CallState) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
}


void
DisplayCallIdentity( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Start, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Call identity";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Start, Start+TotalIeLength );
	
	ULONG Current = Start + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	msg = "Identity: ";
	for ( ULONG i = 0; i < IeLength+2-(Current-Start); i++ )
	{
		CString Digit;
		Digit.Format( "%c", Payload[Current+i] );
		msg += Digit;
	}
	(void)InsertTreeItem(tree, msg, item, Current, Start+TotalIeLength );
}


void
DisplayRestartIndicator( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Start, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Restart indicator";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Start, Start+TotalIeLength );
	
	ULONG Current = Start + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	UCHAR Class = Payload[Current] & 0x07;
	CString ClassDecode;
	switch ( Class )
	{
		case 0:	ClassDecode = "Indicated channels (B channel)";			break;
		case 6:	ClassDecode = "Single interface (any channel type)";	break;
		default:	ClassDecode = "Reserved";				break;
	}
	msg = ToBitMaskDisplayString( "Class", Payload[Current], 0x07, ClassDecode );
	(void)InsertTreeItem(tree, msg, item, Current, Start+TotalIeLength );
}


void
DisplayCallingPartyNumber( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Start, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Calling party number";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Start, Start+TotalIeLength );
	
	ULONG Current = Start + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	ULONG Type = (Payload[Current] & 0x70) >> 4;
	msg = ToBitMaskDisplayString( "Type", Payload[Current], 0x70, DecodeNumberingType(Type) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	ULONG Plan = Payload[Current] & 0x0F;
	msg = ToBitMaskDisplayString( "Numbering plan", Payload[Current], 0x0F, DecodeNumberingPlan( Plan ) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	if ( (Payload[Current] & 0x80) == 0 )
	{
		Current++;
		ULONG Presentation = (Payload[Current] & 0x60) >> 5;
		switch ( Presentation )
		{
			case 0:	msg = "Presentation allowed";								break;
			case 1:	msg = "Presentation restricted";							break;
			case 2:	msg = "Number not available due to interworking";	break;
			case 3:	msg = "Reserved";												break;
		}
		msg = ToBitMaskDisplayString( "Presentation indicator", Payload[Current], 0x60, msg );
		(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

		ULONG Screening = Payload[Current] & 0x03;
		switch ( Screening )
		{
			case 0:	msg = "User provided, not screened";			break;
			case 1:	msg = "User provided, verified and passed";	break;
			case 2:	msg = "User provided, verified and failed";	break;
			case 3:	msg = "Network provided";							break;
		}
		msg = ToBitMaskDisplayString( "Screening indicator", Payload[Current], 0x03, msg );
		(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
	}

	Current++;
	msg = "Number: ";
	for ( ULONG i = 0; i < IeLength+2-(Current-Start); i++ )
	{
		CString Digit;
		Digit.Format( "%c", Payload[Current+i] );
		msg += Digit;
	}
	(void)InsertTreeItem(tree, msg, item, Current, Start+TotalIeLength );
}


void
DisplayCalledPartyNumber( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Offset, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Called party number";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );
	
	ULONG Current = Offset + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	ULONG Type = (Payload[Current] & 0x70) >> 4;
	msg = ToBitMaskDisplayString( "Type", Payload[Current], 0x70, DecodeNumberingType(Type) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	ULONG Plan = Payload[Current] & 0x0F;
	msg = ToBitMaskDisplayString( "Numbering plan", Payload[Current], 0x0F, DecodeNumberingPlan( Plan ) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	Current++;
	msg = "Number: ";
	for ( ULONG i = 0; i < IeLength-1; i++ )
	{
		CString Digit;
		Digit.Format( "%c", Payload[Current+i] );
		msg += Digit;
	}
	(void)InsertTreeItem(tree, msg, item, Current, Offset+TotalIeLength );
}


CString
DecodeDisplayType( ULONG Type )
{
	CString msg;
	switch ( Type )
	{
		case 0:	msg = "Normal";	break;
		default:	msg = "Reserved";	break;
	}
	return msg;
}

CString
DecodeDisplayTag( ULONG Tag )
{
	CString msg;
	switch ( Tag )
	{
		case 0x80:	msg = "Blank";							break;
		case 0x81:	msg = "Skip";							break;
		case 0x82:	msg = "Continuation";				break;
		case 0x83:	msg = "Called address";				break;
		case 0x84:	msg = "Cause";							break;
		case 0x85:	msg = "Progress indicator";		break;
		case 0x86:	msg = "Notification indicator";	break;
		case 0x87:	msg = "Prompt";						break;
		case 0x88:	msg = "Accumulated digits";		break;
		case 0x89:	msg = "Status";						break;
		case 0x8A:	msg = "Inband";						break;
		case 0x8B:	msg = "Calling address";			break;
		case 0x8C:	msg = "Reason";						break;
		case 0x8D:	msg = "Calling party name";		break;
		case 0x8E:	msg = "Called party name";			break;
		case 0x8F:	msg = "Original called name";		break;
		case 0x90:	msg = "Redirecting name";			break;
		case 0x91:	msg = "Connected name";				break;
		case 0x92:	msg = "Originating restrictions";break;
		case 0x93:	msg = "Date & time of day";		break;
		case 0x94:	msg = "Call apperance ID";			break;
		case 0x95:	msg = "Feature address";			break;
		case 0x96:	msg = "Redirection name";			break;
		case 0x9E:	msg = "Text";							break;
		default:		msg = "Unknown";						break;
	}
	return msg;
}

void
DisplayCodeset5DisplayText( 
	CTreeCtrl& tree, 
	HTREEITEM  hParent, 
	PUCHAR     Payload, 
	ULONG      Offset, 
	ULONG      IeLength,
	ULONG      TotalIeLength,
	CString&   IePrefix )
{
	CString msg = IePrefix + "Display text";
	HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );
	
	ULONG Current = Offset + 1;
	(void)InsertTreeItem(tree, ToLengthString(IeLength), item, Current, Current+1 );

	Current++;
	ULONG Type = Payload[Current] & 0x7F;
	msg = ToBitMaskDisplayString( "Display type", Payload[Current], 0x7F, DecodeDisplayType(Type) );
	(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

	// Display Text IEs include one or more fields of display information
	Current++;
	while ( Current-Offset < TotalIeLength )
	{
		// Each "field" is a Tag/Length/Value triplet.  Where Tag & length are one octet
		// each, and value is length octets, typically of text.
		ULONG Tag = Payload[Current];
		ULONG Length = Payload[Current+1];
		msg.Format( "Display information: %s", DecodeDisplayTag(Tag) );
		HTREEITEM hTag = InsertTreeItem(tree, msg, item, Current, Current+Length+2 );

		// Tag
		msg.Format( "Tag: %s", DecodeDisplayTag(Tag) );
		(void)InsertTreeItem(tree, msg, hTag, Current, Current+1 );

		// Length
		Current++;
		(void)InsertTreeItem(tree, ToLengthString(Length), hTag, Current, Current+1 );

		// Value
		Current++;
		switch ( Tag )
		{
			case 0x80:
			case 0x81:
				msg.Format( "Value: 0x%x characters", Payload[Current] );
				break;
			default:
			{
				ULONG i;
				msg = "Value: ";
				for ( i = 0; i < Length; i++ )
					msg.Format( "%s%c", msg, Payload[Current+i] );
				break;
			}
		}
		(void)InsertTreeItem(tree, msg, hTag, Current, Current+Length );
		Current += Length;
	}
	
}


void CIsdnFrame::TreeDataIl3Ie(CTreeCtrl& tree, HTREEITEM hParent, ULONG Offset )
/*++

Comments:
	Loop until all IE's are processed.

--*/
{
	PUCHAR Payload = GetData();
	ULONG cb = GetDataLen() - Offset;
	UCHAR Codeset, PreviousCodeset = Codeset = 0;
	BOOL fPreviousCodeset = FALSE;

	while ( cb )
	{
		CString msg = "Information Element - ";
		UCHAR Id = Payload[Offset];

		// Is it a single octet IE?
		if ( Id & 0x80 )
		{
			// Yes, single.
			switch ( Id & 0x70 )
			{
				case 0x00:
					msg += "Reserved";
					(void)InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
					break;

				case 0x10:
					if ( Id & 0x08 )
					{
						// Nonlocking shift.  Process one IE and return to previous codeset
						msg += "Non-locking shift procecure";
						PreviousCodeset = Codeset;
						fPreviousCodeset = TRUE;
					}
					else
					{
						msg += "Locking shift procedure";
						fPreviousCodeset = FALSE;
					}

					{
						HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
						Codeset = Id & 0x07;

						msg = ToBitMaskDisplayString( "New codeset", Codeset, 0x07, ToDecDisplayString(Codeset) );
						(void)InsertTreeItem( tree, msg, item, Offset, Offset+1 );
					}

					Offset++;
					cb--;
					continue;	// Special case: Skip PostProcessing!

				case 0x20:
					switch ( Id & 0x0F )
					{
						case 0x00:	msg += "More Data";			break;
						case 0x01:	msg += "Sending complete";	break;
						default:		msg += "Unknown";				break;
					}
					(void)InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
					break;

				case 0x30:
					msg += "Congestion level";
					{
						HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
						switch ( Id & 0x0F )
						{
							case 0x00:	msg = "receiver ready";			break;
							case 0x0F:	msg = "receiver not ready";	break;
							default:		msg = "Unknown";					break;
						}
						(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
					}
					break;

				case 0x50:
					msg += "Repeat indicator";
					{
						HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
						switch ( Id & 0x0F )
						{
							case 0x02:	msg = "Prioritized list";		break;
							default:		msg = "Unknown";					break;
						}
						(void)InsertTreeItem(tree, msg, item, Offset, Offset+1 );
					}
					break;

				default:
					msg += "Unknown";
					(void)InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
					break;
			}

			Offset++;
			cb--;
			goto PostProcessing;
		}

		if ( cb < 2 )
		{
			msg += "Invalid, no length field";
			(void)InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
			Offset += cb;
			cb     -= cb;
			break;	// Stop processing!
		}

		{
			ULONG IeLength = Payload[Offset+1];
			ULONG TotalIeLength = 2 + IeLength;	// two byte header (Id field + len field) + (IE contents)
			if ( cb < TotalIeLength )
			{
				msg += "Invalid, length field greater than received octet count";
				(void)InsertTreeItem(tree, msg, hParent, Offset, Offset+1 );
				Offset += cb;
				cb     -= cb;
				break;	// Stop processing!
			}

			switch ( Codeset )
			{
				case 0:
					switch ( Id )
					{
						case 0x00: msg += "Segmented message";				goto DisplayDefault;
						case 0x0c: msg += "Connected address";				goto DisplayDefault;	// ???
						case 0x1c: msg += "Facility";							goto DisplayDefault;
						case 0x20: msg += "Network specific facilities";goto DisplayDefault;
						case 0x27: msg += "Notification indicator";		goto DisplayDefault;
						case 0x28: msg += "Display";							goto DisplayDefault;
						case 0x29: msg += "Date/time";						goto DisplayDefault;
						case 0x2c: msg += "Keypad facility";				goto DisplayDefault;
						case 0x32: msg += "Information request";			goto DisplayDefault;	// ???
						case 0x37: msg += "Switchhook";						goto DisplayDefault;
						case 0x3a: msg += "Service profile identifier (SPID)";	goto DisplayDefault;
						case 0x3b: msg += "Endpoint identifier";			goto DisplayDefault;
						case 0x40: msg += "Information rate";				goto DisplayDefault;
						case 0x42: msg += "End-to-end transit delay";	goto DisplayDefault;
						case 0x43: msg += "Transit delay selection and indication";	goto DisplayDefault;
						case 0x44: msg += "Packet layer binary parameters";	goto DisplayDefault;
						case 0x45: msg += "Packet layer window size";	goto DisplayDefault;
						case 0x46: msg += "Packet size";						goto DisplayDefault;
						case 0x6d: msg += "Calling party subaddress";	goto DisplayDefault;
						case 0x71: msg += "Called party subaddress";		goto DisplayDefault;
						case 0x74: msg += "Redirecting number";			goto DisplayDefault;
						case 0x78: msg += "Transit network selection";	goto DisplayDefault;
						case 0x7c: msg += "Low layer compatibility";		goto DisplayDefault;
						case 0x7e: msg += "User-user Information";		goto DisplayDefault;
						case 0x7f: msg += "Escape for extension";			goto DisplayDefault;
						default:   msg += "Unknown:";							goto DisplayDefault;

						case 0x01: 
						{
							msg += "Change status";
							HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );

							ULONG Current = Offset + 1;
							msg.Format( "Length: %d (0x%x)", IeLength, IeLength );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							// Octet 3
							Current++;
							ULONG Preference = (Payload[Current] & 0x40) >> 6;
							CString PrefDecode;
							switch ( Preference )
							{
								case 0:	PrefDecode = "Interface";	break;
								case 1:	PrefDecode = "Channel";		break;
							}
							msg = ToBitMaskDisplayString( "Preference", Payload[Current], 0x40, PrefDecode );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG Status = (Payload[Current] & 0x07);
							CString StatusDecode;
							switch ( Status )
							{
								case 0x00:	StatusDecode = "In service";		break;
								case 0x01:	StatusDecode = "Maintenance";		break;
								case 0x02:	StatusDecode = "Out of service";	break;
								default:		StatusDecode = "Reserved";			break;
							}
							msg = ToBitMaskDisplayString( "New status", Payload[Current], 0x07, StatusDecode );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
							break;
						}

						case 0x04: msg += "Bearer capability";
						{
							HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );

							ULONG Current = Offset + 1;
							msg.Format( "Length: %d (0x%x)", IeLength, IeLength );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
								
							// Octet 3
							Current++;
							BOOL IsCCITT = FALSE;
							ULONG Standard = (Payload[Current] & 0x60) >> 5;
							msg.Format( "Coding standard .xx.....: %d - ", Standard );
							switch ( Standard )
							{
								case 0: msg += "CCITT";	IsCCITT = TRUE;	break;
								case 1: msg += "International";				break;
								case 2: msg += "National";						break;
								case 3: msg += "Network";						break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG Capability = Payload[Current] & 0x1F;
							msg.Format( "Information transfer capability ...xxxxx: %d - ", Capability );
							switch ( Capability )
							{
								case 0x00:	msg += "Speech";						break;
								case 0x08:	msg += "Unrestricted digital";	break;
								case 0x09:	msg += "Restricted digital";		break;
								case 0x10:	msg += "3.1 kHz audio";				break;
								case 0x11:	msg += "7 kHz audio";				break;
								case 0x18:	msg += "Video";						break;
								default:		msg += "Reserved"; 					break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							// Octet 4
							Current++;
							ULONG Mode = (Payload[Current] & 0x60) >> 5;
							msg.Format( "Information transfer mode .xx.....: %d - ", Mode );
							switch ( Mode )
							{
								case 0:	msg += "circuit mode";	break;
								case 1:	msg += "packet mode";	break;
								default: msg += "Invalid";			break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG Rate = Payload[Current] & 0x1F;
							msg.Format( "Information transfer rate ...xxxxx: %d - ", Rate );
							switch ( Rate )
							{
								case 0x00:	msg += "N/A - packet mode";	break;
								case 0x10:	msg += "64 kbps";					break;
								case 0x11:	msg += "2 x 64 kbps";			break;
								case 0x13:	msg += "384 kbps";				break;
								case 0x15:	msg += "1536 kbps";				break;
								case 0x17:	msg += "1920 kbps";				break;
								default:		msg += "Invalid";					break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							if ( !(Payload[Current] & 0x80) && (IeLength > 2) )
							{
								// Octet 4a
								Current++;
								ULONG Structure = (Payload[Current] & 0x70) >> 4;
								msg.Format( "Structure .xxx....: %d - ", Structure );
								switch ( Structure )
								{
									case 0:	msg += Mode == 0 ? "8 kHz integrity" : "Service data unit integrity";	break;
									case 1:	msg += "8 kHz integrity";	break;
									case 4:	msg += "Service data unit integrity";	break;
									case 7:	msg += "Unstructured";	break;
									default:	msg += "";	break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								ULONG Configuration = (Payload[Current] & 0xC0) >> 2;
								msg.Format( "Configuration ....xx..: %d - ", Configuration );
								switch ( Configuration )
								{
									case 0:	msg += "Point to point";	break;
									default:	msg += "Invalid";				break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								ULONG Establishment = Payload[Current] & 0x03;
								msg.Format( "Establishment ......xx: %d - ", Establishment );
								switch ( Establishment )
								{
									case 0:	msg += "Demand";	break;
									default:	msg += "Invalid";	break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								if ( !(Payload[Current] & 0x80) && (IeLength > 3) )
								{
									// Octet 4b
									Current++;
									ULONG Symmetry = (Payload[Current] & 0x60) >> 5;
									msg.Format( "Symmetry .xx.....: %d - ", Symmetry );
									switch ( Symmetry )
									{
										case 0:	msg += "Bidirectional symmetric";	break;
										default:	msg += "Invalid";							break;
									}
									(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

									ULONG RxRate = Payload[Current] & 0x1F;
									msg.Format( "Receive information transfer rate ...xxxxx: %d - ", RxRate );
									switch ( RxRate )
									{
										case 0x00:	msg += "N/A - packet mode";	break;
										case 0x10:	msg += "64 kbps";					break;
										case 0x11:	msg += "2 x 64 kbps";			break;
										case 0x13:	msg += "384 kbps";				break;
										case 0x15:	msg += "1536 kbps";				break;
										case 0x17:	msg += "1920 kbps";				break;
										default:		msg += "Invalid";					break;
									}
									(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
								}
							}

							Current++;
							if ( IeLength > Current-Offset-2 && (Payload[Current] & 0x60) == 0x20 )
							{
								// Octet 5
								msg = "Layer 1 identifier .xx.....: 1";
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								ULONG Layer1 = Payload[Current] & 0x1F;
								msg.Format( "Layer 1 protocol ...xxxxx: %d - ", Layer1 );
								switch ( Layer1 )
								{
									case 0x01:	msg += "CCITT standard rate adaption V.110/X.30";	break;
									case 0x02:	msg += "Recommendation G.711 mu-law";	break;
									case 0x03:	msg += "Recommendation G.711 A-law";	break;
									case 0x04:	msg += "Recommendation G.721 32 kbps ADPCM and Recommendation I.460";	break;
									case 0x05:	msg += "Recommendations G.722 and G.725 7 kHz audio";	break;
									case 0x06:	msg += "Recommendation H.271 for 384 kbps video";	break;
									case 0x07:	msg += "Non-CCITT standardized rate adaption";	break;
									case 0x08:	msg += "CCITT standard rate adaption V.120";	break;
									case 0x09:	msg += "CCITT standard rate adaption X.31 HDLC flag stuffing";	break;
									default:		msg += "Invalid";	break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								if ( !(Payload[Current] & 0x80) && (IeLength > Current-Offset-1) )
								{
									// Octet 5a
									Current++;
									ULONG SyncAsync = (Payload[Current] & 0x40) >> 6;
									msg.Format( "Synchronous/asynchronous .x.....: %d - ", SyncAsync );
									switch ( SyncAsync )
									{
										case 0:	msg += "Synchronous";	break;
										case 1:	msg += "Asynchronous";	break;
									}
									(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

									ULONG Negotiation = (Payload[Current] & 0x20) >> 5;
									msg.Format( "Negotiation ..x....: %d - ", Negotiation );
									switch ( Negotiation )
									{
										case 0:	msg += "In-band negotiation not possible";	break;
										case 1:	msg += "In-band negotiation possible";	break;
									}
									(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

									ULONG UserRate = Payload[Current] & 0x1F;
									msg.Format( "User rate ...xxxxx: %d - ", UserRate );
									switch ( UserRate )
									{
										case 0x0000:	msg += "Rate indicated by E-bits specified in Recommendation I.460";	break;
										case 0x0001:	msg += "0.6 kbps Recommendations V.6 and X.1";	break;
										case 0x0002:	msg += "1.2 kbps Recommendation V.6";	break;
										case 0x0003:	msg += "2.4 kbps Recommendation V.6 and X.1";	break;
										case 0x0004:	msg += "3.6 kbps Recommendation V.6";	break;
										case 0x0005:	msg += "4.8 kbps Recommendation V.6 and X.1";	break;
										case 0x0006:	msg += "7.2 kbps Recommendation V.6";	break;
										case 0x0007:	msg += "8 kbps Recommendation I.460";	break;
										case 0x0008:	msg += "9.6 kbps Recommendation V.6 and X.1";	break;
										case 0x0009:	msg += "14.4 kbps Recommendation V.6";	break;
										case 0x000A:	msg += "16 kbps Recommendation I.460";	break;
										case 0x000B:	msg += "19.2 kbps Recommendation V.6";	break;
										case 0x000C:	msg += "32 kbps Recommendation I.460";	break;
										case 0x000E:	msg += "48 kbps Recommendation V.6 and X.1";	break;
										case 0x000F:	msg += "56 kbps Recommendation V.6";	break;
										case 0x1005:	msg += "0.1345 kbps Recommendation X.1";	break;
										case 0x1006:	msg += "0.100 kbps Recommendation X.1";	break;
										case 0x1007:	msg += "0.075(tx)/1.2(rx) kbps Recommendation V.6 and X.1";	break;
										case 0x1008:	msg += "1.2(tx)/0.075(rx) kbps Recommendation V.6 and X.1";	break;
										case 0x1009:	msg += "0.050 kbps Recommendation V.6 and X.1";	break;
										case 0x100A:	msg += "0.075 kbps Recommendation V.6 and X.1";	break;
										case 0x100B:	msg += "0.110 kbps Recommendation V.6 and X.1";	break;
										case 0x100C:	msg += "0.150 kbps Recommendation V.6 and X.1";	break;
										case 0x100D:	msg += "0.200 kbps Recommendation V.6 and X.1";	break;
										case 0x100E:	msg += "0.300 kbps Recommendation V.6 and X.1";	break;
										case 0x100F:	msg += "12 kbps Recommendation V.6";	break;
										default:	msg += "Invalid";	break;
									}
									(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

									if ( !(Payload[Current] & 0x80) && (IeLength > Current-Offset-1) )
									{
										// Octet 5b
										Current++;
										switch ( Layer1 )
										{
											case 0x01:
											{
												ULONG Intermediate = (Payload[Current] & 0x60) >> 5;
												msg.Format( "Intermediate rate .xx.....: %d - ", Intermediate );
												switch ( Intermediate )
												{
													case 0:	msg += "Invalid";	break;
													case 1:	msg += "8 kbps";	break;
													case 2:	msg += "16 kbps";	break;
													case 3:	msg += "32 kbps";	break;
												}
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG NicTx = (Payload[Current] & 0x10) >> 4;
												msg.Format( "Network independent clock on transmission ...x....: %d - %s", NicTx, NicTx ? "Required" : "Not required" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG NicRx = (Payload[Current] & 0x08) >> 3;
												msg.Format( "Network independent clock on reception ....x...: %d - %s", NicRx, NicRx ? "Required" : "Not required" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG FlowTx = (Payload[Current] & 0x04) >> 2;
												msg.Format( "Flow control on transmission .....x..: %d - %s", FlowTx, FlowTx ? "Required" : "Not required" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG FlowRx = (Payload[Current] & 0x02) >> 1;
												msg.Format( "Flow control on reception ......x.: %d - %s", FlowRx, FlowRx ? "Required" : "Not required" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
												break;
											}

											case 0x08:
											{
												ULONG Header = (Payload[Current] & 0x40) >> 6;
												msg.Format( "Rate adaption header .x......: %d - %s", Header, Header ? "Included" : "Not included" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG MultiFrame = (Payload[Current] & 0x20) >> 5;
												msg.Format( "Multiple frame establishment in data link ..x.....: %d - %s", MultiFrame, MultiFrame ? "Supported" : "Not supported" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG L1Mode = (Payload[Current] & 0x10) >> 4;
												msg.Format( "L1Mode ...x....: %d - %s", L1Mode, L1Mode ? "Protocol sensitive mode" : "Bit transparent mode" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG LLI = (Payload[Current] & 0x08) >> 3;
												msg.Format( "Logical link identifier negotiation ....x...: %d - %s", LLI, LLI ? "Full protocol negotiation" : "Default, LLI = 256 only" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG Assignor = (Payload[Current] & 0x04) >> 2;
												msg.Format( "Assignor .....x..: %d - %s", Assignor, Assignor ? "Message originator is Default Assignee" : "Message originator is Assignor Only" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG InBand = (Payload[Current] & 0x02) >> 1;
												msg.Format( "InBand ......x.: %d - %s", InBand, InBand ? "In-band" : "Out-band" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												break;
											}

											default:
												msg = "Invalid";
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
												break;
										}

										if ( !(Payload[Current] & 0x80) && (IeLength > Current-Offset-1) )
										{
											// Octet 5c
											Current++;

											ULONG StopBits = (Payload[Current] & 0x60) >> 5;
											msg.Format( "Stop bits .xx.....: %d - ", StopBits );
											switch ( StopBits )
											{
												case 0:	msg += "Invalid";		break;
												case 1:	msg += "1 bit";		break;
												case 2:	msg += "1.5 bits";	break;
												case 3:	msg += "2 bits";		break;
											}
											(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

											ULONG DataBits = (Payload[Current] & 0x18) >> 3;
											msg.Format( "Data bits ...xx...: %d - ", DataBits );
											switch ( DataBits )
											{
												case 0:	msg += "Invalid";		break;
												case 1:	msg += "5 bits";		break;
												case 2:	msg += "7 bits";	break;
												case 3:	msg += "8 bits";		break;
											}
											(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

											ULONG Parity = Payload[Current] & 0x7;
											msg.Format( "Parity .....xxx: %d - ", Parity );
											switch ( Parity )
											{
												case 0:	msg += "Odd";			break;
												case 2:	msg += "Even";			break;
												case 3:	msg += "None";			break;
												case 4:	msg += "Forced 0";	break;
												case 5:	msg += "Forced 1";	break;
												default:	msg += "Invalid";		break;
											}
											(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

											if ( !(Payload[Current] & 0x80) && (IeLength > Current-Offset-1) )
											{
												// Octet 5d
												Current++;

												ULONG Duplex = (Payload[Current] & 0x40) >> 6;
												msg.Format( "Duplex mode .x......: %d - %s", Duplex, Duplex ? "Full duplex" : "Half duplex" );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

												ULONG Modem = Payload[Current] & 0x3F;
												msg.Format( "Modem type ..xxxxxx: %d", Modem );
												(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
											}
										}
									}
								}
								Current++;
							}

							if ( IeLength > Current-Offset-2 && (Payload[Current] & 0x60) == 0x40 )
							{
								// Octet 6
								msg = "Layer 2 identifier .xx.....: 2";
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								ULONG Layer2 = Payload[Current] & 0x1F;
								msg.Format( "Layer 2 protocol ...xxxxx: %d - ", Layer2 );
								switch ( Layer2 )
								{
									case 2:	msg += "Q.921";				break;
									case 6:	msg += "X.25 link layer";	break;
									default:	msg += "Invalid";				break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
								Current++;
							}

							if ( IeLength > Current-Offset-2 && (Payload[Current] & 0x60) == 0x60 )
							{
								// Octet 7
								msg = "Layer 3 identifier .xx.....: 3";
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								ULONG Layer3 = Payload[Current] & 0x1F;
								msg.Format( "Layer 3 protocol ...xxxxx: %d - ", Layer3 );
								switch ( Layer3 )
								{
									case 2:	msg += "Q.931";					break;
									case 6:	msg += "X.25 packet layer";	break;
									default:	msg += "Invalid";					break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
								Current++;
							}
							break;
						}

						case 0x08:
						{
							// |  8  |  7  |  6  |  5  |  4  |  3  |  2  |  1  |
							// -------------------------------------------------
							// |  0  |  0     0     0     1     0     0     0  |
							// -------------------------------------------------
							// |                    Length                     |
							// -------------------------------------------------
							// | 0/1	| Standard  |  0  |        Location       |
							// -------------------------------------------------
							// |  1  |             Cause value                 |
							// -------------------------------------------------
							// |             Diagnostic(s), if any             |
							// -------------------------------------------------
							msg += "Cause";
							HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );

							ULONG Current = Offset + 1;
							msg.Format( "Length: %d (0x%x)", IeLength, IeLength );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
							Current++;
								
							ULONG Coding = (Payload[Current] & 0x60) >> 5;
							msg.Format( "Coding standard .xx.....: %d - ", Coding );
							BOOL IsCCITT = FALSE;
							switch ( Coding )
							{
								case 0:	msg += "CCITT";	IsCCITT = TRUE;	break;
								case 1:	msg += "International";	break;
								case 2:	msg += "National";	break;
								case 3:	msg += "Specific to location";	break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG Location = Payload[Current] & 0x0F;
							msg.Format( "Location ....xxxx: %d - ", Location );
							switch ( Location )
							{
								case 0x00:	msg += "User";													break;
								case 0x01:	msg += "Private network serving the local user";	break;
								case 0x02:	msg += "Public network serving the local user";		break;
								case 0x03:	msg += "Transit network";									break;
								case 0x04:	msg += "Public network serving the remote user";	break;
								case 0x05:	msg += "Private network serving the remote user";	break;
								case 0x07:	msg += "International network";							break;
								case 0x0A:	msg += "Network beyond interworking point";			break;
								default:		msg += "Invalid";												break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
							Current++;

							if ( !IsCCITT )
							{
								msg.Format( "Remaining octets: %d (0x%x)", TotalIeLength - Current, TotalIeLength - Current );
								(void)InsertTreeItem(tree, msg, item, Current, Offset+TotalIeLength );
							}
							else
							{
								ULONG CauseNumber = Payload[Current] & 0x7F;
								msg.Format( "Cause number: %d (0x%x) - ", CauseNumber, CauseNumber );
								msg += GetCauseName( CauseNumber );
								HTREEITEM parent= InsertTreeItem(tree, msg, item, Current, Offset+TotalIeLength );

								ULONG CauseClass = (CauseNumber & 0x70) >> 4;
								msg.Format( "Class .xxx....: %d - ", CauseClass );
								switch ( CauseClass )
								{
									case 0:	msg += "normal event"; 								break;
									case 1:	msg += "normal event"; 								break;
									case 2:	msg += "resource unavailable"; 				break;
									case 3:	msg += "service or option not available"; 		break;
									case 4:	msg += "service or option not implemented"; 	break;
									case 5:	msg += "invalid message"; 						break;
									case 6:	msg += "protocol error"; 							break;
									case 7:	msg += "interworking"; 								break;
								}
								(void)InsertTreeItem(tree, msg, parent, Current, Current+1 );

								msg.Format( "Value ....xxxx: %d - %s", CauseNumber & 0x0F, GetCauseName( CauseNumber ) );
								(void)InsertTreeItem(tree, msg, parent, Current, Current+1 );

								if ( IeLength > 2 )
								{
									// There's some form of diagnostic.  Display it.
									Current++;
									// TODO: Add decode for diagnostic fields
									msg.Format( "Diagnostic: length %d (0x%x)", IeLength - 2, IeLength - 2 );
									(void)InsertTreeItem(tree, msg, parent, Current, TotalIeLength );
								}
							}
							break;
						}

						case 0x10:
							DisplayCallIdentity( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;

						case 0x14:
							DisplayCallState( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;

						case 0x18: 
						{
							msg += "Channel identification";
							HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );
							
							ULONG Current = Offset + 1;
							msg.Format( "Length: %d (0x%x)", IeLength, IeLength );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							Current++;
							ULONG IdPresent = (Payload[Current] & 0x40) >> 6;
							msg.Format( "Interface Id Present .x......: %d - %s", IdPresent, IdPresent ? "Present" : "Implicit" );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG InterfaceType = (Payload[Current] & 0x20) >> 5;
							msg.Format( "Interface type ..x.....: %d - %s", InterfaceType, InterfaceType ? "Primary rate" : "Basic rate" );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
							ULONG Exclusive = (Payload[Current] & 0x08) >> 3;
							msg.Format( "Preferred/exclusive ....x...: %d - %s", Exclusive, Exclusive ? "Exclusive - indicated channel is required" : "Preferred - Indicated channel is preferred" );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG DChan = (Payload[Current] & 0x04) >> 2;
							msg.Format( "D-channel indicator .....x..: %d - %s", DChan, DChan ? "D-channel" : "Not the D-channel" );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG InfoChan = Payload[Current] & 0x03;
								msg.Format( "Information channel selection ......xx: %d", InfoChan );	// In BRI, this is the channel number
							if ( InterfaceType == 1 )	// In PRI, channel number is decoded below
							{
								switch ( InfoChan )
								{
									case 0:	msg += " = No channel";		break;
									case 1:	msg += " = B1 channel";		break;
									case 2:	msg += " = B2 channel";		break;
									case 3:	msg += " = Any channel";	break;
								}
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							if ( IdPresent && IeLength > Current-Offset-1 )
							{
								// Octet 3.1 (optional)
								Current++;
								ULONG InterfaceId = Payload[Current] & 0x7F;
								msg.Format( "Interface identifier .xxxxxxx: %d (0x%x)", InterfaceId, InterfaceId );
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
							}

							if ( InterfaceType == 1 && IeLength > Current-Offset-1 )
							{
								// Octet 3.2 (optional)
								Current++;
								ULONG Coding = (Payload[Current] & 0x60) >> 5;
								msg.Format( "Coding standard .xx.....: %d - ", Coding );
								switch ( Coding )
								{
									case 0:	msg += "CCITT";			break;
									case 1:	msg += "International";	break;
									case 2:	msg += "National";		break;
									case 3:	msg += "Network";			break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								ULONG Map = (Payload[Current] & 0x10) >> 4;
								msg.Format( "Channel indication by number or map ...x....: %d - %s", Map, Map ? "Map" : "Number" );
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								ULONG Number = Payload[Current] & 0x0F;
								msg.Format( "%s ....xxxx: %d - ", Map ? "Channel type" : "Map element type", Number );
								switch ( Number )
								{
									case 3:	msg += "B-channel";		break;
									case 6:	msg += "H0-channel";		break;
									case 8:	msg += "H11-channel";	break;
									case 9:	msg += "H12-channel";	break;
									default:	msg += "Invalid";			break;
								}
								(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

								while ( IeLength > Current-Offset-1 )
								{
									// Octet 3.3 (optional, possibly multiple)
									Current++;
									if ( Map )
									{
										ULONG SlotMap = Payload[Current];
										msg.Format( "Slot map: %d (0x%x)", SlotMap, SlotMap );
									}
									else
									{
										ULONG ChannelNumber = Payload[Current] & 0x7F;
										msg.Format( "Channel number .xxxxxxx: %d (0x%x)", ChannelNumber, ChannelNumber );
									}
									(void)InsertTreeItem(tree, msg, item, Current, Current+1 );
								}
							}

							break;
						}
						
						case 0x1e: 
							DisplayProgressIndicator( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;

						case 0x34:
							DisplaySignal( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;

						case 0x6c:
							DisplayCallingPartyNumber( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;

						case 0x70:
							DisplayCalledPartyNumber( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;

						case 0x79: 
							DisplayRestartIndicator( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;

						case 0x7D:
						{
							msg += "High layer compatibility";
							HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+TotalIeLength );
							
							ULONG Current = Offset+1;
							msg.Format( "Length: %d (0x%x)", IeLength, IeLength );
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							Current++;
							ULONG Coding = (Payload[Current] & 0x60) >> 5;
							msg.Format( "Coding standard .xx.....: %d - ", Coding );
							switch ( Coding )
							{
								case 0:	msg += "CCITT";	break;
								case 1:	msg += "International";	break;
								case 2:	msg += "National";	break;
								case 3:	msg += "Network";	break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG Interpretation = (Payload[Current] & 0x1C) >> 2;
							msg.Format( "Interpretation ...xxx..: %d - ", Interpretation );
							switch ( Interpretation )
							{
								case 4:	msg += "First HLC identification to be used";	break;
								default:	msg += "Invalid";	break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							ULONG Method = Payload[Current] & 0x03;
							msg.Format( "Presentation method of protocol profile ......xx: %d - ", Method );
							switch ( Method )
							{
								case 1:	msg += "High layer protocol profile";	break;
								default:	msg += "Invalid";	break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							Current++;
							ULONG HlcId = Payload[Current] & 0x7F;
							msg.Format( "High layer characteristics identification .xxxxxxx: %d - ", HlcId );
							switch ( HlcId )
							{
								case 0x0001:	msg += "Telephony (G.711)";	break;
								case 0x0004:	msg += "Group 4 FAX";	break;
								case 0x0021:	msg += "Document Application Profile for Group 4 Class 1 FAX";	break;
								case 0x0024:	msg += "Document Application Profile for formatted Mixed-Mode";	break;
								case 0x0028:	msg += "Document Application Profile for Processable-form";	break;
								case 0x0031:	msg += "Teletex";	break;
								case 0x0032:	msg += "Document Application Profile for Videotex interworking between Gateways";	break;
								case 0x0035:	msg += "Telex";	break;
								case 0x0038:	msg += "Message Handling Systems (MHS)";	break;
								case 0x0041:	msg += "OSI application";	break;
								case 0x005E:	msg += "Maintenance";	break;
								case 0x005F:	msg += "Management";	break;
								case 0x007F:	msg += "Reserved";	break;
								default:	msg += "Invalid";	break;
							}
							(void)InsertTreeItem(tree, msg, item, Current, Current+1 );

							if ( (Payload[Current] & 0x80) == 0 && IeLength > Current-Offset-1 )
							{
								// Octet 4a (optional)
								Current++;
								ULONG ExtId = Payload[Current] & 0x7F;
								msg.Format( "Extended high layer characteristics identification .xxxxxxx: %d - ", ExtId );
								switch ( ExtId )
								{
									case 0x0001:	msg += "Telephony (G.711)";	break;
									case 0x0004:	msg += "Group 4 FAX";	break;
									case 0x0021:	msg += "Document Application Profile for Group 4 Class 1 FAX";	break;
									case 0x0024:	msg += "Document Application Profile for formatted Mixed-Mode";	break;
									case 0x0028:	msg += "Document Application Profile for Processable-form";	break;
									case 0x0031:	msg += "Teletex";	break;
									case 0x0032:	msg += "Document Application Profile for Videotex interworking between Gateways";	break;
									case 0x0035:	msg += "Telex";	break;
									case 0x0038:	msg += "Message Handling Systems (MHS)";	break;
									case 0x0041:	msg += "OSI application";	break;
									case 0x005E:	msg += "Not available";	break;
									case 0x005F:	msg += "Not available";	break;
									case 0x007F:	msg += "Reserved";	break;
									default:	msg += "Invalid";	break;
								}
								(void)InsertTreeItem(tree, msg, item, Offset+3, Offset+TotalIeLength );
							}

							break;
						}


					}
					break;

				case 5:
					switch ( Id )
					{
						case 0x2A: 
							DisplayCodeset5DisplayText( tree, hParent, Payload, Offset, IeLength, TotalIeLength, msg );
							break;
						default:	
							msg += "Unknown";
							goto DisplayDefault;
					}
					break;

				case 6:
					switch ( Id )
					{
						case 0x01: msg += "Service indication";			goto DisplayDefault;	// ???
						case 0x02: msg += "Charging information";			goto DisplayDefault;	// ???
						case 0x03: msg += "Date";								goto DisplayDefault;	// ???
						case 0x05: msg += "Facility selection";			goto DisplayDefault;	// ???
						case 0x06: msg += "Status facility";				goto DisplayDefault;	// ???
						case 0x07: msg += "Status called user";			goto DisplayDefault;	// ???
						case 0x08: msg += "Add transmit attribute";		goto DisplayDefault;	// ???
						case 0x24: msg += "Terminal capability";			goto DisplayDefault;	// ???
						case 0x7a: msg += "User-user facility";			goto DisplayDefault;	// ???
						default:   msg += "Unknown";							goto DisplayDefault;	// ???
					}
					break;
			}

			goto SkipDefault;

DisplayDefault:
			{
				HTREEITEM item = InsertTreeItem(tree, msg, hParent, Offset, Offset+2+IeLength );

				msg.Format( "Length: %d (0x%x)", IeLength, IeLength );
				(void)InsertTreeItem(tree, msg, item, Offset+1, Offset+2 );

				msg.Format( "Remaining bytes: %d (0x%x)", IeLength, IeLength );
				(void)InsertTreeItem(tree, msg, item, Offset+2, Offset+2+IeLength );
			}

SkipDefault:
			Offset += 2 + IeLength;
			cb     -= 2 + IeLength;
			goto PostProcessing;
		}

PostProcessing:
		if ( fPreviousCodeset )
		{
			Codeset = PreviousCodeset;
			fPreviousCodeset = FALSE;
		}
	}
}


// Ioctl sent down to start\stop trace
class CIsdnTraceSwitch : public CStat<lapd_trioc_t>
{
public :
	CIsdnTraceSwitch(DWORD Snid, eSwitch Pos = sOn) : 
		CStat<lapd_trioc_t>(Pos == sOff ? LAPD_TRACEOFF : LAPD_TRACEON)
		{ l_type = LAPD_TRACE;l_level = ISDN_LAPD_DAT;l_snid = Snid; }
};



CIsdnTracer::CIsdnTracer(CDataSource *ds) : CTracer(ds, stLAPD)
{
// if no failure then turn trace on
	if (!Fail())
	{
		m_SwitchOn = new CIsdnTraceSwitch(m_Snid);

		ThrowSwitch(m_SwitchOn, sOn);
	}
}


CIsdnTracer::~CIsdnTracer()
{
// turn trace off
	m_SwitchOff = new CIsdnTraceSwitch(m_Snid, sOff);

	ThrowSwitch(m_SwitchOff, sOff);
}


/////////////////////////////////////////////////////////////////////////////
// CIsdnFilter dialog

CIsdnFilter::CIsdnFilter(CWnd* pParent /*=NULL*/)
	: CDialog(CIsdnFilter::IDD, pParent)
{
	//{{AFX_DATA_INIT(CIsdnFilter)
	m_FilterRadio = -1;
	//}}AFX_DATA_INIT
}

void CIsdnFilter::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CIsdnFilter)
	DDX_Radio(pDX, IDC_ISDN_DISPLAY_ALL, m_FilterRadio);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CIsdnFilter, CDialog)
	//{{AFX_MSG_MAP(CIsdnFilter)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CIsdnFilter message handlers

BOOL CIsdnFilter::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// TODO: Add extra initialization here
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}

void CIsdnFilter::OnOK() 
{
	// TODO: Add extra validation here
		CDialog::OnOK();
}
