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

   sohbuild.c

Abstract:

   This module contains functions to enumurate the registry and build
   the Streams stacks.

Author:

   Jeff Romatoski

Revision History:


--*/


////////////////////////////////////////////////////////////////////////////////


#include "digisoh.h"
#include "sohutil.h"
#include "sohbuild.h"
#include "sohfr.h"
#include "sohwan.h"
#include "sohlapb.h"
#include "sohx25.h"

#if !NT5_BUILD
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DigiSohCreateControllingStreams)
#pragma alloc_text(INIT,DigiSohEnumMiniportInstance)
#pragma alloc_text(INIT,DigiSohBuildMiniportInstance)
#pragma alloc_text(INIT,SohBuildX25OverXWan)
#pragma alloc_text(INIT,SohBuildFrameOverXWan)
#endif
#endif


// Keeps track of the relative port number
USHORT iPort = 0;

#if NT5_BUILD

NTSTATUS
DigiSohCreateControllingStreams(
	IN PDEVICE_OBJECT	pDevObj,
	IN PSOH_DEV_EXT		pDevExt,
	IN ULONG			ulPort,
	IN HANDLE			hkLine
	)

/*++

   Routine Description:

   This routine builds the streams stacks based on the configured miniport
   adapters.  

   Arguments:

   pDevObj			- Pointer to this devices object.
   pDevExt			- Pointer to this devices extension.
   pRegistryPath	- Pointer to the registry path (DigiSoh).

   Return Value:	- NT_STATUS


   -- */
{
	NTSTATUS		Status;
	BOOLEAN			bProcessControlStreams = FALSE;
	BOOLEAN			bWaitProcessControlStreams = FALSE;
	KIRQL			Kirql;

	DigiDump( DIGIFLOW, ( "Entering DigiSohCreateControllingStreams\n" ) );

	// In W2K multiple adapters can call this routine.  Only one adpater instance
	// can be opening or closing the controlling streams.  This requirement goes
	// away if the firmware is every modified to support multiple controlling streams.
	//
	do 
	{
		KeAcquireSpinLock( &gSohRuntimeEnv.HandleSpinLock, &Kirql );
		if (gSohRuntimeEnv.bProcessingControlStreams)
		{
			gSohRuntimeEnv.bWaitProcessingControlStreams = TRUE;
			KeReleaseSpinLock( &gSohRuntimeEnv.HandleSpinLock, Kirql );

			// wait for the other threads to free the Controlling Stream Handles
			KeWaitForSingleObject( &gSohRuntimeEnv.evHandles,
											Executive,
											KernelMode,
											TRUE,
											NULL );

		}
		else
		{
			gSohRuntimeEnv.bProcessingControlStreams = TRUE;
			bProcessControlStreams = TRUE;
			KeReleaseSpinLock( &gSohRuntimeEnv.HandleSpinLock, Kirql );
		}
	} while (bProcessControlStreams == FALSE);

	if (gSohRuntimeEnv.hDevice == NULL)
	{
		// Open The DigiSoh Device
		Status = DsOpenDeviceInternal( pDevObj, &gSohRuntimeEnv.hDevice );

		if (Status != STATUS_SUCCESS)
		{
			DigiDump(
				DIGIINIT,
				("DigiSohCreateControllingStreams: Couldn't Open DigiSoh\n")
				);
			return Status;
		}
		gSohRuntimeEnv.pDeviceObject = pDevObj;
	}

	// Build the Stream for the Miniport Line Instance
	iPort = (USHORT) ulPort;
	Status = DigiSohBuildMiniportInstance(hkLine,gSohRuntimeEnv.pDeviceObject,pDevExt);

	// See if any other threads are waiting for access to the controlling streams.
	KeAcquireSpinLock( &gSohRuntimeEnv.HandleSpinLock, &Kirql );
	bWaitProcessControlStreams = gSohRuntimeEnv.bWaitProcessingControlStreams;
	gSohRuntimeEnv.bProcessingControlStreams = FALSE;
	KeReleaseSpinLock( &gSohRuntimeEnv.HandleSpinLock, Kirql );
	if (bWaitProcessControlStreams)
	{			
		// set event for other threads to access the Controlling Streams
		KeSetEvent( &gSohRuntimeEnv.evHandles,
					0,
					FALSE );	
	}

	DigiDump( DIGIINIT, ( "DigiSohCreateControllingStreams: Status = %08x\n", Status ) );

	return ( Status );
}

void
DigiSohCloseControllingStreams(
	IN PSOH_DEV_EXT		pDevExt
	)

/*++

   Routine Description:

   This routine builds the closes the Streams Stacks associated with this adapter.

   Arguments:

   pDevExt			- Pointer to this devices extension.

   Return Value:	- void


   -- */
{

	BOOLEAN			bProcessControlStreams = FALSE;
	BOOLEAN			bWaitProcessControlStreams = FALSE;
	KIRQL			Kirql;

	DigiDump( DIGIFLOW, ( "Entering DigiSohCloseControllingStreams\n" ) );

	// In W2K multiple adapters can call this routine.  Only one adpater instance
	// can be opening or closing the controlling streams.  This requirement goes
	// away if the firmware is every modified to support multiple controlling streams.
	//
	do 
	{
		KeAcquireSpinLock( &gSohRuntimeEnv.HandleSpinLock, &Kirql );
		if (gSohRuntimeEnv.bProcessingControlStreams)
		{
			gSohRuntimeEnv.bWaitProcessingControlStreams = TRUE;
			KeReleaseSpinLock( &gSohRuntimeEnv.HandleSpinLock, Kirql );

			// wait for the other threads to free the Controlling Stream Handles
			KeWaitForSingleObject( &gSohRuntimeEnv.evHandles,
											Executive,
											KernelMode,
											TRUE,
											NULL );

		}
		else
		{
			gSohRuntimeEnv.bProcessingControlStreams = TRUE;
			bProcessControlStreams = TRUE;
			KeReleaseSpinLock( &gSohRuntimeEnv.HandleSpinLock, Kirql );
		}
	} while (bProcessControlStreams == FALSE);

	if ( gSohRuntimeEnv.hDevice )
	{
		if ( gSohRuntimeEnv.hFr )
		{
			(void)DsClose( gSohRuntimeEnv.hFr );
			gSohRuntimeEnv.hFr = NULL;
		}
		if ( gSohRuntimeEnv.hX25 )
		{
			(void)DsClose( gSohRuntimeEnv.hX25 );
			gSohRuntimeEnv.hX25 = NULL;
		}

		(void)DsCloseDevice( gSohRuntimeEnv.hDevice );
		gSohRuntimeEnv.hDevice = NULL;
		gSohRuntimeEnv.pDeviceObject = NULL;
	}

	// See if any other threads are waiting for access to the controlling streams.
	KeAcquireSpinLock( &gSohRuntimeEnv.HandleSpinLock, &Kirql );
	bWaitProcessControlStreams = gSohRuntimeEnv.bWaitProcessingControlStreams;
	gSohRuntimeEnv.bProcessingControlStreams = FALSE;
	KeReleaseSpinLock( &gSohRuntimeEnv.HandleSpinLock, Kirql );
	if (bWaitProcessControlStreams)
	{			
		// set event for other threads to access the Controlling Streams
		KeSetEvent( &gSohRuntimeEnv.evHandles,
					0,
					FALSE );	
	}

	DigiDump( DIGIFLOW, ( "Exiting DigiSohCloseControllingStreams\n" ) );

	return;
}

NTSTATUS
DigiSohBuildMiniportInstance(
	IN HANDLE					hkLine, 
    IN PDEVICE_OBJECT			pDevObj,
    IN PSOH_DEV_EXT				pDevExt
    )

/*++

   Routine Description:

   This routine processes Miniport adapter port instance.  Here we register
   the Miniport with the Streams based XWAN module.

   Arguments:

   pRegistryPath	- Pointer to the registry path (CurrentControlSet\Services\Dg570MP1\0).
   pwPortName		- Pointer to the registry path (0).
   pDevObj			- Pointer to the Device Object		
   pDevExt			- Pointer to the Device Extension	
   
   Return Value:	- Status


   -- */
{

	NTSTATUS		Status = STATUS_SUCCESS;
	ULONG			ulProtocolId;
	HANDLE			hKeyMiniPort = NULL;
	UNICODE_STRING	Protocol;
	UNICODE_STRING	Enum;
	ULONG			ulSnid;

	DigiDump( DIGIFLOW, ( "Entering DigiSohBuildMiniportInstance\n" ) );

	// Get Protocol setting
	RtlInitUnicodeString( &Protocol, NULL );
	(void)DigiRegQuerySz( hkLine, DXB_REGVAL_PROTOCOL, &Protocol, 
			              DigiRegFlagOptional, DXB_REGVAL_PROTOCOL_DEFAULT );

	// Build protocol stack accordingly

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_FRAMERELAY );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		Status = SohBuildFrameOverXWan( pDevObj, pDevExt, hkLine );
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_X25 );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		Status = SohBuildX25OverXWan( pDevObj, pDevExt, hkLine );
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_SNA );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		// No configuration necessary for SNA
		Status = STATUS_SUCCESS;
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_PPP );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		// No configuration necessary for PPP
		Status = STATUS_SUCCESS;
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_NONE );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		// No configuration necessary if no protocol
		Status = STATUS_SUCCESS;
		goto ProtocolDone;
	}

	// This Registry Key is not a Port!
	Status = STATUS_UNSUCCESSFUL;
	ASSERT(0);

ProtocolDone:

	DigiRegFreeString( &Protocol );

	DigiDump( DIGIINIT, ( "DigiSohBuildMiniportInstance : Status = %08x\n", Status ) );
	DigiDump( DIGIFLOW, ( "Exiting DigiSohBuildMiniportInstance\n" ) );

	return ( Status );
}

#else



NTSTATUS
DigiSohCreateControllingStreams(
	IN PDEVICE_OBJECT	 pDevObj,
	IN PSOH_DEV_EXT		 pDevExt,
	IN PUNICODE_STRING   pucParametersPath
	)

/*++

   Routine Description:

   This routine builds the streams stacks based on the configured miniport
   adapters.  

   Arguments:

   pDevObj			- Pointer to this devices object.
   pDevExt			- Pointer to this devices extension.
   pRegistryPath	- Pointer to the registry path (DigiSoh).

   Return Value:	- NT_STATUS


   -- */
{
	NTSTATUS		Status;

	DigiDump( DIGIFLOW, ( "Entering DigiSohCreateControllingStreams\n" ) );

	// Open The DigiSoh Device
	Status = DsOpenDeviceInternal( pDevObj, &gSohRuntimeEnv.hDevice );

	if (Status != STATUS_SUCCESS)
	{
		DigiDump(
			DIGIINIT,
			("DigiSohCreateControllingStreams: Couldn't Open DigiSoh\n")
			);
		return Status;
	}

	// Initialize the Port Count
	iPort = 0;

	// Enumerate the the Miniport Devices Key
    Status = DigiSohUtilEnumSubkeys(pucParametersPath,
							 DIGISOH_REGSUBKEY_MPA,
							 DigiSohEnumMiniportInstance,
							 (PVOID) pDevObj,
							 (PVOID) pDevExt,
							 NULL);

	// If there were any errors then close all the Controlling Streams
	if ( NT_ERROR( Status ) )
	{
		// Frame Relay
		if (gSohRuntimeEnv.hFr) 
		{
			DsClose( gSohRuntimeEnv.hFr );
		}

		// X25
		if (gSohRuntimeEnv.hX25) 
		{
			DsClose( gSohRuntimeEnv.hX25 );
		}

		// CloseThe DigiSoh Device
		if (gSohRuntimeEnv.hDevice)
		{
			(void) DsCloseDevice( gSohRuntimeEnv.hDevice );
		}

	}


	DigiDump( DIGIINIT, ( "DigiSohCreateControllingStreams: Status = %08x\n", Status ) );
	DigiDump( DIGIFLOW, ( "Exiting DigiSohCreateControllingStreams\n" ) );

	return ( Status );
}


NTSTATUS
DigiSohEnumMiniportInstance(
    IN PWSTR					pwRegistryPath,
    IN PWSTR					pwDeviceName,
    IN PDEVICE_OBJECT			pDevObj,
    IN PSOH_DEV_EXT				pDevExt,
	IN PVOID					Nothing
    )

/*++

   Routine Description:

   This routine processes on instance of a miniport adapter by enumerating the
   ports for a Miniport adapter port instance. 

   Arguments:

   pRegistryPath	- Pointer to the registry path (CurrentControlSet\Services\Dg570MP1).
   pwDeviceName		- Pointer to the registry path (Dg570MP1).

   Return Value:	- The Number of Ports


   -- */
{

	NTSTATUS		Status;
	UNICODE_STRING	MiniportPath;

	DigiDump( DIGIFLOW, ( "Entering DigiSohEnumMiniportInstance\n" ) );
	DigiDump( DIGIINIT, ( "Registry Path = %ls\n",pwRegistryPath ) );
	DigiDump( DIGIINIT, ( "Registry Path = %ls\n",pwDeviceName ) );

	// Enumerate the Ports from the Miniports Miniport Key

	// Obtain a UNICODE STRING long enough to add the Paramters Key to 
	// the input registry path.
	Status = DigiStrAllocUnicodeString ( &MiniportPath,
									     DIGISOH_REGSUBKEY_SERVICES,
									     pwDeviceName,
									     NULL );
	
	if (Status != STATUS_SUCCESS)
	{
		DigiDump(
			DIGIINIT,
			("DigiSohEnumMiniportInstance: Couldn't Allocate the Configurations String \n")
			);
		return Status;
	}

	// Enumerate the the Miniport Devices Key
    Status = DigiSohUtilEnumSubkeys(&MiniportPath,
							 DIGISOH_REGSUBKEY_PARAMETERS,
							 DigiSohBuildMiniportInstance,
							 (PVOID) pDevObj,
							 (PVOID) pDevExt,
							 NULL);

	DigiStrFreeUnicodeString( &MiniportPath);

	DigiDump( DIGIINIT, ( "DigiSohEnumMiniportInstance : Status = %08x\n", Status ) );
	DigiDump( DIGIFLOW, ( "Exiting DigiSohEnumMiniportInstance\n" ) );

	return ( Status );
}

NTSTATUS
DigiSohBuildMiniportInstance(
    IN PWSTR					pwRegistryPath,
    IN PWSTR					pwPortName,
    IN PDEVICE_OBJECT			pDevObj,
    IN PSOH_DEV_EXT				pDevExt,
	IN PVOID					Nothing
    )

/*++

   Routine Description:

   This routine processes Miniport adapter port instance.  Here we register
   the Miniport with the Streams based XWAN module.

   Arguments:

   pRegistryPath	- Pointer to the registry path (CurrentControlSet\Services\Dg570MP1\0).
   pwPortName		- Pointer to the registry path (0).

   Return Value:	- Status


   -- */
{

	NTSTATUS		Status = STATUS_SUCCESS;
	ULONG			ulProtocolId;
	HANDLE			hKeyMiniPort = NULL;
	UNICODE_STRING	Protocol;
	UNICODE_STRING	Enum;
	ULONG			ulSnid;

	DigiDump( DIGIFLOW, ( "Entering DigiSohBuildMiniportInstance\n" ) );
	DigiDump( DIGIINIT, ( "Registry Path = %ls\n",pwRegistryPath ) );
	DigiDump( DIGIINIT, ( "Registry Path = %ls\n",pwPortName ) );

	// Get a Key to the Registry Path
	Status = DigiRegOpenKey( &hKeyMiniPort, pwRegistryPath );
	if ( NT_ERROR( Status ) ) {
		goto ProtocolDone;
	}


	// Get Protocol setting
	RtlInitUnicodeString( &Protocol, NULL );
	(void)DigiRegQuerySz( hKeyMiniPort, DXB_REGVAL_PROTOCOL, &Protocol, 
			              DigiRegFlagOptional, DXB_REGVAL_PROTOCOL_DEFAULT );

	// Build protocol stack accordingly

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_FRAMERELAY );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		Status = SohBuildFrameOverXWan( pDevObj, pDevExt, hKeyMiniPort );
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_X25 );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		Status = SohBuildX25OverXWan( pDevObj, pDevExt, hKeyMiniPort );
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_SNA );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		// No configuration necessary for SNA
		Status = STATUS_SUCCESS;
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_PPP );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		// No configuration necessary for PPP
		Status = STATUS_SUCCESS;
		goto ProtocolDone;
	}

	RtlInitUnicodeString( &Enum, DXB_REGVAL_PROTOCOL_ENUM_NONE );
	if ( RtlEqualUnicodeString( &Protocol, &Enum, TRUE ) )
	{
		// No configuration necessary if no protocol
		Status = STATUS_SUCCESS;
		goto ProtocolDone;
	}

	// This Registry Key is not a Port!
	Status = STATUS_UNSUCCESSFUL;
	DigiDump( DIGIERRORS, ( "SOH: Port %ls Invalid protocol setting, %ls!\n", pwPortName, Protocol.Buffer ) );

ProtocolDone:
	
	// If the Subkey contains a SNID then it defines a port	
	if (NT_SUCCESS(DigiRegQueryDword( hKeyMiniPort,
							 DXB_REGVAL_LINE_SNID,
							 &ulSnid,
							 DigiRegFlagRequired,
							 0 )))
	{
		// Update the Relative Port Count
		iPort++;
	}


	// Free the Key Resource
	if ( hKeyMiniPort ) {
		DigiRegCloseKey( hKeyMiniPort );
	}

	DigiRegFreeString( &Protocol );

	DigiDump( DIGIINIT, ( "DigiSohBuildMiniportInstance : Status = %08x\n", Status ) );
	DigiDump( DIGIFLOW, ( "Exiting DigiSohBuildMiniportInstance\n" ) );

	return ( Status );
}

#endif // NT5_BUILD

NTSTATUS
SohBuildFrameOverXWan(
    IN PDEVICE_OBJECT			pDevObj,
    IN PSOH_DEV_EXT				pDevExt,
	IN HANDLE					hKeyPort
	)
/*++

Description:

	Build up Frame Relay protocol using XWAN lower edge.

Parameters:

	pDevObj
		Device Object for the adapter.

	pDevExt
		Device Extension for the adapter.

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

Return Value:

	STATUS_SUCCESS, or failure status.

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


	DigiDump( DIGIINIT, ( "SOH: Enter SodBuildFrameOverXWan\n") );

	// Us the already open FR controlling stream if it exists.
	hFr = gSohRuntimeEnv.hFr;
	if (hFr == NULL) 
	{

		// Get a controlling Stream for FR
		Status = DsOpen( gSohRuntimeEnv.hDevice, "FR", NULL, NULL, 0, &hFr, &Error ) ;
		if (NT_ERROR( Status)) 
		{
			goto Exit;
		}
		else
		{
			gSohRuntimeEnv.hFr = hFr;
		}
	}

	// Link Frame on XWan
	{
	
		Status =  DsOpen( gSohRuntimeEnv.hDevice, "XWAN", NULL, NULL, 0, &hWan, &Error );

		if ( NT_SUCCESS( Status ) )
		{
			Status = SohLinkDevice( hFr, hWan, &MuxId );
			DsClose( hWan );
		}
	}

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

	if ( NT_SUCCESS( Status ) )
		Status = SohFrSetSnid( pDevExt, MuxId, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = SohWanTune( pDevExt, hKeyPort, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = SohFrTune( pDevExt, hKeyPort, Snid, iPort );

	if ( NT_SUCCESS( Status ) )
		Status = SohFrSnIdent( pDevExt, Snid, iPort );

Exit:

	DigiDump( DIGIINIT, ( "SOH: Leave SohBuildFrameOverWan, Status 0x%x\n", Status ) );
	return Status;
}


NTSTATUS
SohBuildX25OverXWan(
    IN PDEVICE_OBJECT			pDevObj,
    IN PSOH_DEV_EXT				pDevExt,
	IN HANDLE					hKeyPort
	)
/*++

Description:

	Build up Lapb protocol using WAN lower edge.  

Parameters:

	pExt

	pDevObj
		Device Object for the adapter.

	pDevExt
		Device Extension for the adapter.

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

Return Value:

	STATUS_SUCCESS, or failure status.

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

	DigiDump( DIGIINIT, ( "SOH: Enter sohBuildX25OverXWan\n") );

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

	if ( NT_SUCCESS( Status ) )
		Status = DsOpen( gSohRuntimeEnv.hDevice, "LAPB", NULL, NULL, 0, &hLapb, &Error );
	{

		// Link Lapb on XWAN
		if ( NT_SUCCESS( Status ) )
		{
			HANDLE hWan = NULL;

			Status =  DsOpen( gSohRuntimeEnv.hDevice, "XWAN", NULL, NULL, 0, &hWan, &Error );

			if ( NT_SUCCESS( Status ) )
			{
				ULONG MuxId;
				Status = SohLinkDevice( hLapb, hWan, &MuxId );

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

	if ( NT_SUCCESS( Status ) )
		Status = SohWanTune( pDevExt, hKeyPort, Snid );

	if ( NT_SUCCESS( Status ) )
		Status = SohLapbTune( pDevExt, hKeyPort, Snid, iPort);


	// Us the already open X25 controlling stream if it exists.
	hX25 = gSohRuntimeEnv.hX25;
	if (hX25 == NULL) 
	{

		// Get a controlling Stream for FR
		Status = DsOpen( gSohRuntimeEnv.hDevice, "X250", NULL, NULL, 0, &hX25, &Error ) ;
		if (NT_SUCCESS( Status)) 
		{
			gSohRuntimeEnv.hX25 = hX25;
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		Status = SohX25Tune( hKeyPort, iPort, Snid, hX25);
	}

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

		if ( NT_SUCCESS( Status ) )
		{
			Status = SohX25SnIdent( hKeyPort, MuxId, Snid , hX25);
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		Status = SohX25Tune( hKeyPort, iPort, Snid, hX25);
	}

	if ( hLapb )
		DsClose( hLapb );

	DigiDump( DIGIINIT, ( "SOH: Leave SohBuildX25OverWan, Status 0x%x\n", Status ) );
	return Status;
}


STATIC NTSTATUS
SohLinkDevice(
	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( DIGIERRORS, ( "Soh!LinkDevice failed, Status 0x%x, Error 0x%x\n", Status, Error ) );
		goto Exit;
	}

	*pMuxId = MuxId;

Exit:

	return Status;
}

