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

   ppptapi.c

Abstract:

   Handler routines for Tapi SetInfo and QueryInfo requests.  Each request is 
   identified by its Object Identifier (OID).

	Also, routines for indicating status back up to Tapi.


--*/

#include "digippp.h"
#include "stdio.h"

#define DIGI_PPP_LINE_CALL_STATES			( LINECALLSTATE_IDLE | LINECALLSTATE_OFFERING | LINECALLSTATE_CONNECTED | LINECALLSTATE_PROCEEDING | LINECALLSTATE_DISCONNECTED )
#define DIGI_PPP_LINE_MAX_ACTIVE_CALLS		1
#define DIGI_PPP_LINE_CALL_FEATURES			( LINECALLFEATURE_ANSWER | LINECALLFEATURE_DIAL | LINECALLFEATURE_DROP )
#define DIGI_PPP_LINE_MEDIA_MODES			( LINEMEDIAMODE_DIGITALDATA )
#define DIGI_PPP_ADDRESS_ID					0
#define DIGI_PPP_MAX_RATE						2048000
#define DIGI_PPP_ADDRESS_MODES				( LINEADDRESSMODE_ADDRESSID | LINEADDRESSMODE_DIALABLEADDR )
#define DIGI_PPP_BEARER_MODES					( LINEBEARERMODE_DATA )
#define DIGI_PPP_MEDIA_MODES					( LINEMEDIAMODE_DIGITALDATA )
#define DIGI_PPP_DEVSTATUS_FLAGS				( LINEDEVSTATUSFLAGS_CONNECTED | LINEDEVSTATUSFLAGS_INSERVICE )

#define DIGI_PPP_LINE_DEV_STATES				( LINEDEVSTATE_REINIT | LINEDEVSTATE_OPEN | LINEDEVSTATE_CLOSE )
#define DIGI_PPP_LINE_ADDRESS_STATES		( 0 )


NDIS_STATUS
PPPTapiQuery(
	IN PDIGI_PPP_ADAPTER pAdapter,
	IN NDIS_OID      Oid,
	IN PVOID         pData,
	IN ULONG         cbData,
	OUT PULONG       pcbWritten,
	OUT PULONG       pcbNeeded )
/*++

Description:

	Handle all TAPI query OID requests.

--*/
{
	NDIS_STATUS Status = NDIS_STATUS_SUCCESS;	// Assume success!
	ULONG cbWritten = 0;
	ULONG cbNeeded  = 0;
//	DigiDump( DIGIOID, ( "PPP!TapiQuery: pAdapter 0x%x, Oid 0x%08x\n", pAdapter, Oid ) );

	switch ( Oid )
	{
		case OID_TAPI_GET_ADDRESS_CAPS:
		{
			PNDIS_TAPI_GET_ADDRESS_CAPS const pAddrCaps = pData;
			PLINE_ADDRESS_CAPS const pLineCaps = &pAddrCaps->LineAddressCaps;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_ADDRESS_CAPS, DeviceId 0x%x, AddressId 0x%x\n", pAddrCaps->ulDeviceID, pAddrCaps->ulAddressID ) );

			pLineCaps->ulUsedSize          = sizeof *pLineCaps;
			pLineCaps->ulLineDeviceID      = pAddrCaps->ulDeviceID;
			pLineCaps->ulCallStates        = DIGI_PPP_LINE_CALL_STATES;
			pLineCaps->ulMaxNumActiveCalls = DIGI_PPP_LINE_MAX_ACTIVE_CALLS;
			pLineCaps->ulCallFeatures      = DIGI_PPP_LINE_CALL_FEATURES;
			pLineCaps->ulAddressStates     = DIGI_PPP_LINE_ADDRESS_STATES;
			cbWritten = sizeof *pAddrCaps;

			// Fill in Address
			{
				CHAR Address[32];
				PCHAR pAddress = (PVOID)( (PCHAR)pLineCaps + sizeof *pLineCaps );
				sprintf( Address, "%s-%d-0", pAdapter->Number.Buffer, pAddrCaps->ulDeviceID - pAdapter->TapiBaseId );
				pLineCaps->ulAddressSize = strlen( Address );
				pLineCaps->ulAddressOffset = sizeof *pLineCaps;
				cbWritten += strlen( Address );
				pLineCaps->ulNeededSize = pLineCaps->ulUsedSize + strlen( Address );
				if ( pLineCaps->ulNeededSize <= pLineCaps->ulTotalSize )
				{
					pLineCaps->ulUsedSize = pLineCaps->ulNeededSize;
					strcpy( pAddress, Address );
					DigiDump( DIGITAPIOID, ( "   Address: %s\n", pAddress ) );
				}
			}
			ASSERT( cbWritten <= sizeof *pAddrCaps + pLineCaps->ulTotalSize - sizeof *pLineCaps );
			break;
		}

		case OID_TAPI_GET_ADDRESS_ID:
		{
			PNDIS_TAPI_GET_ADDRESS_ID const pGetId = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_ADDRESS_ID, hdLine 0x%x\n", pGetId->hdLine ) );
			
			if ( pGetId->ulAddressMode != LINEADDRESSMODE_DIALABLEADDR )
			{
				Status = NDIS_STATUS_TAPI_INVALADDRESS;
				break;
			}

			pGetId->ulAddressID = DIGI_PPP_ADDRESS_ID;
			cbWritten = sizeof *pGetId;
			break;
		}

		case OID_TAPI_GET_ADDRESS_STATUS:
		{
			PNDIS_TAPI_GET_ADDRESS_STATUS const pGetStatus = pData;
			PLINE_ADDRESS_STATUS const pAddrStatus = &pGetStatus->LineAddressStatus;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_ADDRESS_STATUS, hdLine 0x%x, AddressID 0x%x\n", pGetStatus->hdLine, pGetStatus->ulAddressID ) );
			pAddrStatus->ulUsedSize = sizeof *pAddrStatus;
			pAddrStatus->ulNumInUse = pAdapter->pLines[pGetStatus->hdLine].pCall ? 1 : 0;
			cbWritten = sizeof *pAddrStatus;
			ASSERT( cbWritten <= sizeof *pGetStatus + pAddrStatus->ulTotalSize - sizeof *pAddrStatus );
			break;
		}

		case OID_TAPI_GET_CALL_ADDRESS_ID:
		{
			PNDIS_TAPI_GET_CALL_ADDRESS_ID const pAddrId = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_ADDRESS_ID, hdCall 0x%x\n", pAddrId->hdCall ) );
			pAddrId->ulAddressID = DIGI_PPP_ADDRESS_ID;
			cbWritten = sizeof *pAddrId;
			break;
		}

		case OID_TAPI_GET_CALL_INFO:
		{
			PNDIS_TAPI_GET_CALL_INFO const pGetInfo = pData;
			PLINE_CALL_INFO const pCallInfo         = &pGetInfo->LineCallInfo;
			PDIGI_PPP_CALL const pCall                  = (PVOID)pGetInfo->hdCall;
			ASSERT( MmIsAddressValid( pCall ) );
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_CALL_INFO, hdCall 0x%x\n", pGetInfo->hdCall ) );

			if ( !pCall )
			{
				DigiDump( DIGIWARN, ( "PPP:Error!  OID_TAPI_GET_CALL_INFO pCall = NULL!\n" ) );
				Status = NDIS_STATUS_TAPI_INVALCALLHANDLE;
				break;
			}

			pCallInfo->ulLineDeviceID = (ULONG)pCall->hdLine + pCall->pAdapter->TapiBaseId;
			pCallInfo->ulAddressID    = DIGI_PPP_ADDRESS_ID;
			pCallInfo->ulBearerMode   = pCall->BearerMode;

			pCallInfo->ulRate         = pCall->Rate;
			pCallInfo->ulMediaMode    = pCall->MediaMode;
			pCallInfo->ulAppSpecific  = pCall->AppSpecific;
			pCallInfo->ulCallStates   = DIGI_PPP_LINE_CALL_STATES;
			pCallInfo->ulUsedSize     = sizeof *pCallInfo;
			cbWritten = sizeof *pGetInfo;
			
			//TODO Get the CALLER, CALLED and CONNECTED IDs.

			pCallInfo->ulCallerIDFlags = LINECALLPARTYID_UNAVAIL;
			pCallInfo->ulCalledIDFlags = LINECALLPARTYID_UNAVAIL;
			pCallInfo->ulConnectedIDFlags = LINECALLPARTYID_UNAVAIL;
			pCallInfo->ulRedirectionIDFlags = LINECALLPARTYID_UNAVAIL;
			pCallInfo->ulRedirectingIDFlags = LINECALLPARTYID_UNAVAIL;

			ASSERT( cbWritten <= sizeof *pGetInfo + pCallInfo->ulTotalSize - sizeof *pCallInfo );
			break;
		}

		case OID_TAPI_GET_CALL_STATUS:
		{
			PNDIS_TAPI_GET_CALL_STATUS const pGetStatus = pData;
			PLINE_CALL_STATUS const pStatus = &pGetStatus->LineCallStatus;
			PDIGI_PPP_CALL pCall = (PVOID)pGetStatus->hdCall;
			ASSERT( MmIsAddressValid( pCall ) );
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_CALL_STATUS, pCall 0x%x\n", pCall ) );
			pStatus->ulUsedSize  = sizeof *pStatus;
			pStatus->ulCallState = pCall->TapiState;
			pStatus->ulCallFeatures = PPPGetCallFeatures( pCall->TapiState );
			cbWritten = sizeof *pGetStatus;
			ASSERT( cbWritten <= sizeof *pGetStatus + pStatus->ulTotalSize - sizeof *pStatus );
			break;
		}

		case OID_TAPI_GET_DEV_CAPS:
		{
			PNDIS_TAPI_GET_DEV_CAPS const pGetCaps = pData;
			PLINE_DEV_CAPS const pCaps = &pGetCaps->LineDevCaps;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_DEV_CAPS, DeviceId 0x%x\n", pGetCaps->ulDeviceID ) );

			{
				ULONG TotalSize = pCaps->ulTotalSize;
				NdisZeroMemory( pCaps, pCaps->ulTotalSize );
				pCaps->ulTotalSize = TotalSize;
			}

			pCaps->ulUsedSize        = sizeof *pCaps;
			pCaps->ulAddressModes    = DIGI_PPP_ADDRESS_MODES;
			pCaps->ulNumAddresses    = DIGI_PPP_LINE_MAX_ACTIVE_CALLS;
			pCaps->ulBearerModes     = DIGI_PPP_BEARER_MODES;
			pCaps->ulMaxRate         = DIGI_PPP_MAX_RATE;
			pCaps->ulMediaModes      = DIGI_PPP_LINE_MEDIA_MODES;
			pCaps->ulMaxNumActiveCalls = DIGI_PPP_LINE_MAX_ACTIVE_CALLS;
			pCaps->ulPermanentLineID = pGetCaps->ulDeviceID;
			pCaps->ulLineStates      = DIGI_PPP_LINE_DEV_STATES;
			cbWritten = sizeof *pGetCaps;

			// Fill in the ProviderName
			{
				PCHAR const Media  = DIGI_PPP_PROVIDER_MEDIA;
				PCHAR const Device = DIGI_PPP_PROVIDER_DEVICE;
				PCHAR pProvider = (PVOID)( (PCHAR)pCaps + sizeof *pCaps );
				ULONG cbProvider = strlen(Media) + strlen(Device) + 3;	// 2 embedded NULLs plus 1 terminating NULL

				if ( cbData < cbWritten + cbProvider )
				{
					pCaps->ulNeededSize = cbWritten + cbProvider;
					pCaps->ulUsedSize = 0;
					break;
				}

				sprintf( pProvider, "%s%c%s%c", Media, '\0', Device, '\0' );
				cbWritten += cbProvider;
				pCaps->ulUsedSize += cbProvider;
				pCaps->ulProviderInfoSize = cbProvider;
				pCaps->ulProviderInfoOffset = sizeof *pCaps;
			}

			// Fill in the LineName
			{
				PCHAR pLine = (PVOID)( (PCHAR)pCaps + pCaps->ulUsedSize );
				ULONG cbLine;
				CHAR Temp[32];
				sprintf( Temp, "%s-Line%d", pAdapter->Name.Buffer, pGetCaps->ulDeviceID - pAdapter->TapiBaseId );
				cbLine = strlen( Temp ) + 1;
				if ( cbData < cbWritten + cbLine )
				{
					pCaps->ulNeededSize = cbWritten + cbLine;
				}
				else
				{
					strcpy( pLine, Temp );
					cbWritten += cbLine;
					pCaps->ulUsedSize += cbLine;
					pCaps->ulNeededSize = pCaps->ulUsedSize;
					pCaps->ulLineNameSize = cbLine;
					pCaps->ulLineNameOffset = sizeof *pCaps + pCaps->ulProviderInfoSize;
					pCaps->ulStringFormat = STRINGFORMAT_ASCII;
				}
			}

			ASSERT( pCaps->ulUsedSize <= pCaps->ulTotalSize );
			ASSERT( cbWritten <= sizeof *pGetCaps + pCaps->ulTotalSize - sizeof *pCaps );
			break;
		}

		case OID_TAPI_GET_DEV_CONFIG:
		{
			PNDIS_TAPI_GET_DEV_CONFIG const pGetConfig = pData;
			PVAR_STRING pConfig = &pGetConfig->DeviceConfig;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_DEV_CONFIG, DeviceId 0x%x\n", pGetConfig->ulDeviceID ) );
			if ( !_strnicmp((PCHAR)pGetConfig + pGetConfig->ulDeviceClassOffset, "tapi/line", pGetConfig->ulDeviceClassSize ) )
			{
				VAR_STRING *pDeviceConfig = &pGetConfig->DeviceConfig;
				PULONG pDest = (PVOID)( (PCHAR)pDeviceConfig + sizeof *pDeviceConfig );

				if ( pDeviceConfig->ulTotalSize < sizeof *pDeviceConfig + sizeof(ULONG) )
				{
					pDeviceConfig->ulNeededSize = sizeof *pDeviceConfig + sizeof (ULONG);
					break;
				}
				pDeviceConfig->ulStringSize = sizeof(ULONG);
				pDeviceConfig->ulUsedSize = sizeof *pDeviceConfig + pDeviceConfig->ulStringSize;
				pDeviceConfig->ulStringFormat = STRINGFORMAT_BINARY;
				pDeviceConfig->ulStringOffset = sizeof *pDeviceConfig;
				*pDest = (ULONG)pGetConfig->ulDeviceID;
				cbWritten = sizeof *pGetConfig + pDeviceConfig->ulStringSize;
			}
			else
			{
				if ( pGetConfig->ulDeviceClassOffset && pGetConfig->ulDeviceClassSize )
				{
					DigiDump( DIGIWARN, ( "   Invalid device class %s\n", (PCHAR)pGetConfig + pGetConfig->ulDeviceClassOffset ) );
				}
				else
				{
					DigiDump( DIGIWARN, ( "   Invalid device class [null]\n" ) );
				}
				Status = NDIS_STATUS_TAPI_INVALDEVICECLASS;
			}
			break;
		}

		case OID_TAPI_GET_ID:
		{
			PNDIS_TAPI_GET_ID const pGetId = pData;
			PCHAR const pszClass = (PCHAR)pGetId + pGetId->ulDeviceClassOffset;

			ASSERT( pGetId->ulDeviceClassSize );
			switch ( pGetId->ulSelect )
			{
				case LINECALLSELECT_LINE:

					if ( !_strnicmp(pszClass, "tapi/line", pGetId->ulDeviceClassSize ) )
					{
						VAR_STRING *pDeviceId = &pGetId->DeviceID;
						PULONG pDest = (PVOID)( (PCHAR)pDeviceId + sizeof *pDeviceId );
						DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_ID, LINECALLSELECT_LINE, hdLine 0x%x\n", pGetId->hdLine ) );

						if ( pDeviceId->ulTotalSize < sizeof *pDeviceId + sizeof( ULONG ) )
						{
							Status = NDIS_STATUS_TAPI_RESOURCEUNAVAIL;
							break;
						}
						pDeviceId->ulStringSize = sizeof(ULONG);
						pDeviceId->ulUsedSize = sizeof *pDeviceId + pDeviceId->ulStringSize;
						pDeviceId->ulStringFormat = STRINGFORMAT_BINARY;
						pDeviceId->ulStringOffset = sizeof *pDeviceId;
						*pDest = (ULONG)pGetId->hdLine + pAdapter->TapiBaseId;
						cbWritten = sizeof *pGetId + pDeviceId->ulStringSize;
						ASSERT( cbWritten <= sizeof *pGetId + pDeviceId->ulTotalSize - sizeof *pDeviceId );
						break;
					}
					Status = NDIS_STATUS_TAPI_INVALDEVICECLASS;
					break;

				case LINECALLSELECT_CALL:

					if ( !_strnicmp(pszClass, "ndis", pGetId->ulDeviceClassSize ) )
					{
						VAR_STRING *pDeviceId = &pGetId->DeviceID;
						PULONG pDest = (PVOID)( (PCHAR)pDeviceId + sizeof *pDeviceId );
						PDIGI_PPP_CALL const pCall = (PVOID)pGetId->hdCall;
						ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
						DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_ID, LINECALLSELECT_CALL, pCall 0x%x\n", pCall ) );

						if ( pDeviceId->ulTotalSize < sizeof *pDeviceId + sizeof(ULONG) )
						{
							Status = NDIS_STATUS_TAPI_RESOURCEUNAVAIL;
							break;
						}
						pDeviceId->ulStringSize = sizeof(ULONG);
						pDeviceId->ulUsedSize = sizeof *pDeviceId + pDeviceId->ulStringSize;
						pDeviceId->ulStringFormat = STRINGFORMAT_BINARY;
						pDeviceId->ulStringOffset = sizeof *pDeviceId;
						*pDest = (ULONG)pCall->htCall;
						cbWritten = sizeof *pGetId + pDeviceId->ulStringSize;
						ASSERT( cbWritten <= sizeof *pGetId + pDeviceId->ulTotalSize - sizeof *pDeviceId );
						break;
					}
					Status = NDIS_STATUS_TAPI_INVALDEVICECLASS;
					break;

				default:

					Status = NDIS_STATUS_TAPI_INVALDEVICECLASS;
					break;
			}
			break;
		}

		case OID_TAPI_GET_LINE_DEV_STATUS:
		{
			PNDIS_TAPI_GET_LINE_DEV_STATUS const pGetStatus = pData;
			PLINE_DEV_STATUS const pDevStatus = &pGetStatus->LineDevStatus;
			PDIGI_PPP_CALL const pCall = pAdapter->pLines[pGetStatus->hdLine].pCall;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_GET_LINE_DEV_STATUS, hdLine 0x%x, pCall 0x%x\n", pGetStatus->hdLine, pCall ) );
			pDevStatus->ulUsedSize = sizeof *pDevStatus;
			pDevStatus->ulNumOpens = pCall != NULL ? 1 : 0;
			pDevStatus->ulDevStatusFlags = DIGI_PPP_DEVSTATUS_FLAGS;
			cbWritten = sizeof *pGetStatus;
			break;
		}

		case OID_TAPI_MAKE_CALL:
		{
			PNDIS_TAPI_MAKE_CALL const pMakeCall = pData;
			PLINE_CALL_PARAMS const pParams = &pMakeCall->LineCallParams;
			PDIGI_PPP_CALL pCall;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_MAKE_CALL, pAdapter 0x%x, hdLine 0x%x, htCall 0x%x\n", pAdapter, pMakeCall->hdLine, pMakeCall->htCall ) );
			ASSERT( !pMakeCall->bUseDefaultLineCallParams );
			
			Status = PPPCallDial( pAdapter, pMakeCall->hdLine, pMakeCall->htCall,
						pParams->ulMediaMode, pParams->ulBearerMode,
						(PCHAR)pMakeCall + pMakeCall->ulDestAddressOffset,
						pMakeCall->ulDestAddressSize, &pCall );
			
		   
			if ( !NT_ERROR( Status ) )
				pMakeCall->hdCall = (ULONG_PTR)pCall;

			cbWritten = sizeof *pMakeCall;
			break;
		}

		case OID_TAPI_OPEN:
		{
			PNDIS_TAPI_OPEN const pOpen = pData;
			ULONG const iLine           = pOpen->ulDeviceID - pAdapter->TapiBaseId;
			PDIGI_PPP_LINE const pLine = &pAdapter->pLines[iLine];

			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_OPEN, pAdapter 0x%x, htLine 0x%x, DeviceId 0x%x\n", pAdapter, pOpen->htLine, pOpen->ulDeviceID ) );
			ASSERT( pOpen->ulDeviceID >= pAdapter->TapiBaseId );
			ASSERT( pOpen->ulDeviceID - pAdapter->TapiBaseId < pAdapter->nLines );

			pLine->Listen = 0;
			pLine->Closing = 0;
			pLine->htLine = pOpen->htLine;
			pOpen->hdLine = iLine;
			cbWritten = sizeof *pOpen;
			break;
		}

		case OID_TAPI_PROVIDER_INITIALIZE:
		{
			ULONG iLine;

			PNDIS_TAPI_PROVIDER_INITIALIZE const pInit = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_PROVIDER_INITIALIZE, pAdapter 0x%x, TapiBaseId 0x%x\n", pAdapter, pInit->ulDeviceIDBase ) );

			for ( iLine = 0; iLine < pAdapter->nLines; iLine++ )
			{
				PDIGI_PPP_LINE pLine = &pAdapter->pLines[iLine];
				pLine->htLine  = 0;
				pLine->Listen  = 0;
				pLine->Closing = 0;
			}

			pAdapter->TapiBaseId = pInit->ulDeviceIDBase;
			pInit->ulNumLineDevs = pAdapter->nLines;
			pInit->ulProviderID  = (ULONG_PTR)pAdapter;
			cbWritten = sizeof *pInit;
			break;
		}

#if 0
		case OID_TAPI_NEGOTIATE_EXT_VERSION:
		{
			PNDIS_TAPI_NEGOTIATE_EXT_VERSION const pVer = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_NEGOTIATE_EXT_VERSION, pAdapter 0x%x, TapiBaseId 0x%x, LowVersion 0x%x HighVersion 0x%x\n", pAdapter, pVer->ulDeviceID, pVer->ulLowVersion, pVer->ulHighVersion ) );
			if ( pVer->ulLowVersion == 0 )
				pVer->ulExtVersion = 0;
			else
				Status = NDIS_STATUS_TAPI_INCOMPATIBLEEXTVERSION;

			cbWritten = sizeof *pVer;
			break;
		}
#endif

		default:
			DigiDump( DIGIWARN, ( "PPP!TapiQuery: Unsupported Oid 0x%08x on pAdapter 0x%x\n", Oid, pAdapter ) );
			Status = NDIS_STATUS_TAPI_OPERATIONUNAVAIL;
			break;
	}

	ASSERT( cbWritten <= cbData );
	*pcbWritten = cbWritten;
	*pcbNeeded  = cbNeeded;
	return Status;
}


NDIS_STATUS
PPPTapiSet(
	IN PDIGI_PPP_ADAPTER pAdapter,
	IN NDIS_OID      Oid,
	IN PVOID         pData,
	IN ULONG         cbData,
	OUT PULONG       pcbRead,
	OUT PULONG       pcbNeeded )
/*++

Description:

	Handle all TAPI Set OID requests.

--*/
{
	NDIS_STATUS Status = NDIS_STATUS_SUCCESS;	// Assume success!
	ULONG cbRead = 0;
	ULONG cbNeeded  = 0;
	DigiDump( DIGIOID, ( "PPP!TapiSet: pAdapter 0x%x, Oid 0x%08x\n", pAdapter, Oid ) );

	switch ( Oid )
	{
		case OID_TAPI_ANSWER:
		{
			PNDIS_TAPI_ANSWER const pAnswer = pData;
			PDIGI_PPP_CALL const pCall = (PVOID)pAnswer->hdCall;
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_ANSWER, pCall 0x%x\n", pCall ) );
			Status = PPPCallAnswer( pCall );
			cbRead = sizeof *pAnswer;
			break;
		}

		case OID_TAPI_CLOSE:
		{
			PNDIS_TAPI_CLOSE const pClose = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_CLOSE, pAdapter 0x%x, hdLine 0x%x\n", pAdapter, pClose->hdLine ) );

			pAdapter->pLines[pClose->hdLine].htLine = 0;
			pAdapter->pLines[pClose->hdLine].Listen = FALSE;

			if ( pAdapter->pLines[pClose->hdLine].pCall )
				Status = PPPCallClose( pAdapter->pLines[pClose->hdLine].pCall );
			else
				Status = NDIS_STATUS_SUCCESS;

			cbRead = sizeof *pClose;
			break;
		}

		case OID_TAPI_CLOSE_CALL:
		{
			PNDIS_TAPI_CLOSE_CALL const pCloseCall = pData;
			PDIGI_PPP_CALL const pCall = (PVOID)pCloseCall->hdCall;
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_CLOSE_CALL, pCall 0x%x\n", pCall ) );
			Status = PPPCallCloseCall( pCall );
			cbRead = sizeof *pCloseCall;
			break;
		}

		case OID_TAPI_DROP:
		{
			PNDIS_TAPI_DROP const pDrop = pData;
			PDIGI_PPP_CALL const pCall = (PVOID)pDrop->hdCall;
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_DROP, pCall 0x%x\n", pCall ) );
			Status = PPPCallDrop( pCall );
			cbRead = sizeof *pDrop;
			break;
		}

		case OID_TAPI_PROVIDER_SHUTDOWN:
		{
			PNDIS_TAPI_PROVIDER_SHUTDOWN const pShutdown = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_PROVIDER_SHUTDOWN, pAdapter 0x%x\n", pAdapter ) );

			{
				ULONG i;
				for ( i = 0; i < pAdapter->nLines; i++ )
				{
					if ( pAdapter->pLines[i].pCall )
					{
						DigiDump( DIGIWARN, ( "PPP: Non-null pCall on line %d!\n", i ) );
					}
				}
			}
			cbRead = sizeof *pShutdown;
			break;
		}

		case OID_TAPI_SET_APP_SPECIFIC:
		{
			PNDIS_TAPI_SET_APP_SPECIFIC const pApp = pData;
			PDIGI_PPP_CALL const pCall = (PVOID)pApp->hdCall;
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_SET_APP_SPECIFIC, pCall 0x%x\n", pCall ) );
			pCall->AppSpecific = pApp->ulAppSpecific;
			cbRead = sizeof *pApp;
			break;
		}

		case OID_TAPI_SET_CALL_PARAMS:
		{
			PNDIS_TAPI_SET_CALL_PARAMS const pSet = pData;
			PDIGI_PPP_CALL const pCall = (PVOID)pSet->hdCall;
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_SET_CALL_PARAMS, pCall 0x%x, BearerMode 0x%x, Min %d, Max %d\n", pCall, pSet->ulBearerMode, pSet->ulMinRate, pSet->ulMaxRate ) );
			DigiDump( DIGIERROR, ( "PPP: Unsupported Oid, OID_TAPI_SET_CALL_PARAMS!\n" ) );
			if( pCall->TapiState & LINECALLSTATE_IDLE )
					{
						Status = NDIS_STATUS_TAPI_INVALCALLSTATE;
						break;
					}
			else if ( pSet->ulBearerMode & DIGI_PPP_BEARER_MODES )
		  		{
					if( pSet->ulMaxRate >= pSet->ulMinRate && DIGI_PPP_MAX_RATE >= pSet->ulMaxRate )
						Status = NDIS_STATUS_SUCCESS;
					else
						Status = NDIS_STATUS_TAPI_INVALRATE;
				}
			else
				Status = NDIS_STATUS_TAPI_INVALBEARERMODE;
			cbRead = sizeof *pSet;
			break;
		}

		case OID_TAPI_SET_DEFAULT_MEDIA_DETECTION:
		{
			PNDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION const pSet = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_SET_DEFAULT_MEDIA_DETECTION, pAdapter 0x%x, hdLine 0x%x, MediaModes 0x%x\n", pAdapter, pSet->hdLine, pSet->ulMediaModes ) );
			if ( pSet->ulMediaModes & ~DIGI_PPP_MEDIA_MODES )
				Status = NDIS_STATUS_TAPI_INVALMEDIAMODE;
			else
				pAdapter->pLines[pSet->hdLine].Listen = TRUE;
			cbRead = sizeof *pSet;
			break;
		}

		case OID_TAPI_SET_MEDIA_MODE:
		{
			PNDIS_TAPI_SET_MEDIA_MODE const pSet = pData;
			PDIGI_PPP_CALL const pCall = (PVOID)pSet->hdCall;
			ASSERT( pCall->Type == DIGI_PPP_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_SET_MEDIA_MODE, pCall 0x%x, MediaMode 0x%x\n", pCall, pSet->ulMediaMode ) );
			DigiDump( DIGIERROR, ( "PPP: OID_TAPI_SET_MEDIA_MODE, cannot modify the MediaMode\n" ) );
			if ( pSet->ulMediaMode & DIGI_PPP_MEDIA_MODES )
				Status = NDIS_STATUS_SUCCESS;
			else
				Status = NDIS_STATUS_TAPI_INVALMEDIAMODE;
			
			cbRead = sizeof *pSet;
			break;
		}

		case OID_TAPI_SET_STATUS_MESSAGES:
		{
			PNDIS_TAPI_SET_STATUS_MESSAGES const pSet = pData;
			DigiDump( DIGITAPIOID, ( "PPP: OID_TAPI_SET_STATUS_MESSAGES, pAdapter 0x%x, hdLine 0x%x, LineStates 0x%x, AddrStates 0x%x\n", pAdapter, pSet->hdLine, pSet->ulLineStates, pSet->ulAddressStates ) );
			if ( pSet->ulLineStates & ~DIGI_PPP_LINE_DEV_STATES )
				Status = NDIS_STATUS_TAPI_INVALLINESTATE;
			if ( pSet->ulAddressStates & ~DIGI_PPP_LINE_ADDRESS_STATES )
				Status = NDIS_STATUS_TAPI_INVALADDRESSSTATE;
			cbRead = sizeof *pSet;
			break;
		}

		default:
			DigiDump( DIGIWARN, ( "PPP!TapiSet: Unsupported Oid 0x%08x on pAdapter 0x%x\n", Oid, pAdapter ) );
			Status = NDIS_STATUS_TAPI_OPERATIONUNAVAIL;
			break;
	}

	ASSERT( cbRead <= cbData );
	*pcbRead   = cbRead;
	*pcbNeeded = cbNeeded;
	return Status;
}


VOID
PPPTapiIndicate(
	IN PDIGI_PPP_CALL pCall, 
	IN HTAPI_LINE htLine, 
	IN ULONG      ulMsg, 
	IN ULONG_PTR  Param1, 
	IN ULONG_PTR  Param2, 
	IN ULONG_PTR  Param3 ) 
/*++

Description:

	Send a Tapi indication up through Ndis.  

--*/
{
	NDIS_TAPI_EVENT Event;
	DigiDump( DIGITAPIEVENT, ( "PPP!TapiIndicate: pAdapter 0x%x, pCall 0x%x, htLine 0x%x, htCall 0x%x,\n"
	                           "                   ulMsg 0x%x, Param1 0x%x, Param2 0x%x, Param3 0x%x\n", 
	                           pCall->pAdapter, pCall, htLine, pCall->htCall, ulMsg, Param1, Param2, Param3 ) );

	Event.htLine   = htLine;
	Event.htCall   = pCall->htCall;
	Event.ulMsg    = ulMsg;
	Event.ulParam1 = Param1;
	Event.ulParam2 = Param2;
	Event.ulParam3 = Param3;

	NdisMIndicateStatus( pCall->pAdapter->hMiniport, NDIS_STATUS_TAPI_INDICATION, &Event, sizeof Event );

	if ( ulMsg == LINE_NEWCALL )
	{
		DigiDump( DIGITAPIEVENT, ( "PPP!TapiIndicate: LINE_NEWCALL, returned htCall 0x%x\n", Event.ulParam2 ) );
		pCall->htCall = Event.ulParam2;
	}
	else if ( ulMsg == LINE_CALLSTATE )
	{
		pCall->TapiState = (ULONG)Param1;
	}
}


VOID
PPPTapiSetComplete(
	IN PDIGI_PPP_ADAPTER pAdapter,
	IN NDIS_STATUS   Status )
/*++

Description:

	Notify Tapi that a previous SetInfo request has completed.

--*/
{
	DigiDump( DIGITAPIOID, ( "PPP!TapiSetComplete: SetInfo request completed, pAdapter 0x%x, Status 0x%x\n", pAdapter, Status ) );
	NdisMSetInformationComplete( pAdapter->hMiniport, Status );
}


VOID
PPPTapiQueryComplete(
	IN PDIGI_PPP_ADAPTER pAdapter,
	IN NDIS_STATUS   Status )
/*++

Description:

	Notify Tapi that a previous QueryInfo request has completed.

--*/
{
	DigiDump( DIGITAPIOID, ( "PPP!TapiQueryComplete: QueryInfo request completed, pAdapter 0x%x, Status 0x%x\n", pAdapter, Status ) );
	NdisMQueryInformationComplete( pAdapter->hMiniport, Status );
}

ULONG PPPGetCallFeatures( IN ULONG Tapi_State )
{
	ULONG CallFeatures;
	switch( Tapi_State )
	{
		case LINECALLSTATE_IDLE:
		{
			CallFeatures =	0;
			break; 
		}
		case LINECALLSTATE_OFFERING:
		{
			CallFeatures = LINECALLFEATURE_ANSWER | LINECALLFEATURE_DROP;
			break; 
		}
		case LINECALLSTATE_ACCEPTED:
		{
			CallFeatures = LINECALLFEATURE_ANSWER | LINECALLFEATURE_DROP;
			break; 
		}
		case LINECALLSTATE_DIALING:
		{
			CallFeatures = LINECALLFEATURE_DROP;
			break; 
		}
		case LINECALLSTATE_CONNECTED:
		{
			CallFeatures =	LINECALLFEATURE_DROP;
			break; 
		}
		case LINECALLSTATE_PROCEEDING:
		{
			CallFeatures = LINECALLFEATURE_DROP;
			break; 
		}
		case LINECALLSTATE_DISCONNECTED:
		{
			CallFeatures =	LINECALLFEATURE_DROP;
			break; 
		}
	}
		return CallFeatures;
}