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

   x25tapi.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.

Author:

   Michael Zarns

--*/

#include "digix25.h"
#include "stdio.h"

#define X25_LINE_CALL_STATES					( LINECALLSTATE_IDLE | LINECALLSTATE_OFFERING | LINECALLSTATE_CONNECTED | LINECALLSTATE_DISCONNECTED )
#define X25_LINE_MAX_ACTIVE_CALLS			1
#define X25_LINE_CALL_FEATURES				( LINECALLFEATURE_ANSWER | LINECALLFEATURE_DIAL | LINECALLFEATURE_DROP )
#define X25_LINE_MEDIA_MODES					( LINEMEDIAMODE_DIGITALDATA )
#define X25_ADDRESS_ID							0
#define X25_MAX_RATE							2048000
#define X25_ADDRESS_MODES						 LINEADDRESSMODE_ADDRESSID 
#define X25_BEARER_MODES						LINEBEARERMODE_DATA 
#define X25_MEDIA_MODES						( LINEMEDIAMODE_DIGITALDATA )
#define X25_DEVSTATUS_FLAGS					( LINEDEVSTATUSFLAGS_CONNECTED | LINEDEVSTATUSFLAGS_INSERVICE )

#define X25_DEFAULT_MEDIA_MODE				( LINEMEDIAMODE_DIGITALDATA )
#define X25_DEFAULT_BEARER_MODE				( LINEBEARERMODE_DATA )
#define X25_DEFAULT_MIN_RATE					( 0 )
#define X25_DEFAULT_MAX_RATE					( 2048000 )

#define X25_LINE_DEV_STATES					( LINEDEVSTATE_REINIT | LINEDEVSTATE_OPEN | LINEDEVSTATE_CLOSE )
#define X25_LINE_DEV_STATES_ALL				( LINEDEVSTATE_OTHER | LINEDEVSTATE_RINGING | LINEDEVSTATE_CONNECTED | LINEDEVSTATE_DISCONNECTED | LINEDEVSTATE_MSGWAITON | LINEDEVSTATE_MSGWAITOFF | LINEDEVSTATE_INSERVICE | LINEDEVSTATE_OUTOFSERVICE | LINEDEVSTATE_MAINTENANCE | LINEDEVSTATE_OPEN | LINEDEVSTATE_CLOSE | LINEDEVSTATE_NUMCALLS | LINEDEVSTATE_NUMCOMPLETIONS | LINEDEVSTATE_TERMINALS | LINEDEVSTATE_ROAMMODE | LINEDEVSTATE_BATTERY | LINEDEVSTATE_SIGNAL | LINEDEVSTATE_DEVSPECIFIC | LINEDEVSTATE_REINIT | LINEDEVSTATE_LOCK )

#define X25_LINE_ADDRESS_STATES				( 0 )
#define X25_LINE_ADDRESS_STATES_ALL		( LINEADDRESSSTATE_OTHER | LINEADDRESSSTATE_DEVSPECIFIC | LINEADDRESSSTATE_INUSEZERO | LINEADDRESSSTATE_INUSEONE | LINEADDRESSSTATE_INUSEMANY | LINEADDRESSSTATE_NUMCALLS | LINEADDRESSSTATE_FORWARD | LINEADDRESSSTATE_TERMINALS )


NDIS_STATUS
X25TapiQuery(
	IN PX25_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( DIGITAPIOID, ( "DigiX25!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, ( "DigiX25: 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        = X25_LINE_CALL_STATES;
			pLineCaps->ulMaxNumActiveCalls = X25_LINE_MAX_ACTIVE_CALLS;
			pLineCaps->ulCallFeatures      = X25_LINE_CALL_FEATURES;
			pLineCaps->ulAddressStates     = X25_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, ( "DigiX25: OID_TAPI_GET_ADDRESS_ID, hdLine 0x%x\n", pGetId->hdLine ) );
			
			if ( pGetId->ulAddressMode != LINEADDRESSMODE_DIALABLEADDR )
			{
				Status = NDIS_STATUS_TAPI_INVALADDRESS;
				break;
			}

			pGetId->ulAddressID = X25_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, ( "DigiX25: 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, ( "DigiX25: OID_TAPI_GET_ADDRESS_ID, hdCall 0x%x\n", pAddrId->hdCall ) );
			pAddrId->ulAddressID = X25_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;
			PX25_CALL const pCall                  = (PVOID)pGetInfo->hdCall;
			ASSERT( MmIsAddressValid( pCall ) );
			ASSERT( pCall->Type == X25_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_GET_CALL_INFO, hdCall 0x%x\n", pGetInfo->hdCall ) );

			if ( !pCall )
			{
				DigiDump( DIGIWARN, ( "DigiX25: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    = X25_ADDRESS_ID;
			pCallInfo->ulBearerMode   = pCall->BearerMode;
			pCallInfo->ulMediaMode    = pCall->MediaMode;
			pCallInfo->ulAppSpecific  = pCall->AppSpecific;
			pCallInfo->ulCallStates   = X25_LINE_CALL_STATES;
			pCallInfo->ulUsedSize     = sizeof *pCallInfo;
			if( pCall->LocalLinkSpeed < pCall->RemoteLinkSpeed )
				pCallInfo->ulRate = pCall->LocalLinkSpeed;
		   else
      			pCallInfo->ulRate = pCall->RemoteLinkSpeed;

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


			cbWritten = sizeof *pGetInfo;
			ASSERT( cbWritten <= sizeof *pGetInfo + pCallInfo->ulTotalSize - sizeof *pCallInfo );
			DigiDump(DIGITAPIOID,("OID_TAPI_CALL_INFO:LineDevId %d AddrId %d BearMode %x Media Mode %x App spcf %x call st %x rate %d\n",
					  pCallInfo->ulLineDeviceID, pCallInfo->ulAddressID,pCallInfo->ulBearerMode, pCallInfo->ulMediaMode,
					  pCallInfo->ulAppSpecific,pCallInfo->ulCallStates,pCallInfo->ulRate));
			
									
			break;
		}

		case OID_TAPI_GET_CALL_STATUS:
		{
			PNDIS_TAPI_GET_CALL_STATUS const pGetStatus = pData;
			PLINE_CALL_STATUS const pStatus = &pGetStatus->LineCallStatus;
			PX25_CALL pCall = (PVOID)pGetStatus->hdCall;
			ASSERT( MmIsAddressValid( pCall ) );
			ASSERT( pCall->Type == X25_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_GET_CALL_STATUS, pCall 0x%x\n", pCall ) );
			pStatus->ulUsedSize  = sizeof *pStatus;
			pStatus->ulCallState = pCall->TapiState;
			pStatus->ulCallFeatures = X25GetCallFeatures( 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, ( "DigiX25: 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    = X25_ADDRESS_MODES;
			pCaps->ulNumAddresses    = X25_LINE_MAX_ACTIVE_CALLS;
			pCaps->ulBearerModes     = X25_BEARER_MODES;
			pCaps->ulMaxRate         = X25_MAX_RATE;
			pCaps->ulMediaModes      = X25_LINE_MEDIA_MODES;
			pCaps->ulMaxNumActiveCalls = X25_LINE_MAX_ACTIVE_CALLS;
			pCaps->ulPermanentLineID = pGetCaps->ulDeviceID;
			pCaps->ulLineStates      = X25_LINE_DEV_STATES;
			cbWritten = sizeof *pGetCaps;

			// Fill in the ProviderName
			{
				PCHAR const Media  = X25_PROVIDER_MEDIA;
				PCHAR const Device = X25_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 - cbData;
					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;
				DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_GET_DEV_CAPS, Provider %s\n", pProvider ) );



			}

			// 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 - cbData;
				}
				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;
					DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_GET_DEV_CAPS, Linename %s\n", pLine ) );


				}
			}

			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, ( "DigiX25: 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, ( "DigiX25: 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 );
						PX25_CALL const pCall = (PVOID)pGetId->hdCall;
						ASSERT( pCall->Type == X25_TYPE_CALL );
						DigiDump( DIGITAPIOID, ( "DigiX25: 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;
			PX25_CALL const pCall = pAdapter->pLines[pGetStatus->hdLine].pCall;
			DigiDump( DIGITAPIOID, ( "DigiX25: 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 = X25_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;
			PX25_CALL pCall;
			DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_MAKE_CALL, pAdapter 0x%x, hdLine 0x%x, htCall 0x%x\n", pAdapter, pMakeCall->hdLine, pMakeCall->htCall ) );
			ASSERT( !pMakeCall->bUseDefaultLineCallParams );
			if ( pMakeCall->bUseDefaultLineCallParams )
			{
				Status = X25CallDial( pAdapter, pMakeCall->hdLine, pMakeCall->htCall, X25_DEFAULT_MEDIA_MODE,
				                       X25_DEFAULT_BEARER_MODE, X25_DEFAULT_MIN_RATE, X25_DEFAULT_MAX_RATE,
				                       (PCHAR)pMakeCall + pMakeCall->ulDestAddressOffset,
				                       pMakeCall->ulDestAddressSize, &pCall );
			}
			/*
			else if ( !( pParams->ulBearerMode & X25_BEARER_MODES ) )
			{
				Status = NDIS_STATUS_TAPI_BEARERMODEUNAVAIL;
				break;
			}
			*/
			else
			{
				Status = X25CallDial( pAdapter, pMakeCall->hdLine, pMakeCall->htCall, pParams->ulMediaMode,
				                       pParams->ulBearerMode, pParams->ulMinRate, pParams->ulMaxRate,
				                       (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;
			DigiDump( DIGITAPIOID, ( "DigiX25: 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 );

			// These won't hurt because we zero out the struct, but being non-empty means
			// we previously haven't cleaned up properly.
			ASSERT( pAdapter->pLines[iLine].pCall == NULL );
			ASSERT( pAdapter->pLines[iLine].htLine == 0 );
			ASSERT( pAdapter->pLines[iLine].Listen == FALSE );

			NdisZeroMemory( &pAdapter->pLines[iLine], sizeof pAdapter->pLines[iLine] );
			pAdapter->pLines[iLine].htLine = pOpen->htLine;
			pOpen->hdLine = iLine;
			cbWritten = sizeof *pOpen;
			break;
		}

		case OID_TAPI_PROVIDER_INITIALIZE:
		{
			PNDIS_TAPI_PROVIDER_INITIALIZE const pInit = pData;
			DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_PROVIDER_INITIALIZE, pAdapter 0x%x, TapiBaseId 0x%x\n", pAdapter, pInit->ulDeviceIDBase ) );
			NdisZeroMemory( pAdapter->pLines, sizeof *pAdapter->pLines * pAdapter->nLines );
			pAdapter->TapiBaseId = pInit->ulDeviceIDBase;
			pInit->ulNumLineDevs = pAdapter->nLines;
			pInit->ulProviderID  = (ULONG_PTR)pAdapter;
			cbWritten = sizeof *pInit;
			DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_PROVIDER_INITIALIZE, pAdapter 0x%x, TapiBaseId 0x%x Numlines %d \n", pAdapter, pInit->ulDeviceIDBase ,pInit->ulNumLineDevs) );
			
			
			break;
		}

#if 0
		case OID_TAPI_NEGOTIATE_EXT_VERSION:
		{
			PNDIS_TAPI_NEGOTIATE_EXT_VERSION const pVer = pData;
			DigiDump( DIGITAPIOID, ( "DigiX25: 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, ( "DigiX25!TapiQuery: Unsupported Oid 0x%08x on pAdapter 0x%x\n", Oid, pAdapter ) );
			Status = NDIS_STATUS_TAPI_OPERATIONUNAVAIL;
			break;
	}

	if ( cbWritten > cbData )
	{
		DigiDump( DIGIWARN, ( "*** X25: Error, Oid 0x%08x, cbData 0x%x < cbWritten 0x%x!\n", Oid, cbData, cbWritten ) );
	}

	*pcbWritten = cbWritten;
	*pcbNeeded  = cbNeeded;
	DigiDump( DIGITAPIOID, ( "DigiX25!TapiQuery returns: Status 0x%x\n",Status));

	return Status;
}


NDIS_STATUS
X25TapiSet(
	IN PX25_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( DIGITAPIOID, ( "X25!TapiSet: pAdapter 0x%x, Oid 0x%08x\n", pAdapter, Oid ) );

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

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

			if ( pAdapter->pLines[pClose->hdLine].pCall )
				Status = X25CallClose( pAdapter->pLines[pClose->hdLine].pCall );
			else
			{
				pAdapter->pLines[pClose->hdLine].htLine = 0;
				pAdapter->pLines[pClose->hdLine].Listen = FALSE;
				Status = NDIS_STATUS_SUCCESS;
			}
			cbRead = sizeof *pClose;
			break;
		}

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

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

		case OID_TAPI_PROVIDER_SHUTDOWN:
		{
			PNDIS_TAPI_PROVIDER_SHUTDOWN const pShutdown = pData;
			DigiDump( DIGITAPIOID, ( "DigiX25: 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, ( "DigiX25: 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;
			PX25_CALL const pCall = (PVOID)pApp->hdCall;
			ASSERT( pCall->Type == X25_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "DigiX25: 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;
			PX25_CALL const pCall = (PVOID)pSet->hdCall;
			ASSERT( pCall->Type == X25_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "DigiX25: 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, ( "DigiX25: Unsupported Oid, OID_TAPI_SET_CALL_PARAMS!\n" ) );
			if( pCall->TapiState & LINECALLSTATE_IDLE )
					{
						Status = NDIS_STATUS_TAPI_INVALCALLSTATE;
						break;
					}
			else if ( pSet->ulBearerMode & X25_BEARER_MODES )
		  		{
					if( pSet->ulMaxRate >= pSet->ulMinRate && X25_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, ( "DigiX25: OID_TAPI_SET_DEFAULT_MEDIA_DETECTION, pAdapter 0x%x, hdLine 0x%x, MediaModes 0x%x\n", pAdapter, pSet->hdLine, pSet->ulMediaModes ) );
			pAdapter->pLines[pSet->hdLine].Listen = TRUE;
			cbRead = sizeof *pSet;
			break;
		}

		case OID_TAPI_SET_MEDIA_MODE:
		{
			PNDIS_TAPI_SET_MEDIA_MODE const pSet = pData;
			PX25_CALL const pCall = (PVOID)pSet->hdCall;
			ASSERT( pCall->Type == X25_TYPE_CALL );
			DigiDump( DIGITAPIOID, ( "DigiX25: OID_TAPI_SET_MEDIA_MODE, pCall 0x%x, MediaMode 0x%x\n", pCall, pSet->ulMediaMode ) );
			DigiDump( DIGIERROR, ( "DigiX25: OID_TAPI_SET_MEDIA_MODE, cannot modify the MediaMode\n" ) );
			if ( pSet->ulMediaMode & X25_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, ( "DigiX25: 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 & ~X25_LINE_DEV_STATES )
				Status = NDIS_STATUS_TAPI_INVALLINESTATE;
			if ( pSet->ulAddressStates & ~X25_LINE_ADDRESS_STATES )
				Status = NDIS_STATUS_TAPI_INVALADDRESSSTATE;
			cbRead = sizeof *pSet;
			break;
		}

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

	if ( cbRead > cbData )
	{
		DigiDump( DIGIWARN, ( "DigiX25: Warning, Oid 0x%08x, cbData 0x%x < cbRead 0x%x!\n", Oid, cbData, cbRead ) );
	}

	*pcbRead   = cbRead;
	*pcbNeeded = cbNeeded;
	DigiDump( DIGITAPIOID, ( "X25!TapiSet: returns 0x%x ", Status ) );

	return Status;
}


VOID
X25TapiIndicate(
	IN PX25_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, ( "DigiX25!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, ( "DigiX25!TapiIndicate: LINE_NEWCALL, returned htCall 0x%x\n", Event.ulParam2 ) );
		pCall->htCall = Event.ulParam2;
	}
	else if ( ulMsg == LINE_CALLSTATE )
	{
		pCall->TapiState = (ULONG)Param1;
	}
}


VOID
X25TapiSetComplete(
	IN PX25_ADAPTER pAdapter,
	IN NDIS_STATUS   Status )
/*++

Description:

	Notify Tapi that a previous SetInfo request has completed.

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


VOID
X25TapiQueryComplete(
	IN PX25_ADAPTER pAdapter,
	IN NDIS_STATUS   Status )
/*++

Description:

	Notify Tapi that a previous QueryInfo request has completed.

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

ULONG X25GetCallFeatures( 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;
}