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

   x25mp.c

Abstract:

   This module contains NDIS driver upper-edge functions or dispatch routines.

Author:

   Michael Zarns

--*/

#include "digix25.h"

VOID X25DispatchInit( PX25_ADAPTER pAdapter );


STATIC INLINE NDIS_STATUS
DigiNdisRegOpenKey( 
	OUT PNDIS_HANDLE hKey, 
	IN  NDIS_HANDLE  hWrapper )
{
	NDIS_STATUS Status;
	NdisOpenConfiguration( &Status, hKey, hWrapper );
	return Status;
}


#if NT5_BUILD
STATIC INLINE NDIS_STATUS
DigiNdisRegOpenSubkey( 
	OUT PNDIS_HANDLE hKey, 
	IN  NDIS_HANDLE  hkParent,
	IN  PCWSTR       SubKeyName )
{
	NDIS_STATUS Status;
	NDIS_STRING Name;
	RtlInitUnicodeString( &Name, SubKeyName );
	NdisOpenConfigurationKeyByName( &Status, hkParent, &Name, hKey );
	return Status;
}
#endif

STATIC INLINE VOID
DigiNdisRegCloseKey( IN NDIS_HANDLE hKey )
{
	NdisCloseConfiguration( hKey );
}


STATIC INLINE NDIS_STATUS
DigiNdisRegWriteDword( 
	IN NDIS_HANDLE hKey,
	IN PCWSTR      pValue,
	IN ULONG       Dword )
{
	NDIS_STATUS Status;
	NDIS_STRING Value;

	NDIS_CONFIGURATION_PARAMETER Param = {0};
	Param.ParameterType = NdisParameterInteger;
	Param.ParameterData.IntegerData = Dword;

	NdisInitUnicodeString( &Value, pValue );
	NdisWriteConfiguration( &Status, hKey, &Value, &Param );
	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIWARN, ( "DigiX25: Registry DWORD write failed, value %S\n", Value.Buffer ) );
	}
	return Status;
}


STATIC INLINE NDIS_STATUS
DigiNdisRegReadDword( 
	IN NDIS_HANDLE hKey,
	IN PCWSTR      pValue,
	OUT PULONG     pDword )
{
	NDIS_STATUS Status = NDIS_STATUS_FAILURE;
	PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
	NDIS_STRING Value;
	NdisInitUnicodeString( &Value, pValue );
	NdisReadConfiguration( &Status, &pParam, hKey, &Value, NdisParameterInteger );
	if ( NT_SUCCESS( Status ) )
	{
		ASSERT( pParam->ParameterType == NdisParameterInteger );
		*pDword = pParam->ParameterData.IntegerData;
	}
	else
	{
		DigiDump( DIGIWARN, ( "DigiX25: Registry DWORD read failed, value %S\n", Value.Buffer ) );
	}
	return Status;
}


STATIC INLINE NDIS_STATUS
DigiNdisRegReadSz( 
	IN NDIS_HANDLE   hKey,
	IN PCWSTR        pValue,
	OUT PNDIS_STRING pString )
{
	NDIS_STATUS Status = NDIS_STATUS_FAILURE;
	PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
	NDIS_STRING Value;
	NdisInitUnicodeString( &Value, pValue );
	NdisReadConfiguration( &Status, &pParam, hKey, &Value, NdisParameterString );
	if ( NT_SUCCESS( Status ) )
	{
		ASSERT( pParam->ParameterType == NdisParameterString );
		*pString = pParam->ParameterData.StringData;
	}
	else
	{
		DigiDump( DIGIWARN, ( "DigiX25: Registry SZ read failed, value %S\n", Value.Buffer ) );
	}
	return Status;
}


STATIC INLINE NDIS_STATUS
DigiNdisRegReadMultiSz( 
	IN NDIS_HANDLE   hKey,
	IN PCWSTR        pValue,
	OUT PNDIS_STRING pString )
{
	NDIS_STATUS Status = NDIS_STATUS_FAILURE;
	PNDIS_CONFIGURATION_PARAMETER pParam = NULL;
	NDIS_STRING Value;
	NdisInitUnicodeString( &Value, pValue );
	NdisReadConfiguration( &Status, &pParam, hKey, &Value, NdisParameterMultiString );
	if ( NT_SUCCESS( Status ) )
	{
		ASSERT( pParam->ParameterType == NdisParameterMultiString );
		*pString = pParam->ParameterData.StringData;
	}
	else
	{
		DigiDump( DIGIWARN, ( "DigiX25: Registry MULTI_SZ read failed, value %S\n", Value.Buffer ) );
	}
	return Status;
}


STATIC VOID
X25DestructAdapter( 
	IN PX25_ADAPTER pAdapter )
{
	if ( pAdapter != NULL )
	{
		if ( pAdapter->hDevice != NULL )
		{
			X25CallMgrThreadStop( pAdapter );	
			X25QueueStop( pAdapter );
			X25StopListening( pAdapter );
			(void)DsCloseDevice( pAdapter->hDevice );
		}
		if ( pAdapter->pLines )
			DigiFreeMem( pAdapter->pLines );
		DigiFreeMem( pAdapter );
	}
}


STATIC NDIS_STATUS
X25ConstructAdapter( 
	IN PX25_ADAPTER   *ppAdapter,
	IN NDIS_HANDLE     hMiniport,
	IN PUNICODE_STRING pName,
	IN PUNICODE_STRING pAdapterIndex,
	IN PUNICODE_STRING pDxbDevice,
	IN ULONG           Snid,
	IN ULONG           Endpoints,
	IN ULONG           fVersion1,
	IN	ULONG				 LinkSpeed)
{
	NDIS_STATUS Status = NDIS_STATUS_RESOURCES;
	PX25_ADAPTER pAdapter = DigiAllocMem( NonPagedPool, sizeof *pAdapter );

	DigiDump( DIGIINIT, ("X25ConstructAdapter:pName %wZ\n\tAdapterIndex %wZ\n\tDxbDevice %wZ", 
		pName,pAdapterIndex,pDxbDevice) );




	if ( !pAdapter )
		goto Exit;

	NdisZeroMemory( pAdapter, sizeof *pAdapter );
	pAdapter->Type = X25_TYPE_ADAPTER;

	pAdapter->Name.Buffer = pAdapter->NameBuffer;
	pAdapter->Name.MaximumLength = sizeof pAdapter->NameBuffer;
	RtlUnicodeStringToAnsiString( &pAdapter->Name, pName, FALSE );

	pAdapter->Number.Buffer = pAdapter->NumberBuffer;
	pAdapter->Number.MaximumLength = sizeof pAdapter->NumberBuffer;
	RtlUnicodeStringToAnsiString( &pAdapter->Number, pAdapterIndex, FALSE );

	pAdapter->hMiniport = hMiniport;
	pAdapter->Snid      = Snid;
	pAdapter->nLines    = Endpoints;
	pAdapter->LinkSpeed = LinkSpeed;
	pAdapter->pLines    = DigiAllocMem( NonPagedPool, ( sizeof pAdapter->pLines[0] ) * Endpoints );
#if DBG   
	DbgPrint("DigiX25:Memory Allocated for pLines - count %d\n",Endpoints);
#endif   
	if ( !pAdapter->pLines )
		goto Exit;

	NdisZeroMemory( pAdapter->pLines, (sizeof pAdapter->pLines[0]) * Endpoints );

	DigiInitializeSpinLock( &pAdapter->Lock );



	X25DispatchInit( pAdapter );
	
	Status = DsOpenDevice( pDxbDevice, &pAdapter->hDevice );

	if ( NT_SUCCESS( Status ) )
		Status = X25StartListening( pAdapter );

	if ( NT_SUCCESS( Status ) )
		Status = X25QueueStart( pAdapter );	// Start the adapter context queue

	if ( NT_SUCCESS( Status ) )
		Status = X25CallMgrThreadStart( pAdapter );	

	if ( NT_SUCCESS( Status ) )
		*ppAdapter = pAdapter;

Exit:

	if ( NT_ERROR( Status ) && pAdapter )
		X25DestructAdapter( pAdapter );

	return Status;
}

STATIC NDIS_STATUS
X25ConstructFoo( 
	IN PX25_ADAPTER   *ppAdapter,
	IN NDIS_HANDLE     hMiniport,
	IN PUNICODE_STRING pAdapterIndex,
	IN ULONG           Endpoints )
{
	NDIS_STATUS Status = NDIS_STATUS_RESOURCES;
	PX25_ADAPTER pAdapter = DigiAllocMem( NonPagedPool, sizeof *pAdapter );
	if ( !pAdapter )
		goto Exit;

	NdisZeroMemory( pAdapter, sizeof *pAdapter );
	pAdapter->Type = X25_TYPE_ADAPTER;

	pAdapter->Name.Buffer = pAdapter->NameBuffer;
	pAdapter->Name.MaximumLength = sizeof pAdapter->NameBuffer;

	pAdapter->Number.Buffer = pAdapter->NumberBuffer;
	pAdapter->Number.MaximumLength = sizeof pAdapter->NumberBuffer;
	RtlUnicodeStringToAnsiString( &pAdapter->Number, pAdapterIndex, FALSE );


	pAdapter->hMiniport = hMiniport;
	pAdapter->nLines    = Endpoints;
	pAdapter->pLines    = DigiAllocMem( NonPagedPool, ( sizeof pAdapter->pLines[0] ) * Endpoints );
	if ( !pAdapter->pLines )
		goto Exit;

	NdisZeroMemory( pAdapter->pLines, (sizeof pAdapter->pLines[0]) * Endpoints );

	DigiInitializeSpinLock( &pAdapter->Lock );

	Status = STATUS_SUCCESS;

Exit:
	if ( NT_SUCCESS( Status ) )
		*ppAdapter = pAdapter;

	return Status;
}

STATIC VOID
X25StripTapiDevices( 
	IN PUNICODE_STRING  pSource,
	OUT PUNICODE_STRING pDest,
	IN PUNICODE_STRING  pIndex )
/*++

Description

	Strip the entries in pSource matching the adapter index at pIndex, writing the
	the resultant string in pDest.

--*/
{
	UNICODE_STRING Entry;
	ULONG i;
	UNICODE_STRING NullSeparator;
	WCHAR NullStr[2] = {0};
	NullSeparator.Buffer = (PVOID)&NullStr;
	NullSeparator.Length = sizeof NullStr[0];
	NullSeparator.MaximumLength = sizeof NullStr;

	ASSERT( pDest->MaximumLength >= pSource->MaximumLength );

	for ( i = 0; DigiStrExtractMultiSzEntry( &Entry, pSource, i ); i++ )
	{
		ASSERT( Entry.Length > pIndex->Length );
		if ( RtlCompareMemory( Entry.Buffer, pIndex->Buffer, pIndex->Length ) < pIndex->Length )
		{
			// This entry does *not* match the adapter instance we're stripping.  Move it.
			(void)RtlAppendUnicodeStringToString( pDest, &Entry );
			(void)RtlAppendUnicodeStringToString( pDest, &NullSeparator );
			ASSERT( pDest->Length < pDest->MaximumLength );
		}
	}
}


STATIC VOID
X25AddTapiDevices( 
	IN OUT PUNICODE_STRING pDest,
	IN PUNICODE_STRING     pIndex,
	IN ULONG               nEndpoints )
/*++

Description

	Append Tapi devices to string pDest, one per endpoint.  Use pIndex as adapter index.

--*/
{
	ULONG i;
	UNICODE_STRING NullSeparator;
	WCHAR NullStr[2] = {0};
	NullSeparator.Buffer = (PVOID)&NullStr;
	NullSeparator.Length = sizeof NullStr[0];
	NullSeparator.MaximumLength = sizeof NullStr;

	for ( i = 0; i < nEndpoints; i++ )
	{
		UNICODE_STRING Line;
		WCHAR LineBuffer[8];
		Line.Buffer = LineBuffer;
		Line.Length = 0;
		Line.MaximumLength = sizeof LineBuffer;
		(void)RtlIntegerToUnicodeString( i, 10, &Line );

		// Append TapiDeviceName, of the form [Index]-[Endpoint]-0, i.e. "1000-1-0".
		(void)RtlAppendUnicodeStringToString( pDest, pIndex );
		(void)RtlAppendUnicodeToString( pDest, L"-" );
		(void)RtlAppendUnicodeStringToString( pDest, &Line );
		(void)RtlAppendUnicodeToString( pDest, L"-0" );
		(void)RtlAppendUnicodeStringToString( pDest, &NullSeparator );
		ASSERT( pDest->Length < pDest->MaximumLength );
	}
	// Add terminating NULL 
	(void)RtlAppendUnicodeStringToString( pDest, &NullSeparator );
	ASSERT( pDest->Length < pDest->MaximumLength );
}


STATIC NTSTATUS
X25WriteTapiDevicesString( 
	PUNICODE_STRING pAdapterIndex,
	ULONG           nEndpoints )
{
	NTSTATUS Status;
	USHORT cb = 0;
	HANDLE hKeyTapiDevices = NULL;
	HANDLE hKeyDriver = NULL;
	UNICODE_STRING PreviousAddress = {0};
	UNICODE_STRING Address = {0};

	// Create/Open Tapi Devices key
	Status = DigiRegCreateKey( &hKeyTapiDevices, 
	                         L"\\Registry\\Machine\\Hardware\\DeviceMap\\Tapi Devices" );
	if ( NT_SUCCESS( Status ) )
		Status = DigiRegCreateSubkey( &hKeyDriver, hKeyTapiDevices, TEXT( X25_PROVIDER_DEVICE ) );

	// Write Media Type Value
	if ( NT_SUCCESS( Status ) )
		Status = DigiRegWriteSz( hKeyDriver, L"Media Type", TEXT( X25_PROVIDER_MEDIA ) );

	// Read Previous Address Value
	if ( NT_SUCCESS( Status ) )
	{
		Status = DigiRegQueryMultiSz( hKeyDriver, L"Address", &PreviousAddress, DigiRegFlagOptional, L"" );
		if ( NT_SUCCESS( Status ) )
			cb = PreviousAddress.Length;
	}

	if ( NT_ERROR( Status ) )
		goto Exit;

	// Alloc Buffer for new Address multi_sz Value
	{
		// "Index-0-0,Index-1-0,Index-2-0,..."
		USHORT ccNeeded = (USHORT)( ( pAdapterIndex->Length + 8 ) * nEndpoints );
		cb += ccNeeded * sizeof(WCHAR);
		Address.MaximumLength = cb;
		Address.Buffer = DigiAllocMem( PagedPool, Address.MaximumLength );
		if ( !Address.Buffer )
			goto Exit;
	}

	// Strip out any endpoints currently matching to this adapter instance
	X25StripTapiDevices( &PreviousAddress, &Address, pAdapterIndex );

	// Add all endpoints for this adapter instance
	X25AddTapiDevices( &Address, pAdapterIndex, nEndpoints );

	Status = DigiRegWriteMultiSz( hKeyDriver, L"Address", &Address );

Exit:

	if ( Address.Buffer )
		DigiFreeMem( Address.Buffer );			// Alloc'd locally, use DigiFreeMem!

	if ( PreviousAddress.Buffer )
		DigiRegFreeString( &PreviousAddress );	// Alloc'd by DigiRegQuery, use DigiRegFreeString!

	if ( hKeyDriver )
		DigiRegCloseKey( hKeyDriver );
	if ( hKeyTapiDevices )
		DigiRegCloseKey( hKeyTapiDevices );

	return Status;
}


VOID
X25Halt(
	IN NDIS_HANDLE hAdapter )
/*++

Description:

	Called by Ndis to stop a virtual NIC.  Effectively must undo everything that was 
	done in X25Initialize.

--*/
{
	PX25_ADAPTER const pAdapter = hAdapter;
	DigiDump( DIGIMP, ( "DigiX25!X25Halt: hAdapter 0x%08x\n", hAdapter ) );

	X25DestructAdapter( pAdapter );
}


NDIS_STATUS
X25Initialize(
	OUT PNDIS_STATUS pError,
	OUT PUINT        pArrayIndex,
	IN PNDIS_MEDIUM  pMediumArray,
	IN UINT          ArraySize,
	IN NDIS_HANDLE   hMiniport,
	IN NDIS_HANDLE   hConfigContext )
/*++

Description:

	Called by NDIS to start a virtual NIC.  Grab settings from registry, attach to dxb.

--*/
{
	NDIS_STATUS Status;
	ULONG i;

	DigiDump( DIGIINIT|DIGIMP, ( "DigiX25!X25Initialize: hMiniport 0x%08x\n", hMiniport ) );
#if DBG
	DbgPrint("DigiX25!X25Initialize - Entry\n");
#endif
	for ( i = 0; i < ArraySize; i++ )
		if ( pMediumArray[i] == NdisMediumWan )
			break;

	if ( i == ArraySize )
	{
		DigiDump( DIGIERROR, ( "DigiX25!X25Initialize: NdisMediumWan not supported!\n" ) );
		Status = NDIS_STATUS_UNSUPPORTED_MEDIA;
		goto Exit;
	}

	*pArrayIndex = i;

	// Get Configuration Params from registry
	{
		NDIS_HANDLE    hkAdapter = NULL;
		HANDLE         hkParams = NULL;
		ULONG          Snid;
		ULONG          Endpoints;
		ULONG          fVersion1;
		ULONG				LinkSpeed;
		ULONG          fSpoof = FALSE;	// Only spoof if NT5 and regval Spoof=TRUE.
		UNICODE_STRING MyName = {0};
		UNICODE_STRING DxbDevice = {0};
		UNICODE_STRING AdapterIndex = {0};

		Status = DigiNdisRegOpenKey( &hkAdapter, hConfigContext );
		if ( NT_SUCCESS( Status ) )
		{
			UNICODE_STRING RegPath = {0};
			UNICODE_STRING FullRedirectPath;

#if NT5_BUILD
			Status = DigiNdisRegOpenSubkey( &hkParams, hkAdapter, DXB_REGKEY_PARAMETERS );
			if ( NT_SUCCESS( Status ) )
			{
				// Ignore failure reading Spoof, default to FALSE
//				(void)DigiNdisRegReadDword( hkParams, DXB_REGVAL_SPOOF, &fSpoof );
//				(void)DigiNdisRegWriteDword( hkParams, DXB_REGVAL_SPOOF, FALSE );
#else
				hkParams = hkAdapter;
#endif
				// Read NCPA Adapter instance number
				if ( NT_SUCCESS( Status ) )
					Status = DigiNdisRegReadSz( hkParams, DXB_REGVAL_NDIS_ADAPTER_INSTANCE, &AdapterIndex );

				// Read Ndis Adapter Name, i.e. DigiX251001
				if ( NT_SUCCESS( Status ) )
					Status = DigiNdisRegReadSz( hkParams, DXB_REGVAL_SERVICENAME, &MyName );

				if ( NT_SUCCESS( Status ) )
				{
					DigiDump( DIGIINIT, ( "X25!Initialize: Miniport Adapter %S, Instance %S\n", MyName.Buffer, AdapterIndex.Buffer ) );
				}

				// All other params are stored under the Dxb adapter key.  Get the Redirect path
				// and read params from there.
				if ( NT_SUCCESS(Status) )
					Status = DigiNdisRegReadSz( hkParams, DXB_REGVAL_REGPATH, &RegPath );

#if NT5_BUILD
#define REDIRECTPATH_ROOT L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Class\\"
#else
#define REDIRECTPATH_ROOT L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\"
#endif

				if ( NT_SUCCESS( Status ) )
					Status = DigiStrAllocUnicodeString( &FullRedirectPath, REDIRECTPATH_ROOT, RegPath.Buffer, NULL );

				if ( NT_SUCCESS( Status ) )
				{
					HANDLE hKey;
					Status = DigiRegOpenKey( &hKey, FullRedirectPath.Buffer );
					if ( NT_SUCCESS( Status ) )
					{
						// SNID, SubNetwork ID.  Used by firmware to identify the physical line
						Status = DigiRegQueryDword( hKey, DXB_REGVAL_LINE_SNID, &Snid, DigiRegFlagRequired, 0 );

						// Endpoints.  Number of tapi devices to expose
						if ( NT_SUCCESS( Status ) )
							Status = DigiRegQueryDword( hKey, DXB_REGVAL_ENDPOINTS, &Endpoints, DigiRegFlagRequired, 0 );

						// LineSpeed.  LinkSpeed on the wire.
						if ( NT_SUCCESS( Status ) )
							Status = DigiRegQueryDword( hKey, DXB_REGVAL_LINESPEED, &LinkSpeed, DigiRegFlagRequired, 0 );

						// DxbDevice name.  The NT device name exposed by Dxb identifying the adapter
						if ( NT_SUCCESS( Status ) )
							Status = DigiRegQuerySz( hKey, DXB_REGVAL_DEVICE, &DxbDevice, DigiRegFlagRequired, NULL );

						// ISDN Firmware Version Override.  Default is Version2 (HLC).  If TRUE, use V1 instead.
						if ( NT_SUCCESS( Status ) )
							Status = DigiRegQueryDword( hKey, DXB_REGVAL_LEGACY_API, &fVersion1, DigiRegFlagOptional, FALSE );

						DigiRegCloseKey( hKey );
					}

					DigiStrFreeUnicodeString( &FullRedirectPath );
				}

#if NT5_BUILD
				// Copy Endpoints to NT5's WanEndPoints value
				if ( NT_SUCCESS( Status ) )
					Status = DigiNdisRegWriteDword( hkAdapter, L"WanEndPoints",Endpoints );
				if ( NT_SUCCESS( Status ) )
					Status = DigiNdisRegWriteDword( hkAdapter, L"MaxWanEndPoints",Endpoints );
			}
#endif
		}

		if ( fSpoof )
		{
			// Override error, 
			PX25_ADAPTER pAdapter;
			DigiDump( DIGIWARN, ( "X25!Initialize: spoofing adapter startup!\n" ) );
			X25ConstructFoo( &pAdapter, hMiniport, &AdapterIndex, 23 );
			NdisMSetAttributes( hMiniport, pAdapter, FALSE, NdisInterfaceInternal );
			Status = STATUS_SUCCESS;
		}
		else if ( NT_ERROR( Status ) )
		{
			DigiDump( DIGIERROR, ( "X25!Initialize: *** Error reading registry params! ***\n" ) );
		}
		else
		{
			PX25_ADAPTER pAdapter;
			Status = X25ConstructAdapter( &pAdapter, hMiniport, &MyName, &AdapterIndex, &DxbDevice, Snid, Endpoints, fVersion1, LinkSpeed );
			if ( NT_SUCCESS( Status ) )
			{
#if !NT5_BUILD
				(void)X25WriteTapiDevicesString( &AdapterIndex, Endpoints );
#endif
				NdisMSetAttributes( hMiniport, pAdapter, FALSE, NdisInterfaceInternal );
				DigiDump( DIGIINIT, ( "X25!Initialize: Miniport  %s\n"
				                      "                 pAdapter  0x%x\n"
				                      "                 Snid      0x%x\n"
				                      "                 Endpoints 0x%x\n",
				                      pAdapter->Name.Buffer, pAdapter, pAdapter->Snid, pAdapter->nLines ) );
			}
		}
		
// We must *not* reference any strings alloc'd by NdisRegReadSz after closing these
// handles with NdisRegCloseKey.  Ndis frees the strings when the handle is closed.
#if NT5_BUILD
		if ( hkParams )
			DigiNdisRegCloseKey( hkParams );
#endif
		if ( hkAdapter )
			DigiNdisRegCloseKey( hkAdapter );

		// Free DigiRegQuery* strings using digi free routines
		if ( DxbDevice.Buffer )
			DigiRegFreeString( &DxbDevice );
	}

Exit:

	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIERROR, ( "X25!Initialize: returning error status 0x%x\n", Status ) );
	}
	
	return Status;
}


NDIS_STATUS
X25Send(
	IN NDIS_HANDLE      hAdapter, 
	IN NDIS_HANDLE      hCall, 
	IN PNDIS_WAN_PACKET pPacket )
/*++

Description:

	Called by NdisWan to send data.  Verify the call state and send the data.

--*/
{
	PX25_CALL const pCall = hCall;


	ASSERT( pCall->Type == X25_TYPE_CALL );
	ASSERT( pCall->pAdapter == hAdapter );

	return X25CallSend( pCall, pPacket );
}


// Homegrown macros to strip off the major object type from NDIS Object Identifiers 
// used by Set and Query Info routines.
#define OID_MAJOR_MASK				0xFF000000
#define GET_OID_MAJOR( Oid )		( Oid & OID_MAJOR_MASK )

#define OID_MAJOR_GEN				GET_OID_MAJOR( OID_GEN_SUPPORTED_LIST      )
#define OID_MAJOR_802_3				GET_OID_MAJOR( OID_802_3_PERMANENT_ADDRESS )
#define OID_MAJOR_WAN				GET_OID_MAJOR( OID_WAN_PERMANENT_ADDRESS   )
#define OID_MAJOR_TAPI				GET_OID_MAJOR( OID_TAPI_ACCEPT             )


NDIS_STATUS
X25SetInfo( 
	IN NDIS_HANDLE hAdapter,
	IN NDIS_OID    Oid,
	IN PVOID       pData,
	IN ULONG       cbData,
	OUT PULONG     pcbRead,
	OUT PULONG     pcbNeeded )
{
	PX25_ADAPTER const pAdapter = hAdapter;
	NTSTATUS Status;
	ASSERT( pAdapter->Type == X25_TYPE_ADAPTER );

	DigiDump( DIGIMP, ( "X25SetInfo, pAdapter 0x%x, Oid 0x%08x\n", pAdapter, Oid ) );

	// default return values.  Let handlers override them if necessary
	*pcbRead   = 0;
	*pcbNeeded = 0;

	switch ( GET_OID_MAJOR( Oid ) )
	{
		case OID_MAJOR_TAPI:
			Status = X25TapiSet( pAdapter, Oid, pData, cbData, pcbRead, pcbNeeded );
			break;

		case OID_MAJOR_WAN:
			Status = X25WanSet( pAdapter, Oid, pData, cbData, pcbRead, pcbNeeded );
			break;

		case OID_MAJOR_GEN:
		case OID_MAJOR_802_3:
			Status = X25LanSet( pAdapter, Oid, pData, cbData, pcbRead, pcbNeeded );
			break;

		default:
			DigiDump( DIGIERROR|DIGIBREAK, ( "X25!SetInfo: Unsupported Oid object, oid 0x%08x\n", Oid ) );
			Status = NDIS_STATUS_INVALID_OID;
			break;
	}
	return Status;
}


NDIS_STATUS
X25QueryInfo( 
	IN NDIS_HANDLE hAdapter,
	IN NDIS_OID    Oid,
	IN PVOID       pData,
	IN ULONG       cbData,
	OUT PULONG     pcbWritten,
	OUT PULONG     pcbNeeded )
{
	PX25_ADAPTER const pAdapter = hAdapter;
	NTSTATUS Status;
	ASSERT( pAdapter->Type == X25_TYPE_ADAPTER );

	DigiDump( DIGIMP, ( "X25QueryInfo, pAdapter 0x%x, Oid 0x%08x\n", pAdapter, Oid ) );


	switch ( GET_OID_MAJOR( Oid ) )
	{
		case OID_MAJOR_TAPI:
			Status = X25TapiQuery( pAdapter, Oid, pData, cbData, pcbWritten, pcbNeeded );
			break;

		case OID_MAJOR_WAN:
			Status = X25WanQuery( pAdapter, Oid, pData, cbData, pcbWritten, pcbNeeded );
			break;

		case OID_MAJOR_GEN:
		case OID_MAJOR_802_3:
			Status = X25LanQuery( pAdapter, Oid, pData, cbData, pcbWritten, pcbNeeded );
			break;

		default:
			DigiDump( DIGIBREAK, ( "X25!QueryInfo: Unsupported Oid object, oid 0x%08x\n", Oid ) );
			Status = NDIS_STATUS_INVALID_OID;
			break;
	}
	return Status;
}


BOOLEAN
X25CheckForHang( 
	IN NDIS_HANDLE hAdapter )
{
	return FALSE;
}


NDIS_STATUS
X25Reset( 
	OUT PBOOLEAN   pResetAddressing,
	IN NDIS_HANDLE hAdapter )
{
	pResetAddressing = FALSE;
	return STATUS_SUCCESS;
}
