/*++
*******************************************************************************
* 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:

   dxbbuild.c

Abstract:

   Master protocol build routines.  Used to build up protocol stacks on the adapter.

Author:

   Michael Zarns

--*/
#include "digidxb.h"
#pragma hdrstop

#include "isdn_proto.h"
#include "il3_config.h"
#include "iwan_control.h"

// TODO: Get Firmware folks to define these!
#define ISDN_CONS	1
#define ISDN_CLNS	2


STATIC NTSTATUS
dxbBuildOpenEnumeratedKey( 
	OUT PHANDLE phKey,
	IN HANDLE   hKeyParent,
	IN PCWSTR   pRoot,
	IN UCHAR    iEnumStart,
	IN UCHAR    iPort )
/*++

Description:

	Open the numbered subkey (zero-based).  Port/Channel key names may not actually 
	use a numeric index, i.e. format may be PortA, PortB, etc.  This routine hides the 
	details and opens the appropriate key for the numbered port.

Parameters:

	phKey
		Handle to newly opened subkey is written here if successfully opened.

	hKeyParent
		Handle to opened registry key containing the enumerated keys

	pRoot
		String defining the Root of the subkey name, i.e. "Port", for PortA

	iPort
		Zero-based enum number whose key should be opened.

	iEnumStart
		Defines the starting Enum in the subkey name, i.e. 'A' in PortA

Return Value:

	STATUS_SUCCESS or failure status.

--*/
{
	NTSTATUS Status;
	UNICODE_STRING KeyName = {0};

   // Figure out the next subkey
   if ((iEnumStart >= '0') && (iEnumStart <= '9') )
   {
      // base ten integer, allow multiple digits
      UNICODE_STRING TempKey;
      // Convert the starting enum to an integer, then add the port number
      ULONG tmp = iEnumStart - '0' + iPort;
      // Convert the result back to a string
      RtlInitUnicodeString(&TempKey, TEXT("PlaceHolder"));
      RtlIntegerToUnicodeString( tmp, 10, &TempKey );
      TempKey.Buffer[TempKey.Length-1] = 0;	// NULL Terminate
      // Now alloc the persistent one with our lib call
      Status = DigiStrAllocUnicodeString( &KeyName, pRoot, TempKey.Buffer, NULL );
   }
   else
   {
      // alpha, allow a single char
      USHORT Enum[2] = {(USHORT)(iPort+iEnumStart), 0};
      Status = DigiStrAllocUnicodeString( &KeyName, pRoot, Enum, NULL );
   }

	if ( NT_SUCCESS( Status ) )
	{
		HANDLE hKey;
		Status = DigiRegOpenSubkey( &hKey, hKeyParent, KeyName.Buffer );
		if ( NT_SUCCESS( Status ) )
			*phKey = hKey;
	}

	if ( KeyName.Buffer != NULL )
		DigiStrFreeUnicodeString( &KeyName );

	return Status;
}


NTSTATUS
dxbBuildOpenLineKey( 
	OUT PHANDLE phKey,
	IN HANDLE   hKeyParent,
	IN UCHAR    iPort )
{
	return dxbBuildOpenEnumeratedKey( phKey, hKeyParent, DXB_REGKEY_LINE_BASE, DXB_REGKEY_LINE_ENUM_FIRST, iPort );
}


NTSTATUS
dxbBuildOpenBriDirectoryKey( 
	OUT PHANDLE phKey,
	IN HANDLE   hKeyParent,
	IN UCHAR    iPort )
{
	return dxbBuildOpenEnumeratedKey( phKey, hKeyParent, DXB_REGKEY_BRI_DIRECTORY_BASE, DXB_REGKEY_BRI_DIRECTORY_ENUM_FIRST, iPort );
}


#if 0
STATIC NTSTATUS
ReadSnidValue( 
	IN HANDLE  hKeyPort,
	OUT PULONG pSnid )
{
	// Get the SubNetwork Id (or Snid).
	NTSTATUS Status;
	UNICODE_STRING SnidString;
	Status = DigiRegQuerySz( hKeyPort, DXB_REGVAL_LINE_SNID, &SnidString, DigiRegFlagRequired, NULL );
	if ( NT_ERROR( Status ) )
		goto Exit;

	if ( SnidString.Length > sizeof(ULONG) * sizeof(WCHAR) )
	{
		DigiDump( DIGIERROR, ( "Dxb!ReadSnidValue: SnidString too long, length %d, snid: %S\n", SnidString.Length, SnidString.Buffer ) );
		Status = STATUS_UNSUCCESSFUL;
	}
	else
	{
		ULONG i;
		ULONG Snid = 0;
		for ( i = 0; i < SnidString.Length / sizeof(WCHAR); i++ )
		{
			Snid = Snid << 8 | SnidString.Buffer[i] & 0xFF;
		}
		*pSnid = Snid;
	}

	DigiRegFreeString( &SnidString );

Exit:
	return Status;
}
#endif


STATIC NTSTATUS
dxbLinkDevice(
	IN HANDLE  hStream,
	IN HANDLE  hLower, 
	OUT PULONG pMuxId )
/*++

Description:

	Link the specified lower device under this stream.

Parameters:

	pExt
		Device extension defining the adapter

	hStream
		Handle to previously opened stream - "upper"

	hLower
		Handle to previously opened stream - "lower"

	pMuxId
		Output ID of the linked pair if successful.

Return Value:

	STATUS_SUCCESS, or failure status.

--*/
{
	NTSTATUS Status;
	ULONG MuxId, Error;

	Status = DsLink( hStream, hLower, &MuxId, &Error );
	if ( NT_ERROR( Status ) || Error )
	{
		DigiDump( DIGIERROR, ( "Dxb!LinkDevice failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		goto Exit;
	}

	*pMuxId = MuxId;

Exit:

	return Status;
}


NTSTATUS
dxbBuildFrameOverWan(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN HANDLE                hKeyPort,
	IN UCHAR                 iPort )
/*++

Description:

	Build up Frame Relay protocol using WAN lower edge.  This stack is used solely on 
	non-CSU style adapters, i.e. SyncPort.

Parameters:

	pExt
		Device Extension for the adapter.

	hKeyPort
		Hande to previously opened registry key containing config params for this port.

	iPort
		Zero-based index defining the port number on the adapter.

Return Value:

	STATUS_SUCCESS, or failure status.

--*/
{
	NTSTATUS Status;
	HANDLE hFr;
	ULONG MuxId;
	ULONG Snid;
	ULONG Error;

	DigiDump( DIGIINITLOUD, ( "DXB: Enter dxbBuildFrameOverWan, port %d\n", iPort ) );

	hFr = pExt->ControlStreams.Fr;
	if ( hFr == NULL && NT_ERROR( Status = DsOpen( pExt->hDsDevice, "FR", NULL, NULL, 0, &hFr, &Error ) ) )
		goto Exit;

	// Link Frame on WANx
	{
		CHAR szWan[] = "WAN0";
		ANSI_STRING WanDevice;
		HANDLE hWan = NULL;
		// Cheating a little here.  Build the devicename, WAN0 or WAN1, etc.  Start 
		// with a default string "WAN0" and modify the index char, '0', if needed.
		RtlInitAnsiString( &WanDevice, szWan );
		ASSERT( iPort < 10 );	// 1 digit max!
		WanDevice.Buffer[WanDevice.Length-1] = (UCHAR)iPort + '0';
		Status =  DsOpen( pExt->hDsDevice, WanDevice.Buffer, NULL, NULL, 0, &hWan, &Error );

		if ( NT_SUCCESS( Status ) )
			Status = dxbLinkDevice( hFr, hWan, &MuxId );

		if ( hWan )
			DsClose( hWan );
	}

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyPort, DXB_REGVAL_LINE_SNID, &Snid, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = dxbFrSetSnid( pExt, MuxId, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = dxbWanTune( pExt, hKeyPort, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = dxbFrTune( pExt, hKeyPort, (USHORT)iPort, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = dxbFrSnIdent( pExt, Snid, (USHORT)iPort );

	if ( NT_SUCCESS( Status ) )
		pExt->ControlStreams.Fr = hFr;

Exit:
	if ( NT_ERROR( Status ) && hFr != NULL && pExt->ControlStreams.Fr != hFr )
		(void)DsClose( hFr );

	DigiDump( DIGIINITLOUD, ( "DXB: Leave dxbBuildFrameOverWan, Status 0x%x\n", Status ) );
	return Status;
}


NTSTATUS
dxbBuildFrameOverIwan(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN HANDLE                hKeyPort,
	IN UCHAR                 iPort )
/*++

Description:

	Build up Frame Relay protocol using Iwan lower edge.  This stack is used solely on 
	CSU style adapters, i.e. Datafire PRIme.

Parameters:

	pExt
		Device Extension for the adapter.

	hKeyPort
		Hande to previously opened registry key containing config params for this port.

	iPort
		Zero-based index defining the port number on the adapter.

Return Value:

	STATUS_SUCCESS, or failure status.

--*/
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	HANDLE hFr = NULL;
	HANDLE hIwan;
	ULONG MuxId;
	ULONG Snid;
	ULONG Error;

	DigiDump( DIGIINITLOUD, ( "DXB: Enter dxbBuildFrameOverIwan, port %d\n", iPort ) );

	Status = DigiRegQueryDword( hKeyPort, DXB_REGVAL_LINE_SNID, &Snid, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DsOpen( pExt->hDsDevice, "IWAN", NULL, NULL, 0, &hIwan, &Error );

	// TODO: Get firmware to add this to iwan_control.h!
	#define IWAN_CLASS_FRAME_RELAY 2

	if ( NT_SUCCESS( Status ) )
		Status = dxbIwanSetSnid( hIwan, Snid, IWAN_CLASS_FRAME_RELAY, 1 );

	if ( NT_SUCCESS( Status ) )
	{
		hFr = pExt->ControlStreams.Fr;
		if ( hFr == NULL )
			Status = DsOpen( pExt->hDsDevice, "FR", NULL, NULL, 0, &hFr, &Error );
	}

	if ( NT_SUCCESS( Status ) )
		Status = dxbLinkDevice( hFr, hIwan, &MuxId );

	if ( hIwan )
		DsClose( hIwan );

	if ( NT_SUCCESS( Status ) )
		Status = dxbFrSetSnid( pExt, MuxId, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = dxbFrTune( pExt, hKeyPort, (USHORT)iPort, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = dxbFrSnIdent( pExt, Snid, (USHORT)iPort );

#pragma message( "FrameRelay over Iwan hardcoded to T1, no E1!" )
	if ( NT_SUCCESS( Status ) )
		Status = dxbT1Tune( pExt, hKeyPort, iPort, 23 );

	if ( NT_SUCCESS( Status ) )
		pExt->ControlStreams.Fr = hFr;
	else if ( hFr != NULL && pExt->ControlStreams.Fr != hFr )
		(void)DsClose( hFr );


	DigiDump( DIGIINITLOUD, ( "DXB: Leave dxbBuildFrameOverIwan, Status 0x%x\n", Status ) );
	return Status;
}


STATIC NTSTATUS
dxbBuildIl3OverIwan( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyPort,
	IN UCHAR                 iPort,
	IN ULONG                 Snid,
	IN ULONG                 nBChannels,
	IN ULONG                 iDChannel )
{
	NTSTATUS Status = STATUS_SUCCESS;
	HANDLE hIl3 = pExt->ControlStreams.Isdn;
	ULONG Error;
	ULONG iChannel, iTimeSlot;

	for ( iChannel = iTimeSlot = 1; iChannel <= nBChannels && NT_SUCCESS(Status); iTimeSlot++, iChannel++ )
	{
		HANDLE hIwan = NULL;
		ULONG MuxId;

		if ( iChannel == iDChannel )
			iTimeSlot++;	// Skip over the DChannel timeslot

		Status = DsOpen( pExt->hDsDevice, "IWAN", NULL, NULL, 0, &hIwan, &Error );

		if ( NT_SUCCESS( Status ) )
			Status = dxbIwanSetSnid( hIwan, Snid, ISDN_BCHAN, iTimeSlot );

		if ( NT_SUCCESS( Status ) )
			Status = dxbLinkDevice( hIl3, hIwan, &MuxId );

		if ( NT_SUCCESS( Status ) )
			Status = dxbPriIl3SetSnid( hIl3, iPort, Snid, ISDN_B_CHAN, iChannel, MuxId );

		if ( hIwan )
			DsClose( hIwan );
	}

	return Status;
}


STATIC NTSTATUS
dxbBuildLapdOverIwan( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyPort,
	IN UCHAR                 iPort,
	IN ULONG                 Snid,
	IN ULONG                 nBChannels,
	IN ULONG                 iDChannel,
	OUT PHANDLE              phLapd )
{
	NTSTATUS Status = STATUS_SUCCESS;
	HANDLE hIwan = NULL;
	HANDLE hLapd = NULL;
	ULONG Error;
	ULONG MuxId;

	if ( NT_SUCCESS( Status ) )
		Status = DsOpen( pExt->hDsDevice, "IWAN", NULL, NULL, 0, &hIwan, &Error );

	if ( NT_SUCCESS( Status ) )
		Status = dxbIwanSetSnid( hIwan, Snid, ISDN_DCHAN, iDChannel );

	if ( NT_SUCCESS( Status ) )
		Status = DsOpen( pExt->hDsDevice, "LAPD", NULL, NULL, 0, &hLapd, &Error );

	if ( NT_SUCCESS( Status ) )
		Status = dxbLinkDevice( hLapd, hIwan, &MuxId );

	if ( hIwan )
		DsClose( hIwan );
		
	if ( NT_SUCCESS( Status ) )
		Status = dxbPriLapdSetSnid( hLapd, hKeyPort, Snid, MuxId );

	if ( NT_SUCCESS( Status ) )
		Status = dxbPriLapdTune( hLapd, hKeyPort, iPort, Snid );

	if ( NT_SUCCESS( Status ) )
		*phLapd = hLapd;

	return Status;
}


STATIC NTSTATUS
dxbBuildIl3OverLapd( 
	IN PDXB_DEVICE_EXTENSION pExt,
	IN HANDLE                hKeyPort,
	IN HANDLE                hIl3,
	IN HANDLE                hLapd,
	IN UCHAR                 iPort,
	IN ULONG                 Snid )
{
	NTSTATUS Status;
	HANDLE hLapd2 = NULL;
	ULONG Error;
	ULONG MuxId;

	Status = dxbLinkDevice( hIl3, hLapd, &MuxId );
	DsClose( hLapd );

	if ( NT_SUCCESS( Status ) )
		Status = dxbPriIl3Tune( hIl3, hKeyPort, Snid, MuxId );

	if ( NT_SUCCESS( Status ) )
		Status = dxbPriIl3SetSnid( hIl3, iPort, Snid, ISDN_D_CHAN, ISDN_CLNS, MuxId );

	if ( NT_SUCCESS( Status ) )
		Status = DsOpen( pExt->hDsDevice, "LAPD", NULL, NULL, 0, &hLapd2, &Error );

	if ( NT_SUCCESS( Status ) )
		Status = dxbLinkDevice( hIl3, hLapd2, &MuxId );

	if ( hLapd2 )
		DsClose( hLapd2 );
		
	if ( NT_SUCCESS( Status ) )
		Status = dxbPriIl3SetSnid( hIl3, iPort, Snid, ISDN_D_CHAN, ISDN_CONS, MuxId );

	return Status;
}


NTSTATUS
dxbBuildPri(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN HANDLE                hKeyPort,
	IN UCHAR                 iPort )
/*++

Description:

	Build up PRI protocol using Iwan lower edge.  This stack is used solely on 
	CSU style adapters, i.e. Datafire PRIme.

Parameters:

	pExt
		Device Extension for the adapter.

	hKeyPort
		Hande to previously opened registry key containing config params for this port.

	iPort
		Zero-based index defining the port number on the adapter.

Return Value:

	STATUS_SUCCESS, or failure status.

--*/
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	HANDLE hIl3 = pExt->ControlStreams.Isdn;
	HANDLE hLapd = NULL;
	ULONG nBChannels;
	ULONG iDChannel;
	ULONG Snid;
	ULONG Error;

	DigiDump( DIGIINITLOUD, ( "DXB: Enter dxbBuildPri, port %d\n", iPort ) );

	// Try to read number of b-channels from registry.  If successful, setup d-channel
	// index from it.  This will be the case for NT5.
	Status = DigiRegQueryDword( hKeyPort, DXB_REGVAL_IL3_NUMBEROFBCHANNELS, &nBChannels, DigiRegFlagRequired, 0 );
	if ( NT_SUCCESS( Status ) )
	{
		switch ( nBChannels )
		{
			case 30:	iDChannel = 17;					break;
			case 23: iDChannel = 24;					break;
			default: Status = STATUS_UNSUCCESSFUL;	break;
		}
	}
	
	// Otherwise, decide based on PCI ID.  This is used in NT4.
	if ( NT_ERROR( Status ) )
	{
		switch ( pExt->Hardware.PciId )
		{
			case DIGI_PCI_DEVICEID_PRIME_E1_SINGLE:
				nBChannels = 30;
				iDChannel  = 17;
				Status = STATUS_SUCCESS;
				break;

			case DIGI_PCI_DEVICEID_PRIME_T1_SINGLE:
				nBChannels = 23;
				iDChannel  = 24;
				Status = STATUS_SUCCESS;
				break;

			default:
				Status = STATUS_UNSUCCESSFUL;
				break;
		}
	}

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQueryDword( hKeyPort, DXB_REGVAL_LINE_SNID, &Snid, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) && hIl3 == NULL )
		Status = DsOpen( pExt->hDsDevice, "IL3", NULL, NULL, 0, &hIl3, &Error );

	if ( NT_SUCCESS( Status ) )
		pExt->ControlStreams.Isdn = hIl3;

	if ( NT_SUCCESS( Status ) )
		Status = dxbBuildIl3OverIwan( pExt, hKeyPort, iPort, Snid, nBChannels, iDChannel );

	if ( NT_SUCCESS( Status ) )
		Status = dxbBuildLapdOverIwan( pExt, hKeyPort, iPort, Snid, nBChannels, iDChannel, &hLapd );

	if ( NT_SUCCESS( Status ) )
		Status = dxbBuildIl3OverLapd( pExt, hKeyPort, hIl3, hLapd, iPort, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = dxbT1Tune( pExt, hKeyPort, iPort, nBChannels );

	// Stash the control stream handle so we can close it at adapter shutdown time.
	// Note: hLapd is closed by buildIl3OverLapd - don't try to close it again.
	pExt->ControlStreams.Isdn = hIl3;

	DigiDump( DIGIINITLOUD, ( "DXB: Leave dxbBuildPri, Status 0x%x\n", Status ) );
	return Status;
}


NTSTATUS
dxbBuildX25OverWan(
	IN PDXB_DEVICE_EXTENSION pExt, 
	IN HANDLE                hKeyPort,
	IN UCHAR                 iPort )
/*++

Description:

	Build up Lapb protocol using WAN lower edge.  This stack is used solely on 
	non-CSU style adapters, i.e. SyncPort or Clydesdale.

Parameters:

	pExt
		Device Extension for the adapter.

	hKeyPort
		Hande to previously opened registry key containing config params for this port.

	iPort
		Zero-based index defining the port number on the adapter.

Return Value:

	STATUS_SUCCESS, or failure status.

--*/
{
	NTSTATUS Status;
	HANDLE hLapb = NULL;
	HANDLE hX25 = NULL;
	ULONG Snid;
	ULONG Error;

	DigiDump( DIGIINITLOUD, ( "DXB: Enter dxbBuildX25OverWan, port %d\n", iPort ) );

	Status = DigiRegQueryDword( hKeyPort, DXB_REGVAL_LINE_SNID, &Snid, DigiRegFlagRequired, 0 );

	if ( NT_SUCCESS( Status ) )
		Status = DsOpen( pExt->hDsDevice, "LAPB", NULL, NULL, 0, &hLapb, &Error );

	// Link Lapb on WANx
	if ( NT_SUCCESS( Status ) )
	{
		CHAR szWan[] = "WAN0";
		ANSI_STRING WanDevice;
		HANDLE hWan = NULL;
		// Cheating a little here.  Build the devicename, WAN0 or WAN1, etc.  Start 
		// with a default string "WAN0" and modify the index char, '0', if needed.
		RtlInitAnsiString( &WanDevice, szWan );
		ASSERT( iPort < 10 );	// 1 digit max!
		WanDevice.Buffer[WanDevice.Length-1] = iPort + '0';

		Status =  DsOpen( pExt->hDsDevice, WanDevice.Buffer, NULL, NULL, 0, &hWan, &Error );
		if ( NT_SUCCESS( Status ) )
		{
			ULONG MuxId;
			Status = dxbLinkDevice( hLapb, hWan, &MuxId );

			if ( NT_SUCCESS( Status ) )
				Status = dxbLapbSetSnid( hLapb, MuxId, Snid );
			DsClose( hWan );
		}
	}

	if ( NT_SUCCESS( Status ) )
		Status = dxbWanTune( pExt, hKeyPort, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = dxbLapbTune( pExt, hKeyPort, iPort, Snid );

	// Open X25, link it on Lapb
	if ( NT_SUCCESS( Status ) )
	{
		if ( pExt->ControlStreams.X25 )
			hX25 = pExt->ControlStreams.X25;
		else
			Status = DsOpen( pExt->hDsDevice, "X250", NULL, NULL, 0, &hX25, &Error );
	}

	if ( NT_SUCCESS( Status ) )
		Status = dxbX25Tune( pExt, hKeyPort, iPort, Snid, hX25 );

	if ( NT_SUCCESS( Status ) )
	{
		ULONG MuxId;
		Status = dxbLinkDevice( hX25, hLapb, &MuxId );

		if ( NT_SUCCESS( Status ) )
			Status = dxbX25SnIdent( pExt, hKeyPort, MuxId, Snid, hX25 );
	}

	if ( NT_SUCCESS( Status ) )
		pExt->ControlStreams.X25 = hX25;

	if ( hLapb )
		DsClose( hLapb );

	if ( NT_ERROR( Status ) && hX25 != NULL && pExt->ControlStreams.X25 != hX25 )
		DsClose( hX25 );

	DigiDump( DIGIINITLOUD, ( "DXB: Leave dxbBuildX25OverWan, Status 0x%x\n", Status ) );
	return Status;
}


