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

   pppmp.c

Abstract:

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

--*/

#include "digippp.h"

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


#if NT5_BUILD
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

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


NDIS_STATUS
DigiNdisRegReadDword( 
	IN NDIS_HANDLE hKey,
	IN PCWSTR      pValue,
	OUT PULONG     pDword )
{
	NDIS_STATUS Status;
	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, ( "PPP: Registry DWORD read failed, value %S\n", Value.Buffer ) );
	}
	return Status;
}


VOID pppShutdown(PVOID pAdapter)
{
#if DBG
	DbgPrint("DigiPPP:pppShutdown called context 0x%x \n",pAdapter);
#endif   
	PPPCallMgrThreadStop( pAdapter );
}


STATIC INLINE NDIS_STATUS
DigiNdisRegReadSz( 
	IN NDIS_HANDLE   hKey,
	IN PCWSTR        pValue,
	OUT PNDIS_STRING pString )
{
	NDIS_STATUS Status;
	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, ( "PPP: 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;
	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, ( "PPP: Registry MULTI_SZ read failed, value %S\n", Value.Buffer ) );
	}
	return Status;
}


STATIC VOID
PPPDestructAdapter( 
	IN PDIGI_PPP_ADAPTER pAdapter )
{
	if ( pAdapter != NULL )
    {
		if ( pAdapter->hDevice != NULL )
		{
			PPPCallMgrThreadStop( pAdapter );	
			PPPQueueStop( pAdapter );
			(void)DsCloseDevice( pAdapter->hDevice );
		}

		if (pAdapter->pLines)
		{
			ULONG i;
			for ( i = 0; i < pAdapter->nLines; i++ )
				PPPCircuitDestruct( &pAdapter->pLines[i] );
			DigiFreeMem( pAdapter->pLines );
		}
		DigiFreeMem( pAdapter );
	}
}


STATIC NDIS_STATUS
PPPConstructAdapter( 
	IN PDIGI_PPP_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 BOOLEAN           IsCsuLine,
	IN ULONG			      LineSpeed,
	IN ULONG			      Clocking,
	IN PUNICODE_STRING	pLineName,
	IN BOOLEAN           IsNrzi,
	IN ULONG             TimeslotMask,
	IN ULONG             TimeslotSpeed )
{
	NDIS_STATUS Status = NDIS_STATUS_RESOURCES;
	ANSI_STRING	ansiString;
	ULONG iLine;

	PDIGI_PPP_ADAPTER pAdapter = DigiAllocMem( NonPagedPool, sizeof *pAdapter );
	if ( !pAdapter )
		goto Exit;

	NdisZeroMemory( pAdapter, sizeof *pAdapter );
	pAdapter->Type = DIGI_PPP_TYPE_ADAPTER;					// Parag - TODO

	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->MaxFrameSize = DIGI_PPP_MAX_FRAME_SIZE - 10;

	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 = DsOpenDevice( pDxbDevice, &pAdapter->hDevice );
	if ( NT_ERROR(Status) )
	{
		DigiDump( DIGIERROR, ("PPP!ConstructAdapter: Error opening device %S, Status 0x%x\n", pDxbDevice->Buffer, Status) );
		goto Exit;
	}

	pAdapter->IsCsuLine = IsCsuLine;
	if ( IsCsuLine )
	{
		ULONG Bit;
		strcpy( pAdapter->LineName, "IWAN" );
		for ( Bit = 1, iLine = 0; Bit < 0x40000000; Bit <<= 1 )
		{
			ULONG Timeslot = TimeslotMask & Bit;
			if ( Timeslot )
			{
				ULONG Speed = TimeslotSpeed & Bit ? 56000 : 64000;
				Status = PPPCircuitConstruct( pAdapter, iLine, Timeslot, Speed );
				iLine++;
			}
		}

		ASSERT( iLine == Endpoints );
	}
	else
	{
		pAdapter->Sync.LineSpeed = LineSpeed;
		pAdapter->Sync.ExternalClocking  = Clocking;
		pAdapter->Sync.IsNrzi = IsNrzi;

		ansiString.Buffer = pAdapter->LineName;
		ansiString.Length = 0;
		ansiString.MaximumLength = sizeof pAdapter->LineName;
		RtlUnicodeStringToAnsiString(&ansiString, pLineName, FALSE);

		for ( iLine = 0; iLine < pAdapter->nLines && NT_SUCCESS(Status); iLine++ )
			Status = PPPCircuitConstruct( pAdapter, iLine, 0, LineSpeed );
	}

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

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

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

Exit:

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

	return Status;
}


STATIC VOID
PPPStripTapiDevices( 
	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
PPPAddTapiDevices( 
	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
PPPWriteTapiDevicesString( 
	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( DIGI_PPP_PROVIDER_DEVICE ) );

	// Write Media Type Value
	if ( NT_SUCCESS( Status ) )
		Status = DigiRegWriteSz( hKeyDriver, L"Media Type", TEXT( DIGI_PPP_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
	PPPStripTapiDevices( &PreviousAddress, &Address, pAdapterIndex );

	// Add all endpoints for this adapter instance
	PPPAddTapiDevices( &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
PPPHalt(
	IN NDIS_HANDLE hAdapter )
/*++

Description:

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

--*/
{
	PDIGI_PPP_ADAPTER const pAdapter = hAdapter;
	DigiDump( DIGIMP, ( "PPP!PPPHalt: hAdapter 0x%08x\n", hAdapter ) );

	PPPDestructAdapter( pAdapter );
}


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

Description:

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

--*/
{
	NDIS_STATUS Status;
	ULONG i;
	PDIGI_PPP_ADAPTER pAdapter;

	DigiDump( DIGIINIT|DIGIMP, ( "PPP!PPPInitialize: hMiniport 0x%08x\n", hMiniport ) );

	for ( i = 0; i < ArraySize; i++ )
		if ( pMediumArray[i] == NdisMediumWan )
			break;

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

	*pArrayIndex = i;

	// Get Configuration Params from registry
	{
		NDIS_HANDLE hNdisKey = NULL;
		HANDLE hkParams = NULL;
		UNICODE_STRING MyName;
		ULONG Snid;
		ULONG Endpoints;
		ULONG IsCsuLine = FALSE;
		ULONG Clocking;
		ULONG LineSpeed;
		ULONG IsNrzi;
		ULONG TimeslotMask;
		ULONG TimeslotSpeed;
		UNICODE_STRING DxbDevice = {0};
		UNICODE_STRING AdapterIndex = {0};
		UNICODE_STRING LineName = {0};
		ULONG fVersion1;

		Status = DigiNdisRegOpenKey( &hNdisKey, hRegKey );
		if ( NT_SUCCESS( Status ) )
		{
			UNICODE_STRING RegPath;
			UNICODE_STRING FullRedirectPath;

#if NT5_BUILD
			Status = DigiNdisRegOpenSubkey( &hkParams, hNdisKey, DXB_REGKEY_PARAMETERS );
			if ( NT_SUCCESS( Status ) )
			{
#else
				hkParams = hNdisKey;
#endif

				// Ndis Adapter Name, e.g. DigiPPP1001
				Status = DigiNdisRegReadSz( hkParams, DXB_REGVAL_SERVICENAME, &MyName );
				if ( NT_SUCCESS( Status ) )
				{
					DigiDump( DIGIINIT, ( "PPP!Initialize: Miniport Adapter %S\n", MyName.Buffer ) );
				}

				// NCPA Adapter number
				if ( NT_SUCCESS(Status) )
					Status = DigiNdisRegReadSz( hkParams, DXB_REGVAL_NDIS_ADAPTER_INSTANCE, &AdapterIndex );

				// 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 ( NT_SUCCESS( Status ) )
				{
					Status = DigiStrAllocUnicodeString( &FullRedirectPath, DXB_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 );

							// 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 );

							if ( NT_SUCCESS(Status) )
								Status = DigiRegQueryDword( hKey, PPP_REGVAL_IS_CSU, &IsCsuLine, DigiRegFlagOptional, FALSE );

							if ( NT_SUCCESS(Status) )
							{
								if ( IsCsuLine )
								{
									// CSU Line.  Need to know what timeslots to use, and at what
									// speed to run each timeslot: 64K or 56K.  
									Status = DigiRegQueryDword( hKey, DXB_REGVAL_CSU_FRACT1_TS_MASK, &TimeslotMask, DigiRegFlagOptional, DXB_REGVAL_CSU_FRACT1_TS_MASK_DEFAULT );
									if ( NT_SUCCESS( Status ) )
										Status = DigiRegQueryDword( hKey, DXB_REGVAL_CSU_FRACT1_SPD_MASK, &TimeslotSpeed, DigiRegFlagOptional, DXB_REGVAL_CSU_FRACT1_SPD_MASK_DEFAULT );
								}
								else
								{
									// Regular sync line, not a CSU card.  Get extra sync line params
									Status = DigiRegQuerySz( hKey, DXB_REGVAL_LINENAME, &LineName, DigiRegFlagRequired, NULL );

									// Clocking - Internal/External.  0==internal.
									if ( NT_SUCCESS( Status ) )
										Status = DigiRegQueryDword( hKey, PPP_REGVAL_CLOCKING, &Clocking, DigiRegFlagRequired, 0 );

									if ( NT_SUCCESS( Status ) )
										Status = DigiRegQueryDword( hKey, PPP_REGVAL_LINESPEED, &LineSpeed, DigiRegFlagRequired, 0 );

									// NRZ is really a boolean where 1==NRZI, 0==NRZ.
									if ( NT_SUCCESS( Status ) )
										Status = DigiRegQueryDword( hKey, PPP_REGVAL_NRZ, &IsNrzi, DigiRegFlagRequired, 0 );
								}
							}

							DigiRegCloseKey( hKey );
						}

						DigiStrFreeUnicodeString( &FullRedirectPath );
					}
				}
#if NT5_BUILD
				else
				{
					// If no RegPath is available we are spoofing
					Status = STATUS_SUCCESS;
				}
			}
#endif
		}

		if ( NT_SUCCESS( Status ))
		{
			Status = PPPConstructAdapter( &pAdapter, hMiniport, &MyName, &AdapterIndex, 
			                              &DxbDevice, Snid, Endpoints, (BOOLEAN)IsCsuLine, 
			                              LineSpeed, Clocking, &LineName, (BOOLEAN)IsNrzi,
			                              TimeslotMask, TimeslotSpeed );
			if ( NT_SUCCESS( Status ) )
			{
#if !NT5_BUILD
				(void)PPPWriteTapiDevicesString( &AdapterIndex, Endpoints );
#endif
				NdisMSetAttributesEx( hMiniport, pAdapter, 0, NDIS_ATTRIBUTE_IGNORE_PACKET_TIMEOUT | NDIS_ATTRIBUTE_IGNORE_REQUEST_TIMEOUT, NdisInterfaceInternal );
				DigiDump( DIGIINIT, ( "PPP!Initialize: Miniport  %s\n"
				                      "                 pAdapter  0x%x\n"
				                      "                 Snid      0x%x\n"
				                      "                 Endpoints 0x%x\n",
				                      pAdapter->Name.Buffer, pAdapter, pAdapter->Snid, pAdapter->nLines ) );
			}

			NdisMRegisterAdapterShutdownHandler(hMiniport,(PVOID) pAdapter, pppShutdown);
		}
		
		if ( DxbDevice.Buffer )
			DigiRegFreeString( &DxbDevice );
		if ( LineName.Buffer )
			DigiRegFreeString( &LineName );

#if NT5_BUILD
		if ( hkParams )
			DigiNdisRegCloseKey( hkParams );
#endif
		if ( hNdisKey )
			DigiNdisRegCloseKey( hNdisKey );
	}

Exit:
	return Status;
}


NDIS_STATUS
PPPSend(
	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.

--*/
{
	PDIGI_PPP_CALL const pCall = hCall;

#if 0
	DigiDump( DIGIMP, 
	          ("PPP!PPPSend: hAdapter 0x%08x, hCall 0x%08x, pPacket 0x%08x, Length 0x%x\n",
	          hAdapter, hCall, pPacket, pPacket->CurrentLength ) );
#endif

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

	return PPPCallSend( 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
PPPSetInfo( 
	IN NDIS_HANDLE hAdapter,
	IN NDIS_OID    Oid,
	IN PVOID       pData,
	IN ULONG       cbData,
	OUT PULONG     pcbRead,
	OUT PULONG     pcbNeeded )
{
	PDIGI_PPP_ADAPTER const pAdapter = hAdapter;
	NTSTATUS Status;
	ASSERT( pAdapter->Type == DIGI_PPP_TYPE_ADAPTER );

//	DigiDump( DIGIMP, ( "PPPSetInfo, 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 = PPPTapiSet( pAdapter, Oid, pData, cbData, pcbRead, pcbNeeded );
			break;

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

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

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


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

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

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

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

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

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