/*++

******************************************************************************
* Copyright (c) 1990-96 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:

   tapi.c

Abstract:

   This file contains routines which handle Tapi requests from Miniport SetInfo 
   and MiniportQueryInfo.

Author:

   Michael Zarns

Revision History:

   $History: tapi.c $
 * 
 * *****************  Version 23  *****************
 * User: Mikez        Date: 7/16/97    Time: 2:39p
 * Updated in $/Components/Windows/NT/Sync/Build/Wan/Src
 * Corrected dump statement printing uninitialized data.
 * 
 * *****************  Version 22  *****************
 * User: Mikez        Date: 7/16/97    Time: 1:43p
 * Updated in $/Components/Windows/NT/Sync/Build/Wan/Src
 * Added debug prints for tapi call control, DIGICALLCTRL.
 * 
 * *****************  Version 21  *****************
 * User: Mikez        Date: 6/03/97    Time: 11:40a
 * Updated in $/Components/Windows/NT/Sync/Build/Wan/Src
 * Added support for async completion of DsOpen request in Set Default
 * Media Detection routine.  Removed use of RtlStringToInteger since it
 * can only be called at IRQL PASSIVE_LEVEL.
 * 
 * *****************  Version 20  *****************
 * User: Mikez        Date: 4/28/97    Time: 1:55p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Updated to use DsApi library.  Uses IoBuildDeviceIoControlRequest to
 * alloc Irps causing two problems.  Because ioctls are METHOD_BUFFERED, a
 * systemBuffer is alloc'd, but, because the completionroutines return
 * STATUS_MORE_PROCESSING_REQUIRED, never gets freed.  Also, SystemBuffer
 * is not copied to OutputBuffer until after completionRoutine returns
 * STATUS_SUCCESS.  Need to move from METHOD_BUFFER or from using
 * IoBuildDeviceIoControlRequest.
 * 
 * *****************  Version 19  *****************
 * User: Mikez        Date: 3/20/97    Time: 9:50a
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Force indications up to tapi before returning from drop routine.
 * 
 * *****************  Version 18  *****************
 * User: Mikez        Date: 3/17/97    Time: 12:30p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Added support for LineDevStatus via Status Messages routine.
 * 
 * Remove async completions on MakeCall, Drop to work around timing
 * problems in TAPI.  Replaced RtlEqualString with version which can be
 * called at raised IRQL.
 * 
 * *****************  Version 16  *****************
 * User: Mikez        Date: 3/12/97    Time: 3:44p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Misc changes to get RAS dialout working.  Still bugchecks after dialout
 * connection completes on multiprocessor NT 3.51, sp5.
 * 
 * *****************  Version 15  *****************
 * User: Mikez        Date: 3/04/97    Time: 11:11p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * 
 * *****************  Version 14  *****************
 * User: Mikez        Date: 3/04/97    Time: 2:56p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Added a Purge before sending down a Disc during a TAPI Drop request to
 * help speed up the drop completion.
 * 
 * *****************  Version 13  *****************
 * User: Mikez        Date: 3/03/97    Time: 3:39p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Updated to use new SxbWrap SxbClose with async completion.
 * 
 * *****************  Version 12  *****************
 * User: Mikez        Date: 3/03/97    Time: 9:23a
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Updated to use async completions on MakeCall, Drop and Answer TAPI OID
 * requests.
 * 
 * *****************  Version 11  *****************
 * User: Mikez        Date: 2/26/97    Time: 10:24a
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Misc fixes and debug info.  MakeCall now has completion routine.  Drop
 * now closes the stream (after sending a DISC request).  CloseCall calls
 * Drop.
 * 
 * *****************  Version 10  *****************
 * User: Mikez        Date: 1/29/97    Time: 4:56p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Reset hStream to null after closing stream.  Removed error printf for
 * bad media mode request on MakeCall requests.  NT40 acknowledged bug
 * doesn't initialize mediamode correctly.
 * 
 * *****************  Version 9  *****************
 * User: Mikez        Date: 1/02/97    Time: 4:52p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Changed max number active calls constant from 1 to 256.
 * 
 * *****************  Version 8  *****************
 * User: Mikez        Date: 12/17/96   Time: 4:44p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Fixed wrong AddressID param passed into NewCallObject.
 * 
 * *****************  Version 7  *****************
 * User: Mikez        Date: 12/09/96   Time: 4:20p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Cleaned up call states.  No longer use bitmasks, check for unique
 * numeric values.
 * 
 * *****************  Version 6  *****************
 * User: Mikez        Date: 12/06/96   Time: 3:05p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Added NOTIMPLEMENTED debug prints for unimplemented OID routines.
 * 
 * *****************  Version 4  *****************
 * User: Mikez        Date: 11/12/96   Time: 3:02p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Updated MediaType from X25 to X25_.
 * 
 * *****************  Version 3  *****************
 * User: Mikez        Date: 11/12/96   Time: 2:08p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Removed call to RtlUnicodeStringToAnsiString from OidTapiGetDevCaps to
 * avoid calling at raised irql - subsequently causing system to bugcheck.
 * 
 * *****************  Version 2  *****************
 * User: Mikez        Date: 10/28/96   Time: 1:25p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Updated to support latest firmware release, v1.1.2b.

--*/
#include <ntddk.h>
#include <ndis.h>
#include <ndiswan.h>
#include <ndistapi.h>
#include <stdio.h>

#include "DsApi.h"
#include "digix25.h"
#include "digix25p.h"
#include "digifile.h"
#include <digialloc.h>


NTSTATUS GetAddressIdFromAddress( IN PUCHAR Address,
                                  IN ULONG Length, 
                                  OUT PULONG pAddressId );

BOOLEAN DigiEqualString( IN PSTRING String1,
                         IN PSTRING String2,
                         IN BOOLEAN CaseInsensitive );

VOID SetDefaultMediaComplete( IN HANDLE hOwner,
                              IN HANDLE hStream,
                              IN ULONG Error,
                              IN NTSTATUS IoStatus );

VOID MakeCallOpenComplete( IN HANDLE   hOwner,
                           IN HANDLE   hStream, 
                           IN ULONG    Error, 
                           IN NTSTATUS IoStatus	);

VOID MakeCallConnectComplete( IN HANDLE   hOwner,
                              IN HANDLE   Context, 
                              IN PUCHAR   pControl,
                              IN ULONG    ControlLength, 
                              IN PUCHAR   pData,
                              IN ULONG    DataLength, 
                              IN NTSTATUS IoStatus	);

VOID DropDiscComplete( IN HANDLE   hOwner,
                       IN HANDLE   Context,
                       IN PUCHAR   pControl,
                       IN ULONG    ControlLength, 
                       IN PUCHAR   pData,
                       IN ULONG    DataLength, 
                       IN NTSTATUS IoStatus );

VOID DropCloseComplete( IN HANDLE   hOwner,   
                        IN NTSTATUS IoStatus	);

VOID CloseCallComplete( IN HANDLE   hOwner,
                        IN NTSTATUS IoStatus	);

VOID AnswerOpenComplete( IN HANDLE   hOwner, 
                         IN HANDLE   hStream,
                         IN ULONG    Error,
                         IN NTSTATUS IoStatus );

VOID AnswerConfirmComplete( IN HANDLE   hOwner,
                            IN HANDLE   Context, 
                            IN PUCHAR   pControl,
                            IN ULONG    ControlLength, 
                            IN PUCHAR   pData,
                            IN ULONG    DataLength, 
                            IN NTSTATUS IoStatus );

VOID
SendListen( 
	IN PDIGIWAN_PHYS_ADAPTER pPhysAdapter	);



NDIS_STATUS OidTapiAccept( IN NDIS_HANDLE MiniportAdapterContext, 
                           IN PVOID InformationBuffer,
                           IN ULONG InformationBufferLength,
                           OUT PULONG BytesWritten,
                           OUT PULONG BytesNeeded,
                           IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiAccept\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** OidTapiAccept not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exit OidTapiAccept\n") );
   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiAccept



NDIS_STATUS OidTapiAnswer( IN NDIS_HANDLE MiniportAdapterContext,
                           IN PVOID InformationBuffer,
                           IN ULONG InformationBufferLength,
                           OUT PULONG BytesWritten,
                           OUT PULONG BytesNeeded,
                           IN BOOLEAN QueryInfo )
/*++

Routine Description:

	This routine is called to answer an incoming call previously indicated
	to tapi by a PhysAdapter listen stream.  An X.25 Call Confirm is sent to
	remote to bring up the connection.  Wan and Tapi indications are sent.

--*/
{
	PDIGIWAN_ADAPTER_OBJECT AdapterObject = MiniportAdapterContext;
   PNDIS_TAPI_ANSWER Answer = InformationBuffer;
   PDIGIWAN_CALL_OBJECT CallObject;
	NTSTATUS Status;

	ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiAnswer, hdCall = 0x%x\n", 
                                    Answer->hdCall) );
   CallObject = (PDIGIWAN_CALL_OBJECT)Answer->hdCall;
	if( CallObject == NULL )
	{
		DigiDump( DIGIERRORS, ("OidTapiAnswer: Error Null CallObject!\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}
   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

	if ( CallObject->InternalCallState != INTERNAL_CALL_STATE_OFFERING &&
	     CallObject->InternalCallState != INTERNAL_CALL_STATE_ACCEPTED )
		return( NDIS_STATUS_TAPI_INVALCALLSTATE );

	DigiDump( DIGICALLCTRL, 
	          ( "DIGIWAN: Answer on Address 0x%x\n", CallObject->AddressID ) );

//	ASSERT( AdapterObject->Line.CurrentState & LINEDEVSTATE_RINGING );
	AdapterObject->Line.CurrentState &= ~LINEDEVSTATE_RINGING;

	ASSERT( !CallObject->hStream );
	Status = DsOpenEx( CallObject->PhysAdapter->hDevice, X25_DEVICE_NAME, 
	                   CallObject, 0,0, AnswerOpenComplete );

	if ( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERRORS, 
		          ("OidTapiAnswer: DsOpenEx error, Status NDIS_STATUS_RESOURCES\n") );
		return( NDIS_STATUS_RESOURCES );
	}

   DigiDump( DIGIFLOW, ("Exit OidTapiAnswer\n") );
   return( NDIS_STATUS_SUCCESS );

}  // end OidTapiAnswer



NDIS_STATUS OidTapiClose( IN NDIS_HANDLE MiniportAdapterContext,
                          IN PVOID InformationBuffer,
                          IN ULONG InformationBufferLength,
                          OUT PULONG BytesWritten,
                          OUT PULONG BytesNeeded,
                          IN BOOLEAN QueryInfo )
/*++

Routine Description:

	This routine is called to close an entire line.  All calls on the line
	are guaranteed to be closed before entry here.  Cancel listens on all 
	physical adapters associated with this virtual line (there's only one 
	virtual line, so that means all adapters).  

   If we get this call, then RAS is done using this device/line.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_CLOSE Close=(PVOID)InformationBuffer;
   PLIST_ENTRY Entry;

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiClose, hdLine 0x%x\n", Close->hdLine) );

   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   // Force completion of all pending indications up to Tapi and wan.
	AdapterContextTimerFunction( NULL, AdapterObject, NULL, NULL );

	Entry = &AdapterObject->PhysAdapters;
	while ( Entry->Flink != &AdapterObject->PhysAdapters )
	{
		PDIGIWAN_PHYS_ADAPTER PhysAdapter;
		PhysAdapter = CONTAINING_RECORD( Entry->Flink,
		                                 DIGIWAN_PHYS_ADAPTER,
		                                 List );
		ASSERT( PhysAdapter->Type == DIGIWAN_TYPE_PHYS_ADAPTER );
		CancelListen( PhysAdapter->hListenStream );
		Entry = Entry->Flink;
	}

   DigiDump( DIGIFLOW, ("Exit OidTapiClose\n") );
   return( NDIS_STATUS_SUCCESS );

}  // end OidTapiClose



NDIS_STATUS OidTapiCloseCall( IN NDIS_HANDLE MiniportAdapterContext,
                              IN PVOID InformationBuffer,
                              IN ULONG InformationBufferLength,
                              OUT PULONG BytesWritten,
                              OUT PULONG BytesNeeded,
                              IN BOOLEAN QueryInfo )
/*++

Routine Description:

	This routine is called to close down an active call.  Depending on
	the current state of the call a disconnect may be sent down, tapi and
	wan indications may be sent.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_CLOSE_CALL CloseCall=(PVOID)InformationBuffer;
   PDIGIWAN_CALL_OBJECT CallObject;
	NDIS_STATUS Status;
	KIRQL OldIrql;

	DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiCloseCall, hdCall 0x%x\n", 
	                    CloseCall->hdCall) );

   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   CallObject = (PVOID)CloseCall->hdCall;
	if( CallObject == NULL )
	{
		DigiDump( DIGIERRORS, ("*** OidTapiCloseCall: error NULL CallObject ***\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}
   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

	DigiDump( DIGIOPENCALLS, ( "Close: callobject 0x%x, Address ID 0x%x\n", 
	                            CallObject,
	                            CallObject->AddressID ) );

	DigiDump( DIGICALLCTRL|DIGIDIAG2|DIGICLOSECALL, 
	          ("DIGIWAN: CloseCall on Address 0x%x, hStream 0x%x, InternalState 0x%x\n",
             CallObject->AddressID, CallObject->hStream, CallObject->InternalCallState) );

	switch ( CallObject->InternalCallState )
	{
		case INTERNAL_CALL_STATE_CONNECTED:

			// Indicate the line is down
		   AdapterContextWanLinedown( CallObject, NULL );

			// Notify Tapi of disconnection
			CallObject->TapiCallState = LINECALLSTATE_IDLE;
			AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
			                             LINECALLSTATE_IDLE, 0, 0 );
			break;

		case INTERNAL_CALL_STATE_OFFERING:
			CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
			// Remove this call from the offeringcalls list
			KeAcquireSpinLock( &CallObject->PhysAdapter->Lock, &OldIrql );
			RemoveEntryList( &CallObject->List );
			KeReleaseSpinLock( &CallObject->PhysAdapter->Lock, OldIrql );
			ASSERT( !CallObject->hStream );
			break;

		default:
			break;
	}

	// Force all pending indications now!
	AdapterContextTimerFunction( NULL, AdapterObject, NULL, NULL );

	if ( CallObject->hStream )
	{
		HANDLE hStream;
		CallObject->InternalCallState = INTERNAL_CALL_STATE_CLOSING;
		KeRemoveQueueDpc( &CallObject->Dpc );
		Status = DsPurge( CallObject->hStream, DS_PURGE_ALL);
		ASSERT( Status == STATUS_SUCCESS );
		hStream = CallObject->hStream;
		CallObject->hStream = NULL;
		DigiDump( DIGIDIAG5, ("CloseCall: DsClose hStream 0x%x, Address 0x%x\n",
		                     hStream, CallObject->AddressID) );
		Status = DsCloseEx( hStream, CloseCallComplete );
		// Note here: we'll free the call object at CloseCallComplete time!
	}
	else
	{
		FreeCallObject( CallObject );
	}

   DigiDump( DIGIFLOW, ("Exit OidTapiCloseCall\n") );
   return( NDIS_STATUS_SUCCESS );

}  // end OidTapiCloseCall



NDIS_STATUS OidTapiConditionalMediaDetection( IN NDIS_HANDLE MiniportAdapterContext,
                                              IN PVOID InformationBuffer,
                                              IN ULONG InformationBufferLength,
                                              OUT PULONG BytesWritten,
                                              OUT PULONG BytesNeeded,
                                              IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Entering OidTapiConditionalMediaDetection\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** OidTapiConditionalMediaDetection not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exiting OidTapiConditionalMediaDetection\n") );
   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiConditionalMediaDetection



NDIS_STATUS OidTapiDevSpecific( IN NDIS_HANDLE MiniportAdapterContext,
                                IN PVOID InformationBuffer,
                                IN ULONG InformationBufferLength,
                                OUT PULONG BytesWritten,
                                OUT PULONG BytesNeeded,
                                IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Entering OidTapiDevSpecific\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** OidTapiDevSpecific not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exiting OidTapiDevSpecific\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiDevSpecific



NDIS_STATUS OidTapiDial( IN NDIS_HANDLE MiniportAdapterContext,
                         IN PVOID InformationBuffer,
                         IN ULONG InformationBufferLength,
                         OUT PULONG BytesWritten,
                         OUT PULONG BytesNeeded,
                         IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Entering OidTapiDial\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** OidTapiDial not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exiting OidTapiDial\n") );
   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiDial



NDIS_STATUS OidTapiDrop( IN NDIS_HANDLE MiniportAdapterContext,
                         IN PVOID InformationBuffer,
                         IN ULONG InformationBufferLength,
                         OUT PULONG BytesWritten,
                         OUT PULONG BytesNeeded,
                         IN BOOLEAN QueryInfo )
/*++

Routine Description:

	This routine moves the call object to a disconnected state.  If it was
	connected, a Disconnect Request is sent.  Tapi and Wan state changes 
	are indicated when necessary.

--*/
{
	PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
	PNDIS_TAPI_DROP Drop=InformationBuffer;
	PDIGIWAN_CALL_OBJECT CallObject;
	NDIS_STATUS Status;
	KIRQL OldIrql;

	ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

	CallObject = (PDIGIWAN_CALL_OBJECT)Drop->hdCall;
	if( CallObject == NULL )
	{
		DigiDump( DIGIERRORS, ("Error: OidTapiDrop CallObject = 0\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}
	ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

	DigiDump( DIGIFLOW|DIGITAPIFLOW, 
	          ("Enter OidTapiDrop: hdCall 0x%x, TapiState 0x%x, InternalState 0x%x\n",
	          Drop->hdCall, CallObject->TapiCallState, CallObject->InternalCallState) );

	Status = NDIS_STATUS_SUCCESS;

	DigiDump( DIGICALLOBJ, ( "OidTapiDrop: AddressID 0x%x\n", CallObject->AddressID ) );
	DigiDump( DIGICALLCTRL|DIGIDIAG2|DIGIDROP, 
	          ("DIGIWAN: Drop on Address 0x%x, hStream 0x%x, InternalState 0x%x, TapiState 0x%x\n",
             CallObject->AddressID, CallObject->hStream, CallObject->InternalCallState,
             CallObject->TapiCallState) );

	switch ( CallObject->InternalCallState )
	{
		case INTERNAL_CALL_STATE_CONNECTED:

			// Connected.  Must disconnect the call.
		   ASSERT( CallObject->hStream );
			// Indicate the line is down
		   AdapterContextWanLinedown( CallObject, NULL );

			// Fall through

		case INTERNAL_CALL_STATE_PROCEEDING:

			// Sanity check
		   ASSERT( CallObject->hStream );

			// Fall through

		case INTERNAL_CALL_STATE_IDLE:

			if ( CallObject->hStream )
			{
				// Got a stream open, close it down.
				HANDLE hStream;
				CallObject->InternalCallState = INTERNAL_CALL_STATE_CLOSING;
				KeRemoveQueueDpc( &CallObject->Dpc );
				Status = DsPurge( CallObject->hStream, DS_PURGE_ALL );
				ASSERT( Status == STATUS_SUCCESS );
				hStream = CallObject->hStream;
				CallObject->hStream = NULL;
				DigiDump( DIGIDIAG5, ("Drop: DsClose hStream 0x%x, Address 0x%x\n",
				                     hStream, CallObject->AddressID) );
				Status = DsCloseEx( hStream, DropCloseComplete );
				ASSERT( NT_SUCCESS( Status ) );
			}
			else
			{
				ASSERT( CallObject->InternalCallState == INTERNAL_CALL_STATE_IDLE );
			}

			// Notify Tapi 
			AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
			                             LINECALLSTATE_IDLE, 0, 0 );

			Status = NDIS_STATUS_SUCCESS;
			break;

		case INTERNAL_CALL_STATE_OFFERING:

			// Offering means this is an incoming call.  Reject it.
			ASSERT( CallObject->conn_id );
			ASSERT( !CallObject->hStream );
			CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;

	//		ASSERT( AdapterObject->Line.CurrentState & LINEDEVSTATE_RINGING );
			AdapterObject->Line.CurrentState &= ~LINEDEVSTATE_RINGING;

			// Remove this call from the OfferingCalls list
			KeAcquireSpinLock( &CallObject->PhysAdapter->Lock, &OldIrql );
			RemoveEntryList( &CallObject->List );
			KeReleaseSpinLock( &CallObject->PhysAdapter->Lock, OldIrql );

			// Notify Tapi of disconnection
			CallObject->TapiCallState = LINECALLSTATE_IDLE;
			AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
			                             LINECALLSTATE_IDLE, 0, 0 );
			Status = RejectIncoming( CallObject->PhysAdapter, CallObject->conn_id );
			CallObject->conn_id = 0;
			if ( !NT_SUCCESS( Status ) )
				Status = NDIS_STATUS_FAILURE;
			else
				Status = NDIS_STATUS_SUCCESS;
			break;

		default:

			// Bad state
			DigiDump( DIGIERRORS, 
			          ( "*** Drop: Ignoring bad InternalCallState 0x%x! ***\n",
			          CallObject->InternalCallState ) );
			Status = NDIS_STATUS_SUCCESS;
			break;
	}

   // Force completion of all pending indications up to Tapi and wan.
	AdapterContextTimerFunction( NULL, AdapterObject, NULL, NULL );

	DigiDump( DIGIFLOW, ("Exit OidTapiDrop\n") );
	return( Status );
}  // end OidTapiDrop



NDIS_STATUS OidTapiGetAddressCaps( IN NDIS_HANDLE MiniportAdapterContext,
                                   IN PVOID InformationBuffer,
                                   IN ULONG InformationBufferLength,
                                   OUT PULONG BytesWritten,
                                   OUT PULONG BytesNeeded,
                                   IN BOOLEAN QueryInfo )
/*++

Routine Description:

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_GET_ADDRESS_CAPS GetAddressCaps=(PVOID)InformationBuffer;
   ULONG AddressLength;
	UCHAR Address[16];

   DigiDump( DIGIFLOW|DIGITAPIFLOW, 
             ("Enter OidTapiGetAddressCaps: DeviceID 0x%x, AddressID 0x%x\n",
             GetAddressCaps->ulDeviceID,
             GetAddressCaps->ulAddressID) );

   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

	// We expose a single virtual line on which all calls are placed.
	if ( GetAddressCaps->ulDeviceID != AdapterObject->Line.DeviceID )
		return( NDIS_STATUS_TAPI_INVALPARAM );

	if ( GetAddressCaps->ulAddressID >= AdapterObject->NumberOfEndpoints )
      return(NDIS_STATUS_TAPI_INVALADDRESSID);

	//
	// Build Tapi addressID
	//

   sprintf( Address,	
            "%s-0-%d",
            &AdapterObject->Instance,
            GetAddressCaps->ulAddressID );

   AddressLength = strlen( Address );

   GetAddressCaps->LineAddressCaps.ulNeededSize = sizeof(LINE_ADDRESS_CAPS) + AddressLength;

	if( GetAddressCaps->LineAddressCaps.ulNeededSize >
	    GetAddressCaps->LineAddressCaps.ulTotalSize )
   {
      return( NDIS_STATUS_TAPI_STRUCTURETOOSMALL );
   }

   GetAddressCaps->LineAddressCaps.ulUsedSize = sizeof(LINE_ADDRESS_CAPS) + AddressLength;

   NdisMoveMemory( ((LPSTR)&(GetAddressCaps->LineAddressCaps)) + sizeof(LINE_ADDRESS_CAPS),
                   Address,
                   AddressLength );

   GetAddressCaps->LineAddressCaps.ulLineDeviceID = GetAddressCaps->ulDeviceID;

   GetAddressCaps->LineAddressCaps.ulAddressSize = AddressLength;
   GetAddressCaps->LineAddressCaps.ulAddressOffset = sizeof(LINE_ADDRESS_CAPS);

   GetAddressCaps->LineAddressCaps.ulDevSpecificSize = 0;
   GetAddressCaps->LineAddressCaps.ulDevSpecificOffset = 0;

   GetAddressCaps->LineAddressCaps.ulAddressSharing = LINEADDRESSSHARING_PRIVATE;
   GetAddressCaps->LineAddressCaps.ulAddressStates = LINEADDRESSSTATE_OTHER |
                                                     LINEADDRESSSTATE_INUSEZERO |
                                                     LINEADDRESSSTATE_INUSEONE |
                                                     LINEADDRESSSTATE_NUMCALLS;

   GetAddressCaps->LineAddressCaps.ulCallInfoStates = LINECALLINFOSTATE_CALLERID |
                                                      LINECALLINFOSTATE_CALLEDID;

   GetAddressCaps->LineAddressCaps.ulCallerIDFlags = LINECALLPARTYID_ADDRESS |
                                                     LINECALLPARTYID_UNAVAIL;

   GetAddressCaps->LineAddressCaps.ulCalledIDFlags = LINECALLPARTYID_ADDRESS |
                                                     LINECALLPARTYID_UNAVAIL;

   GetAddressCaps->LineAddressCaps.ulConnectedIDFlags = LINECALLPARTYID_UNAVAIL;
   GetAddressCaps->LineAddressCaps.ulRedirectionIDFlags = LINECALLPARTYID_UNAVAIL;
   GetAddressCaps->LineAddressCaps.ulRedirectingIDFlags = LINECALLPARTYID_UNAVAIL;

   GetAddressCaps->LineAddressCaps.ulCallStates = LINECALLSTATE_IDLE |
                                                  LINECALLSTATE_OFFERING |
                                                  LINECALLSTATE_CONNECTED |
                                                  LINECALLSTATE_PROCEEDING |
                                                  LINECALLSTATE_DISCONNECTED |
                                                  LINECALLSTATE_SPECIALINFO |
                                                  LINECALLSTATE_UNKNOWN;

   GetAddressCaps->LineAddressCaps.ulDialToneModes = LINEDIALTONEMODE_UNAVAIL;

   GetAddressCaps->LineAddressCaps.ulBusyModes = LINEBUSYMODE_UNAVAIL;

   GetAddressCaps->LineAddressCaps.ulSpecialInfo = LINESPECIALINFO_UNAVAIL;

   GetAddressCaps->LineAddressCaps.ulDisconnectModes = LINEDISCONNECTMODE_NORMAL |
                                                       LINEDISCONNECTMODE_UNKNOWN |
                                                       LINEDISCONNECTMODE_BUSY |
                                                       LINEDISCONNECTMODE_NOANSWER |
                                                       LINEDISCONNECTMODE_UNREACHABLE | 
                                                       LINEDISCONNECTMODE_UNAVAIL;

   GetAddressCaps->LineAddressCaps.ulMaxNumActiveCalls = 1;
   GetAddressCaps->LineAddressCaps.ulMaxNumOnHoldCalls = 0;
   GetAddressCaps->LineAddressCaps.ulMaxNumOnHoldPendingCalls = 0;
   GetAddressCaps->LineAddressCaps.ulMaxNumConference = 0;
   GetAddressCaps->LineAddressCaps.ulMaxNumTransConf = 0;

   GetAddressCaps->LineAddressCaps.ulAddrCapFlags = ( LINEADDRCAPFLAGS_DIALED );

   GetAddressCaps->LineAddressCaps.ulCallFeatures = ( LINECALLFEATURE_ANSWER |
                                                      LINECALLFEATURE_DIAL |
                                                      LINECALLFEATURE_DROP );

   GetAddressCaps->LineAddressCaps.ulRemoveFromConfCaps = 0;
   GetAddressCaps->LineAddressCaps.ulRemoveFromConfState = 0;
   GetAddressCaps->LineAddressCaps.ulTransferModes = 0;
   GetAddressCaps->LineAddressCaps.ulParkModes = 0;

   GetAddressCaps->LineAddressCaps.ulForwardModes = 0;
   GetAddressCaps->LineAddressCaps.ulMaxForwardEntries = 0;
   GetAddressCaps->LineAddressCaps.ulMaxSpecificEntries = 0;
   GetAddressCaps->LineAddressCaps.ulMinFwdNumRings = 0;
   GetAddressCaps->LineAddressCaps.ulMaxFwdNumRings = 0;

   GetAddressCaps->LineAddressCaps.ulMaxCallCompletions = 0;
   GetAddressCaps->LineAddressCaps.ulCallCompletionConds = 0;
   GetAddressCaps->LineAddressCaps.ulCallCompletionModes = 0;
   GetAddressCaps->LineAddressCaps.ulNumCompletionMessages = 0;
   GetAddressCaps->LineAddressCaps.ulCompletionMsgTextEntrySize = 0;
   GetAddressCaps->LineAddressCaps.ulCompletionMsgTextSize = 0;
   GetAddressCaps->LineAddressCaps.ulCompletionMsgTextOffset = 0;

   DigiDump( DIGIFLOW, ("Exit OidTapiGetAddressCaps\n") );

   return( NDIS_STATUS_SUCCESS );

}  // end OidTapiGetAddressCaps



NDIS_STATUS OidTapiGetAddressId( IN NDIS_HANDLE MiniportAdapterContext,
                                 IN PVOID InformationBuffer,
                                 IN ULONG InformationBufferLength,
                                 OUT PULONG BytesWritten,
                                 OUT PULONG BytesNeeded,
                                 IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
	PNDIS_TAPI_GET_ADDRESS_ID Request = InformationBuffer;
	NDIS_STATUS Status;
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiGetAddressId\n") );

	ASSERT( Request->ulAddressMode == LINEADDRESSMODE_DIALABLEADDR );
	if ( Request->ulAddressMode != LINEADDRESSMODE_DIALABLEADDR )
		return( NDIS_STATUS_TAPI_INVALADDRESSMODE );

	Status = GetAddressIdFromAddress( Request->szAddress,
	                                  Request->ulAddressSize,
	                                  &Request->ulAddressID );
	if ( Status != NDIS_STATUS_SUCCESS )
		return( NDIS_STATUS_TAPI_INVALADDRESS );

   DigiDump( DIGIFLOW, ("Exit OidTapiGetAddressId\n") );
   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiGetAddressId



NDIS_STATUS OidTapiGetAddresStatus( IN NDIS_HANDLE MiniportAdapterContext,
                                    IN PVOID InformationBuffer,
                                    IN ULONG InformationBufferLength,
                                    OUT PULONG BytesWritten,
                                    OUT PULONG BytesNeeded,
                                    IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Entering OidTapiGetAddresStatus\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** OidTapiGetAddressStatus not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exiting OidTapiGetAddresStatus\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiGetAddresStatus



NDIS_STATUS OidTapiGetCallAddressId( IN NDIS_HANDLE MiniportAdapterContext,
                                     IN PVOID InformationBuffer,
                                     IN ULONG InformationBufferLength,
                                     OUT PULONG BytesWritten,
                                     OUT PULONG BytesNeeded,
                                     IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   PNDIS_TAPI_GET_CALL_ADDRESS_ID CallAddressId=(PVOID)InformationBuffer;
   PDIGIWAN_CALL_OBJECT CallObject;

   DigiDump( DIGIFLOW, 
             ("Enter OidTapiGetCallAddressId, hdCall 0x%x\n", CallAddressId->hdCall) );

   CallObject = (PDIGIWAN_CALL_OBJECT)CallAddressId->hdCall;
	if( CallObject == NULL )
	{
		DigiDump( DIGIERRORS, ("Error: OidTapiGetCallAddressId CallObject = 0\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}

   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	CallAddressId->ulAddressID = CallObject->AddressID;
	DigiDump( DIGITAPIFLOW, ("OidTapiGetCallAddressId: hdCall 0x%08x, AddressID 0x%x\n",
	                        CallAddressId->hdCall, CallAddressId->ulAddressID ) );

   DigiDump( DIGIFLOW, ("Exit OidTapiGetCallAddressId\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiGetCallAddressId



NDIS_STATUS OidTapiGetCallInfo( IN NDIS_HANDLE MiniportAdapterContext,
                                IN PVOID InformationBuffer,
                                IN ULONG InformationBufferLength,
                                OUT PULONG BytesWritten,
                                OUT PULONG BytesNeeded,
                                IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
	PDIGIWAN_ADAPTER_OBJECT AdapterObject=MiniportAdapterContext;
   PNDIS_TAPI_GET_CALL_INFO GetCallInfo=(PNDIS_TAPI_GET_CALL_INFO)InformationBuffer;
   PDIGIWAN_CALL_OBJECT CallObject;
   PLINE_CALL_INFO LineCallInfo = &GetCallInfo->LineCallInfo;

	ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiGetCallInfo: hdCall=0x%x, ulTotalSize=0x%x\n",
                        GetCallInfo->hdCall,
                        LineCallInfo->ulTotalSize) );

   CallObject = (PDIGIWAN_CALL_OBJECT)GetCallInfo->hdCall;

   LineCallInfo->ulNeededSize = sizeof(LINE_CALL_INFO);
   LineCallInfo->ulUsedSize = 0;

   if( LineCallInfo->ulNeededSize > LineCallInfo->ulTotalSize )
   {
      DigiDump( DIGIFLOW, ("ret STRUCTURETOOSMALL\n" ) );
      return( NDIS_STATUS_TAPI_STRUCTURETOOSMALL );
   }

   //
   // Do this check AFTER the size check so if this is a query for required
   // size, we will return properly.
   //
   if( CallObject == NULL )
      return( NDIS_STATUS_TAPI_INVALCALLHANDLE );

   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

   LineCallInfo->ulUsedSize = LineCallInfo->ulNeededSize;

// hLine is a ULONG, pointers can be 64-bit.  This is broken.
// Looks like we never use this anyway, so commenting it out.
//   LineCallInfo->hLine = (ULONG)AdapterObject;
   LineCallInfo->ulLineDeviceID = CallObject->pAdapter->Line.DeviceID;

   LineCallInfo->ulAddressID = CallObject->AddressID;

   LineCallInfo->ulBearerMode = CallObject->CurBearerMode;

   if( CallObject->LocalLinkSpeed < CallObject->RemoteLinkSpeed )
      LineCallInfo->ulRate = CallObject->LocalLinkSpeed;
   else
      LineCallInfo->ulRate = CallObject->RemoteLinkSpeed;

   LineCallInfo->ulMediaMode = CallObject->CurMediaMode;

   LineCallInfo->ulAppSpecific = CallObject->AppSpecific;
   LineCallInfo->ulCallID = 0;
   LineCallInfo->ulRelatedCallID = 0;
   LineCallInfo->ulCallParamFlags = 0;
   LineCallInfo->ulCallStates = LINECALLSTATE_IDLE |
                                LINECALLSTATE_OFFERING |
                                LINECALLSTATE_CONNECTED |
                                LINECALLSTATE_DISCONNECTED |
                                LINECALLSTATE_SPECIALINFO |
                                LINECALLSTATE_UNKNOWN;

   LineCallInfo->DialParams.ulDialPause = 0;
   LineCallInfo->DialParams.ulDialSpeed = 0;
   LineCallInfo->DialParams.ulDigitDuration = 0;
   LineCallInfo->DialParams.ulWaitForDialtone = 0;

   LineCallInfo->ulOrigin = LINECALLORIGIN_UNKNOWN;
   LineCallInfo->ulReason = LINECALLREASON_UNKNOWN;
   LineCallInfo->ulCompletionID = 0;

   LineCallInfo->ulCountryCode = 0;
   LineCallInfo->ulTrunk = (ULONG)-1;

   //
   // this should actually fill in called and
   // calling address fields. we don't do this
   // very well right now so I will defer this.
   //
   LineCallInfo->ulCallerIDFlags = LINECALLPARTYID_UNAVAIL;
   LineCallInfo->ulCallerIDSize = 0;
   LineCallInfo->ulCallerIDOffset = 0;
   LineCallInfo->ulCallerIDNameSize = 0;
   LineCallInfo->ulCallerIDNameOffset = 0;

   LineCallInfo->ulCalledIDFlags = LINECALLPARTYID_UNAVAIL;
   LineCallInfo->ulCalledIDSize = 0;
   LineCallInfo->ulCalledIDOffset = 0;
   LineCallInfo->ulCalledIDNameSize = 0;
   LineCallInfo->ulCalledIDNameOffset = 0;

   LineCallInfo->ulConnectedIDFlags = LINECALLPARTYID_UNAVAIL;
   LineCallInfo->ulConnectedIDSize = 0;
   LineCallInfo->ulConnectedIDOffset = 0;
   LineCallInfo->ulConnectedIDNameSize = 0;
   LineCallInfo->ulConnectedIDNameOffset = 0;

   LineCallInfo->ulRedirectionIDFlags = LINECALLPARTYID_UNAVAIL;
   LineCallInfo->ulRedirectionIDSize = 0;
   LineCallInfo->ulRedirectionIDOffset = 0;
   LineCallInfo->ulRedirectionIDNameSize = 0;
   LineCallInfo->ulRedirectionIDNameOffset = 0;

   LineCallInfo->ulRedirectingIDFlags = LINECALLPARTYID_UNAVAIL;
   LineCallInfo->ulRedirectingIDSize = 0;
   LineCallInfo->ulRedirectingIDOffset = 0;
   LineCallInfo->ulRedirectingIDNameSize = 0;
   LineCallInfo->ulRedirectingIDNameOffset = 0;

   LineCallInfo->ulDisplaySize = 0;
   LineCallInfo->ulDisplayOffset = 0;

   LineCallInfo->ulUserUserInfoSize = 0;
   LineCallInfo->ulUserUserInfoOffset = 0;

   LineCallInfo->ulHighLevelCompSize = 0;
   LineCallInfo->ulHighLevelCompOffset = 0;

   LineCallInfo->ulLowLevelCompSize = 0;
   LineCallInfo->ulLowLevelCompOffset = 0;

   LineCallInfo->ulChargingInfoSize = 0;
   LineCallInfo->ulChargingInfoOffset = 0;

   LineCallInfo->ulTerminalModesSize = 0;
   LineCallInfo->ulTerminalModesOffset = 0;

   LineCallInfo->ulDevSpecificSize = 0;
   LineCallInfo->ulDevSpecificOffset = 0;

   DigiDump( DIGIFLOW, ("Exit OidTapiGetCallInfo:\n"
                        "     hLine = 0x%x, ulAddressID = 0x%x, ulRate = %d\n",
                        LineCallInfo->hLine,
                        LineCallInfo->ulAddressID,
                        LineCallInfo->ulRate) );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiGetCallInfo



NDIS_STATUS OidTapiGetCallStatus( IN NDIS_HANDLE MiniportAdapterContext,
                                  IN PVOID InformationBuffer,
                                  IN ULONG InformationBufferLength,
                                  OUT PULONG BytesWritten,
                                  OUT PULONG BytesNeeded,
                                  IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   PNDIS_TAPI_GET_CALL_STATUS GetCallStatus=(PVOID)InformationBuffer;
   PLINE_CALL_STATUS CallStatus;
   PDIGIWAN_CALL_OBJECT CallObject;

   CallStatus = &GetCallStatus->LineCallStatus;
   CallObject = (PDIGIWAN_CALL_OBJECT)GetCallStatus->hdCall;

	if( CallObject == NULL )
	{
		DigiDump( DIGIERRORS, ("Error: OidTapiGetCallStatus CallObject = 0\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}

   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

   DigiDump( DIGIFLOW|DIGITAPIFLOW, 
             ("Enter OidTapiGetCallStatus: hdCall=0x%x, TapiCallState=0x%x\n",
             GetCallStatus->hdCall,CallObject->TapiCallState) );

   CallStatus->ulNeededSize = sizeof(LINE_CALL_STATUS);

   if( CallStatus->ulNeededSize > CallStatus->ulTotalSize )
      return( NDIS_STATUS_TAPI_STRUCTURETOOSMALL );

   CallStatus->ulUsedSize = CallStatus->ulNeededSize;
   CallStatus->ulCallState = CallObject->TapiCallState;

   switch( CallObject->TapiCallState )
   {
      case LINECALLSTATE_IDLE:
         CallStatus->ulCallStateMode = 0;
			CallStatus->ulCallFeatures = LINECALLFEATURE_DIAL;
         break;

      case LINECALLSTATE_CONNECTED:
         CallStatus->ulCallStateMode = 0;
			CallStatus->ulCallFeatures = LINECALLFEATURE_DROP;
         break;

      case LINECALLSTATE_OFFERING:
         CallStatus->ulCallStateMode = 0;
			CallStatus->ulCallFeatures = LINECALLFEATURE_DROP|
			                             LINECALLFEATURE_ANSWER;
         break;

      case LINECALLSTATE_DISCONNECTED:
         CallStatus->ulCallStateMode = LINEDISCONNECTMODE_NORMAL;
			CallStatus->ulCallFeatures = LINECALLFEATURE_DROP;
			break;

      case LINECALLSTATE_BUSY:
         CallStatus->ulCallStateMode = LINEBUSYMODE_UNAVAIL;
			CallStatus->ulCallFeatures = LINECALLFEATURE_DROP;
         break;

      case LINECALLSTATE_SPECIALINFO:
         break;
   }

   CallStatus->ulDevSpecificSize = 0;
   CallStatus->ulDevSpecificOffset = 0;

   DigiDump( DIGIFLOW, 
             ("Exit OidTapiGetCallStatus: CallState=0x%x CallStateMode=0x%x\n",
             CallStatus->ulCallState, CallStatus->ulCallStateMode) );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiGetCallStatus



NDIS_STATUS OidTapiGetDevCaps( IN NDIS_HANDLE MiniportAdapterContext,
                               IN PVOID InformationBuffer,
                               IN ULONG InformationBufferLength,
                               OUT PULONG BytesWritten,
                               OUT PULONG BytesNeeded,
                               IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_GET_DEV_CAPS TapiDevCaps = (PVOID)InformationBuffer;
   PLINE_DEV_CAPS LineDevCaps;
   ULONG ulTotalSize, ulNeededSize, ulProviderNameSize, ulLineNameSize;
   CHAR LineNameString[32], ProviderNameString[32];
	ANSI_STRING AMediaTypeX25;

   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiGetDevCaps: DeviceID = 0x%x\n",
                        TapiDevCaps->ulDeviceID) );

	LineDevCaps = &TapiDevCaps->LineDevCaps;

   //
   // Save size of buffer
   //
   ulTotalSize = LineDevCaps->ulTotalSize;

   //
   // The format of ProviderName Info is:
   //    <media name>\0<device name>\0
   //       where <media name> can be X25
   //             <device name> can be DigiX25
   //
	RtlInitAnsiString( &AMediaTypeX25, MEDIA_TYPE_X25_ANSI );

   NdisZeroMemory( ProviderNameString, sizeof(ProviderNameString) );
   sprintf( ProviderNameString, "%s%c%s%c", 
                                AMediaTypeX25.Buffer, '\0', 
                                AdapterObject->ServiceName, '\0' );
   ulProviderNameSize = AMediaTypeX25.Length + 
                        strlen( AdapterObject->ServiceName ) + 
                        ( 3 * sizeof( CHAR ) );

   NdisZeroMemory( LineNameString, sizeof(LineNameString) );
   sprintf( LineNameString, "%s-%s",
                            AdapterObject->Name,
                            "Line0" );
   ulLineNameSize = strlen( LineNameString ) + sizeof(CHAR);

   ulNeededSize = sizeof(LINE_DEV_CAPS) +
                  ulProviderNameSize +
                  ulLineNameSize;

   if( LineDevCaps->ulTotalSize < ulNeededSize )
   {
      LineDevCaps->ulNeededSize = ulNeededSize;
      return( NDIS_STATUS_TAPI_STRUCTURETOOSMALL );
   }

	DigiDump( DIGIVERYLOUD, ("   LineName=%s, ProviderName=%s\n",
	                        LineNameString,
	                        ProviderNameString) );

   NdisZeroMemory( LineDevCaps, ulTotalSize );

   NdisMoveMemory( ((LPSTR)LineDevCaps) + sizeof(LINE_DEV_CAPS),
                   ProviderNameString,
                   ulProviderNameSize );
   LineDevCaps->ulProviderInfoSize = ulProviderNameSize;
   LineDevCaps->ulProviderInfoOffset = sizeof(LINE_DEV_CAPS);

   NdisMoveMemory( ((LPSTR)LineDevCaps) + 
                   sizeof(LINE_DEV_CAPS) +
                   LineDevCaps->ulProviderInfoSize,
                   LineNameString,
                   ulLineNameSize );

   LineDevCaps->ulLineNameSize = ulLineNameSize;
   LineDevCaps->ulLineNameOffset = sizeof(LINE_DEV_CAPS) + 
                                   LineDevCaps->ulProviderInfoSize;

   LineDevCaps->ulTotalSize = ulTotalSize;
   LineDevCaps->ulNeededSize = ulNeededSize;
   LineDevCaps->ulUsedSize = ulNeededSize;

   LineDevCaps->ulPermanentLineID = TapiDevCaps->ulDeviceID;
   LineDevCaps->ulStringFormat = STRINGFORMAT_ASCII;
   LineDevCaps->ulAddressModes = LINEADDRESSMODE_ADDRESSID;

   TapiDevCaps->LineDevCaps.ulNumAddresses = AdapterObject->NumberOfEndpoints;
   TapiDevCaps->LineDevCaps.ulBearerModes = LINEBEARERMODE_DATA;

   TapiDevCaps->LineDevCaps.ulMaxRate = 2048000;
   TapiDevCaps->LineDevCaps.ulMediaModes = LINEMEDIAMODE_DIGITALDATA;
   TapiDevCaps->LineDevCaps.ulMaxNumActiveCalls = 256;

   TapiDevCaps->LineDevCaps.ulLineStates = LINEDEVSTATE_RINGING;
	TapiDevCaps->LineDevCaps.ulRingModes = 1;

   DigiDump( DIGIFLOW, ("Exit OidTapiGetDevCaps\n") );
   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiGetDevCaps



NDIS_STATUS OidTapiGetId( IN NDIS_HANDLE MiniportAdapterContext,
                          IN PVOID InformationBuffer,
                          IN ULONG InformationBufferLength,
                          OUT PULONG BytesWritten,
                          OUT PULONG BytesNeeded,
                          IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_GET_ID GetId=(PVOID)InformationBuffer;
   PDIGIWAN_CALL_OBJECT CallObject;
   PVOID VarStringOffset;
   ULONG AvailMem, StringLength;
   VAR_STRING *DeviceID;
	STRING DeviceClassString;
	STRING TapiLineString;
	STRING NdisString;

   DigiDump( DIGIFLOW, ("OidTapiGetId: Select = 0x%x, hdLine = 0x%x, AddressID = 0x%x, hdCall = 0x%x\n",
                        GetId->ulSelect,
                        GetId->hdLine,
                        GetId->ulAddressID,
                        GetId->hdCall) );

   CallObject = (PVOID)GetId->hdCall;
	if( CallObject == NULL )
	{
		DigiDump( DIGIERRORS, ("Error: OidTapiGetId CallObject = 0\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}
   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

   //
   // We need to make sure the device class specified is correct.
   //
   if( GetId->ulDeviceClassSize == 0 )
      return( NDIS_STATUS_TAPI_INVALDEVICECLASS );

	RtlInitString( &DeviceClassString, ((PUCHAR)GetId) + GetId->ulDeviceClassOffset );
	RtlInitString( &TapiLineString, "tapi/line" );
	RtlInitString( &NdisString, "ndis" );
   DeviceID = &GetId->DeviceID;

   switch( GetId->ulSelect )
   {
      case LINECALLSELECT_LINE:

         if ( DigiEqualString( &DeviceClassString, &TapiLineString, TRUE ) )
         {
            AvailMem = DeviceID->ulTotalSize - sizeof(VAR_STRING);
            StringLength = (AvailMem > 4) ? 4 : AvailMem;
            DeviceID->ulNeededSize = sizeof(VAR_STRING) + 4;
            DeviceID->ulUsedSize = sizeof(VAR_STRING) + StringLength;
            DeviceID->ulStringFormat = STRINGFORMAT_BINARY;
            DeviceID->ulStringSize = StringLength;

            VarStringOffset = ((CHAR*)&GetId->DeviceID) + sizeof(VAR_STRING);
            NdisMoveMemory(VarStringOffset, &AdapterObject->Line.DeviceID, StringLength);
            DeviceID->ulStringOffset = sizeof(VAR_STRING);
            return(NDIS_STATUS_SUCCESS);
         }
         break;

      case LINECALLSELECT_ADDRESS:

         if( GetId->ulAddressID >= AdapterObject->NumberOfEndpoints )
         {
            DigiDump( DIGIFLOW, ("returning NDIS_STATUS_TAPI_INVALADDRESSID\n") );
            return(NDIS_STATUS_TAPI_INVALADDRESSID);
         }

			// Currently no defined return value for this request.  Just a placeholder
			// for future use.
			return( NDIS_STATUS_TAPI_INVALDEVICECLASS );

      case LINECALLSELECT_CALL:

         if( CallObject->TapiCallState != LINECALLSTATE_CONNECTED )
         {
            DigiDump( DIGIFLOW, ("returning NDIS_STATUS_TAPI_INVALCALLHANDLE\n") );
            return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
         }

         if( DigiEqualString( &DeviceClassString, &NdisString, TRUE ) )
         {
            AvailMem = DeviceID->ulTotalSize - sizeof(VAR_STRING);
            StringLength = (AvailMem > 4) ? 4 : AvailMem;
            DeviceID->ulNeededSize = sizeof(VAR_STRING) + 4;
            DeviceID->ulUsedSize = sizeof(VAR_STRING) + StringLength;
            DeviceID->ulStringFormat = STRINGFORMAT_BINARY;
            DeviceID->ulStringSize = StringLength;

            VarStringOffset = ((CHAR*)GetId) + sizeof(NDIS_TAPI_GET_ID);
            NdisMoveMemory( VarStringOffset, &CallObject->htCall, StringLength );
            DeviceID->ulStringOffset = sizeof(VAR_STRING);

            return(NDIS_STATUS_SUCCESS);
         }
         break;
   }

   DigiDump( DIGIFLOW, ("returning NDIS_STATUS_TAPI_INVALDEVICECLASS\n") );
   return( NDIS_STATUS_TAPI_INVALDEVICECLASS );

}  // end OidTapiGetId



NDIS_STATUS OidTapiGetLineDevStatus( IN NDIS_HANDLE MiniportAdapterContext,
                                     IN PVOID InformationBuffer,
                                     IN ULONG InformationBufferLength,
                                     OUT PULONG BytesWritten,
                                     OUT PULONG BytesNeeded,
                                     IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_GET_LINE_DEV_STATUS GetLineDevStatus=(PVOID)InformationBuffer;

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Entering OidTapiGetLineDevStatus\n") );
   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   GetLineDevStatus->LineDevStatus.ulNeededSize = sizeof(LINE_DEV_STATUS);
   if( GetLineDevStatus->LineDevStatus.ulTotalSize < sizeof(LINE_DEV_STATUS) )
      return( NDIS_STATUS_INVALID_LENGTH );

   //
   // NOTE: the following fields are filled in by TAPI, so don't
   //       touch them:
   //
   //          ulTotalSize
   //          ulNumOpens
   //          ulOpenMediaModes
   //

   GetLineDevStatus->LineDevStatus.ulUsedSize = sizeof(LINE_DEV_STATUS);
   GetLineDevStatus->LineDevStatus.ulNumActiveCalls = 0;

	GetLineDevStatus->LineDevStatus.ulNumActiveCalls = DigiWanCountActiveCalls();

   GetLineDevStatus->LineDevStatus.ulNumOnHoldCalls = 0;
   GetLineDevStatus->LineDevStatus.ulNumOnHoldPendCalls = 0;
   GetLineDevStatus->LineDevStatus.ulLineFeatures = 0;
   GetLineDevStatus->LineDevStatus.ulNumCallCompletions = 0;

   GetLineDevStatus->LineDevStatus.ulRingMode = 
   	AdapterObject->Line.CurrentState & LINEDEVSTATE_RINGING ? 1 : 0;
   GetLineDevStatus->LineDevStatus.ulSignalLevel = 0x0000FFFF;
   GetLineDevStatus->LineDevStatus.ulBatteryLevel = 0x0000FFFF;
   GetLineDevStatus->LineDevStatus.ulRoamMode = LINEROAMMODE_UNAVAIL;
   GetLineDevStatus->LineDevStatus.ulDevStatusFlags = LINEDEVSTATUSFLAGS_CONNECTED;
   GetLineDevStatus->LineDevStatus.ulTerminalModesSize = 0;
   GetLineDevStatus->LineDevStatus.ulTerminalModesOffset = 0;
   GetLineDevStatus->LineDevStatus.ulDevSpecificSize = 0;
   GetLineDevStatus->LineDevStatus.ulDevSpecificOffset = 0;

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

	return NDIS_STATUS_SUCCESS;
}  // end OidTapiGetLineDevStatus



NDIS_STATUS OidTapiMakeCall( IN NDIS_HANDLE MiniportAdapterContext,
                             IN PVOID InformationBuffer,
                             IN ULONG InformationBufferLength,
                             OUT PULONG BytesWritten,
                             OUT PULONG BytesNeeded,
                             IN BOOLEAN QueryInfo )
/*++

Routine Description:

   Initiates an X.25 call.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_MAKE_CALL MakeCall=(PVOID)InformationBuffer;
   PDIGIWAN_CALL_OBJECT CallObject;
   PLINE_CALL_PARAMS CallParams;
   NDIS_STATUS Status;
   ULONG AddressID;

   DigiDump( DIGIFLOW|DIGITAPIFLOW|DIGIMAKECALL, 
             ("Enter OidTapiMakeCall: hdLine=0x%x htCall=0x%x\n", 
             MakeCall->hdLine, MakeCall->htCall) );

   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   CallParams = &MakeCall->LineCallParams;

	DigiDump( DIGIDIAG1, ("OidTapiMakeCall:\n"
	                      "   ulRequestID = 0x%x\n"
	                      "   hdLine = 0x%x\n"
	                      "   htCall = 0x%x\n"
	                      "   hdCall = 0x%x\n"
	                      "   ulDestAddressSize = 0x%x\n"
	                      "   ulDestAddressOffset = 0x%x\n",
	                      MakeCall->ulRequestID,
	                      MakeCall->hdLine,
	                      MakeCall->htCall,
	                      MakeCall->hdCall,
	                      MakeCall->ulDestAddressSize,
	                      MakeCall->ulDestAddressOffset) );
	DigiDump( DIGIDIAG1, ("   bUseDefaultLineCallParams = 0x%x\n"
								 "   LineCallParams\n"
	                      "      ulTotalSize = 0x%x\n"
	                      "      ulBearerMode = 0x%x\n"
	                      "      ulMinRate = 0x%x\n"
	                      "      ulMaxRate = 0x%x\n"
	                      "      ulMediaMode = 0x%x\n"
	                      "      ulCallParamFlags = 0x%x\n"
	                      "      ulAddressMode = 0x%x\n"
	                      "      ulAddressID = 0x%x\n"
	                      "      DialParams\n"
	                      "         ulDialPause = 0x%x\n"
	                      "         ulDialSpeed = 0x%x\n"
	                      "         ulDigitDuration = 0x%x\n"
	                      "         ulWaitForDialtone = 0x%x\n",
	                      MakeCall->bUseDefaultLineCallParams,
	                      CallParams->ulTotalSize,
	                      CallParams->ulBearerMode,
	                      CallParams->ulMinRate,
	                      CallParams->ulMaxRate,
	                      CallParams->ulMediaMode,
	                      CallParams->ulCallParamFlags,
	                      CallParams->ulAddressMode,
	                      CallParams->ulAddressID,
	                      CallParams->DialParams.ulDialPause,
	                      CallParams->DialParams.ulDialSpeed,
	                      CallParams->DialParams.ulDigitDuration,
	                      CallParams->DialParams.ulWaitForDialtone) );
	DigiDump( DIGIDIAG1, ("      ulOrigAddressSize   = 0x%x\n"
	                      "      ulOrigAddressOffset = 0x%x\n"
	                      "      ulDisplayableAddressSize   = 0x%x\n"
	                      "      ulDisplayableAddressOffset = 0x%x\n"
	                      "      ulCalledPartySize   = 0x%x\n"
	                      "      ulCalledPartyOffset = 0x%x\n"
	                      "      ulCommentSize   = 0x%x\n"
	                      "      ulCommentOffset = 0x%x\n",
	                      CallParams->ulOrigAddressSize,
	                      CallParams->ulOrigAddressOffset,
	                      CallParams->ulDisplayableAddressSize,
	                      CallParams->ulDisplayableAddressOffset,
	                      CallParams->ulCalledPartySize,
	                      CallParams->ulCalledPartyOffset,
	                      CallParams->ulCommentSize,
	                      CallParams->ulCommentOffset) );
	DigiDump( DIGIDIAG1, ("      ulUserUserInfoSize   = 0x%x\n"
	                      "      ulUserUserInfoOffset = 0x%x\n"
	                      "      ulHighLevelCompSize   = 0x%x\n"
	                      "      ulHighLevelCompOffset = 0x%x\n"
	                      "      ulLowLevelCompSize   = 0x%x\n"
	                      "      ulLowLevelCompOffset = 0x%x\n"
	                      "      ulDevSpecificSize   = 0x%x\n"
	                      "      ulDevSpecificOffset = 0x%x\n",
	                      CallParams->ulUserUserInfoSize,
	                      CallParams->ulUserUserInfoOffset,
	                      CallParams->ulHighLevelCompSize,
	                      CallParams->ulHighLevelCompOffset,
	                      CallParams->ulLowLevelCompSize,
	                      CallParams->ulLowLevelCompOffset,
	                      CallParams->ulDevSpecificSize,
	                      CallParams->ulDevSpecificOffset) );
#if DBG
	{
		ULONG nBytes, n;
		nBytes = MakeCall->ulDestAddressSize+MakeCall->ulDestAddressOffset;
		if ( nBytes < ( n = CallParams->ulOrigAddressOffset + CallParams->ulOrigAddressSize ) ) nBytes = n;
		if ( nBytes < ( n = CallParams->ulDisplayableAddressSize + CallParams->ulDisplayableAddressOffset ) ) nBytes = n;
		if ( nBytes < ( n = CallParams->ulCalledPartySize + CallParams->ulCalledPartyOffset ) ) nBytes = n;
		if ( nBytes < ( n = CallParams->ulCommentSize + CallParams->ulCommentOffset ) ) nBytes = n;
		if ( nBytes < ( n = CallParams->ulUserUserInfoSize + CallParams->ulUserUserInfoOffset ) ) nBytes = n;
		if ( nBytes < ( n = CallParams->ulHighLevelCompSize + CallParams->ulHighLevelCompOffset ) ) nBytes = n;
		if ( nBytes < ( n = CallParams->ulLowLevelCompSize + CallParams->ulLowLevelCompOffset ) ) nBytes = n;
		if ( nBytes < ( n = CallParams->ulDevSpecificSize + CallParams->ulDevSpecificOffset ) ) nBytes = n;
		DigiDump( DIGIDIAG1, ("OidTapiMakeCall Byte Dump (Length 0x%x):\n",nBytes) );
		DigiDumpData( DIGIDIAG1, (PVOID)MakeCall, nBytes );
	}
#endif

   if( MakeCall->ulDestAddressSize == 0 || MakeCall->ulDestAddressSize > DTEMAXSIZE )
   {
   	DigiDump( DIGIERRORS, ("Error: OidTapiMakeCall bad dest address length!\n") );
      return( NDIS_STATUS_TAPI_INVALADDRESS );
   }

   // Verify the requested local address
   if ( CallParams->ulAddressMode == LINEADDRESSMODE_ADDRESSID )
   {
      if( CallParams->ulAddressID >= AdapterObject->NumberOfEndpoints )
      {
         DigiDump( DIGIERRORS, 
                   ( "OidTapiMakeCall Error: Unsupported ulAddressID (0x%x)!\n",
                   CallParams->ulAddressID ) );
         return( NDIS_STATUS_TAPI_INVALADDRESSID );
      }

		AddressID = CallParams->ulAddressID;
	}
	else
	{
		Status = GetAddressIdFromAddress( ((PUCHAR)CallParams) + 
		                                  CallParams->ulOrigAddressOffset,
		                                  CallParams->ulOrigAddressSize,
		                                  &AddressID );
		if ( !NT_SUCCESS( Status ) )
			return( NDIS_STATUS_TAPI_INVALADDRESS );
   }

	DigiDump( DIGICALLCTRL, ("DIGIWAN: MakeCall on Address 0x%x\n", AddressID) );

	CallObject = NewCallObject( AdapterObject, AddressID );
	if ( !CallObject )
	{
      DigiDump( DIGIERRORS, ( "OidTapiMakeCall Error: CallObject already in use!\n" ) );
		return( NDIS_STATUS_TAPI_INUSE );
	}

	DigiDump( DIGIOPENCALLS, ( "Open: callobject 0x%x, AddressID 0x%x\n", 
	                         CallObject,
	                         AddressID ) );

	CallObject->htCall = MakeCall->htCall;

   if( !MakeCall->bUseDefaultLineCallParams )
   {
      // check media mode and make sure we support it
      if( CallParams->ulMediaMode & ~CallObject->MediaModes )
      {
//------------------------------------------------------------ MLZ 01/29/97 {
// NT 4.0 sends bad info here.  Try allowing it...
/*
         DigiDump( DIGIERRORS, 
                   ( "OidTapiMakeCall Error: Unsupported MediaMode 0x%x!\n",
                   CallParams->ulMediaMode ) );
         return(NDIS_STATUS_TAPI_INVALMEDIAMODE);
*/
//------------------------------------------------------------ MLZ 01/29/97 }
      }
      CallObject->CurMediaMode = CallParams->ulMediaMode;

      // check bearer mode and make sure we support it
      if( CallParams->ulBearerMode & ~CallObject->BearerModes )
      {
//------------------------------------------------------------ MLZ 03/01/97 {
// NT 4.0 sends bad info here.  Try allowing it...
/*
         DigiDump( DIGIERRORS, 
                   ( "OidTapiMakeCall Error: Unsupported BearerMode 0x%x!\n",
                   CallParams->ulBearerMode ) );
         return(NDIS_STATUS_TAPI_RESOURCEUNAVAIL);
*/
//------------------------------------------------------------ MLZ 03/01/97 }
      }
      CallObject->CurBearerMode = CallParams->ulBearerMode;
   }

   MakeCall->hdCall = (HDRV_CALL)CallObject;
   CallObject->htCall = MakeCall->htCall;

	// Store CalledAddress, stripping off terminating NULL
	CallObject->RemoteAddressLength = MakeCall->ulDestAddressSize;
	NdisMoveMemory( CallObject->RemoteAddress, 
	                (PUCHAR)MakeCall + MakeCall->ulDestAddressOffset,
	                CallObject->RemoteAddressLength );

	// Use the X.25 Router to determine the physical adapter (hDevice) and 
	// line (Snid) on which the call should be initiated.
	Status = X25Router( &CallObject->PhysAdapter, 
	                    &CallObject->Snid, 
	                    CallObject->RemoteAddress,
	                    CallObject->RemoteAddressLength );
	if ( !NT_SUCCESS( Status ) )
	{
		Status = NDIS_STATUS_TAPI_INVALPARAM;
		goto MakeCallExitErr;
	}

	DigiDump( DIGIVERYLOUD, ("MakeCall: Opening stream, CallObject 0x%x\n",
	                        CallObject ) );

   // Open a stream on which we'll place this call
	Status = DsOpenEx( CallObject->PhysAdapter->hDevice, X25_DEVICE_NAME, 
	                   CallObject, 0,0, MakeCallOpenComplete );
   if( !NT_SUCCESS( Status ) )
   {
		DigiDump( DIGIERRORS, 
		          ("OidTapiMakeCall: error opening stream, status NDIS_STATUS_TAPI_INUSE\n") );
		Status = NDIS_STATUS_TAPI_INUSE;
		goto MakeCallExitErr;
   }

   DigiDump( DIGIFLOW|DIGIMAKECALL, 
             ("Exit OidTapiMakeCall, AddressID 0x%x\n", CallObject->AddressID) );
	return( NDIS_STATUS_SUCCESS );
	
MakeCallExitErr:;

	FreeCallObject( CallObject );
   DigiDump( DIGIFLOW|DIGIERRORS, ("ExitErr OidTapiMakeCall, status 0x%x on AddressID 0x%x\n", 
             Status, AddressID) );
	ASSERT( Status != STATUS_PENDING );
	ASSERT( Status != STATUS_SUCCESS );
	return( Status );
}  // end OidTapiMakeCall



NDIS_STATUS OidTapiOpen( IN NDIS_HANDLE MiniportAdapterContext,
                         IN PVOID InformationBuffer,
                         IN ULONG InformationBufferLength,
                         OUT PULONG BytesWritten,
                         OUT PULONG BytesNeeded,
                         IN BOOLEAN QueryInfo )
/*++

Routine Description:

   Called once for the single virtual line we expose to tapi.  Attempt to
	access each physical adapter owned by this virtual line by alloc'ing and
	opening a stream on each adapter, but don't send down the listen request
	yet; that'll be done in OidTapiSetDefaultMediaDetection.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_OPEN Open=(PVOID)InformationBuffer;

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiOpen\n") );
   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

	if ( Open->ulDeviceID != AdapterObject->Line.DeviceID )
		return( NDIS_STATUS_TAPI_NODRIVER );

	AdapterObject->Line.htLine = Open->htLine;

   Open->hdLine = (ULONG_PTR)AdapterObject;

   DigiDump( DIGIFLOW, ("Exit OidTapiOpen\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiOpen



NDIS_STATUS OidTapiProviderInitialize( IN NDIS_HANDLE MiniportAdapterContext,
                                       IN PVOID InformationBuffer,
                                       IN ULONG InformationBufferLength,
                                       OUT PULONG BytesWritten,
                                       OUT PULONG BytesNeeded,
                                       IN BOOLEAN QueryInfo )
/*++

Routine Description:

	Called once at init time.  Given the DeviceBaseID, must return 
	ulNumLineDevs, ulProviderID.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_PROVIDER_INITIALIZE Initialize=InformationBuffer;

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiProviderInitialize, ProviderID = 0x%x\n") );

   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

   // Save a copy of our Base ID.
   AdapterObject->TapiBaseID = Initialize->ulDeviceIDBase;
	AdapterObject->Line.DeviceID = Initialize->ulDeviceIDBase;

   Initialize->ulProviderID = (ULONG_PTR)AdapterObject;
	Initialize->ulNumLineDevs = 1;

   DigiDump( DIGIFLOW, ("Exit OidTapiProviderInitialize\n") );

   return( NDIS_STATUS_SUCCESS );

}  // end OidTapiProviderInitialize



NDIS_STATUS OidTapiProviderShutdown( IN NDIS_HANDLE MiniportAdapterContext,
                                     IN PVOID InformationBuffer,
                                     IN ULONG InformationBufferLength,
                                     OUT PULONG BytesWritten,
                                     OUT PULONG BytesNeeded,
                                     IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiProviderShutdown\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** OidTapiProviderShutdown not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exit OidTapiProviderShutdown\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiProviderShutdown



NDIS_STATUS OidTapiSetAppSpecific( IN NDIS_HANDLE MiniportAdapterContext,
                                   IN PVOID InformationBuffer,
                                   IN ULONG InformationBufferLength,
                                   OUT PULONG BytesWritten,
                                   OUT PULONG BytesNeeded,
                                   IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   PNDIS_TAPI_SET_APP_SPECIFIC SetAppSpecific;
   PDIGIWAN_CALL_OBJECT CallObject;


   SetAppSpecific = (PNDIS_TAPI_SET_APP_SPECIFIC)InformationBuffer;

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiSetAppSpecific: hdCall = 0x%x\n",
                        SetAppSpecific->hdCall) );

   CallObject = (PDIGIWAN_CALL_OBJECT)SetAppSpecific->hdCall;
	if( CallObject == NULL )
	{
		DigiDump( DIGIERRORS, ("Error: OidTapiSetAppSpecific CallObject = 0\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}
   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

   CallObject->AppSpecific = SetAppSpecific->ulAppSpecific;

   DigiDump( DIGIFLOW, ("Exit OidTapiSetAppSpecific\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiSetAppSpecific



NDIS_STATUS OidTapiSetCallParams( IN NDIS_HANDLE MiniportAdapterContext,
                                  IN PVOID InformationBuffer,
                                  IN ULONG InformationBufferLength,
                                  OUT PULONG BytesWritten,
                                  OUT PULONG BytesNeeded,
                                  IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiSetCallParams\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** OidTapiSetCallParams not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exit OidTapiSetCallParams\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiSetCallParams



NDIS_STATUS OidTapiSetDefaultMediaDetection( IN NDIS_HANDLE MiniportAdapterContext,
                                             IN PVOID InformationBuffer,
                                             IN ULONG InformationBufferLength,
                                             OUT PULONG BytesWritten,
                                             OUT PULONG BytesNeeded,
                                             IN BOOLEAN QueryInfo )
/*++

Routine Description:

	Called once per virtual line (we expose only 1 virtual line).  Send a 
	listen down each physical adapter associated with this virtual line.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject=(PVOID)MiniportAdapterContext;
   PNDIS_TAPI_SET_DEFAULT_MEDIA_DETECTION SetMediaDetection=InformationBuffer;
   NDIS_STATUS Status;
	PLIST_ENTRY Entry;
	BOOLEAN bSuccess = FALSE;

   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiSetDefaultMediaDetection\n") );

   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );
	ASSERT( SetMediaDetection->hdLine == (ULONG_PTR)AdapterObject );

	if( SetMediaDetection->hdLine != (ULONG_PTR)AdapterObject )
	{
		DigiDump( DIGIERRORS, ("OidTapiSetDefaultMediaDetection: invalid hdLine\n") );
		return( NDIS_STATUS_TAPI_INVALCALLHANDLE );
	}

	if ( IsListEmpty( &AdapterObject->PhysAdapters ) )
		return( NDIS_STATUS_TAPI_NODEVICE );

	Entry = &AdapterObject->PhysAdapters;
	while ( Entry->Flink != &AdapterObject->PhysAdapters )
	{
		PDIGIWAN_PHYS_ADAPTER PhysAdapter;
		PhysAdapter = CONTAINING_RECORD( Entry->Flink,
		                                 DIGIWAN_PHYS_ADAPTER,
		                                 List );
		Entry = Entry->Flink; // done with Entry for this iteration, set to next entry

		ASSERT( PhysAdapter->Type == DIGIWAN_TYPE_PHYS_ADAPTER );
		if ( !PhysAdapter->hListenStream )
		{
			Status = DsOpenEx( PhysAdapter->hDevice, X25_DEVICE_NAME,
					             PhysAdapter, 0,0, SetDefaultMediaComplete );
			if ( !NT_SUCCESS( Status ) )
				continue;
		}
		else
		{
			// Send out the Listen
			SendListen(PhysAdapter);
		}

		// This listen request and getmsg were sent successfully!  Incoming
		// calls will be processed.
		bSuccess = TRUE;
	}

   DigiDump( DIGIFLOW, ("Exit OidTapiSetDefaultMediaDetection\n") );

	if ( !bSuccess )
		return( NDIS_STATUS_TAPI_NODEVICE );
   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiSetDefaultMediaDetection



NDIS_STATUS OidTapiSetMediaMode( IN NDIS_HANDLE MiniportAdapterContext,
                                IN PVOID InformationBuffer,
                                IN ULONG InformationBufferLength,
                                OUT PULONG BytesWritten,
                                OUT PULONG BytesNeeded,
                                IN BOOLEAN QueryInfo )
/*++

Routine Description:

--*/
{
   DigiDump( DIGIFLOW|DIGITAPIFLOW, ("Enter OidTapiSetMediaMode\n") );
   DigiDump( DIGINOTIMPLEMENTED, ("*** SetMediaMode not implemented! ***\n") );
   DigiDump( DIGIFLOW, ("Exit OidTapiSetMediaMode\n") );

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiSetMediaMode



NDIS_STATUS OidTapiStatusMessages( IN NDIS_HANDLE MiniportAdapterContext,
                                   IN PVOID InformationBuffer,
                                   IN ULONG InformationBufferLength,
                                   OUT PULONG BytesWritten,
                                   OUT PULONG BytesNeeded,
                                   IN BOOLEAN QueryInfo )
/*++

Routine Description:

Arguments:


   QueryInfo - Indicates SET or GET.
               If TRUE, OID_WAN_GET_COMP_INFO.
               If FALSE, OID_WAN_SET_COMP_INFO.

Return Value:

   None.

--*/
{
   PNDIS_TAPI_SET_STATUS_MESSAGES SetStatusMessages = InformationBuffer;
	PDIGIWAN_ADAPTER_OBJECT AdapterObject = MiniportAdapterContext;

	ASSERT( (PVOID)AdapterObject == (PVOID)SetStatusMessages->hdLine );

   DigiDump( DIGIFLOW|DIGITAPIFLOW, 
             ("OidTapiStatusMessages: ulLineStates 0x%x, ulAddressStates 0x%x\n",
             SetStatusMessages->ulLineStates,
             SetStatusMessages->ulAddressStates) );

	// We support RINGING and REINIT, but we never do anything with REINIT.
	// NT 4.0 requests both of these.
	ASSERT( !( SetStatusMessages->ulLineStates & 
              ~( LINEDEVSTATE_REINIT | LINEDEVSTATE_RINGING ) ) );
	AdapterObject->Line.NotifyStates = SetStatusMessages->ulLineStates;

   return( NDIS_STATUS_SUCCESS );
}  // end OidTapiStatusMessages



NTSTATUS
GetAddressIdFromAddress(
	IN PUCHAR Address,
	IN ULONG Length,
	OUT PULONG pAddressId )
/*++

Description:

	Translates an ascii string representation of adapter-line-endpoint to an 
	integer AddressID.

Parameters:

	Address    - String
	Length	  - Length of string
	pAddressId - Output location to write the AddressID 

Return Value:

	None

--*/
{
	// Build the AddressId from the OrigAddress.  No guarantee that the OrigAddress
	// is a null-terminated string, so move it to a temp buffer first with room to
	// null-terminate it.
	PUCHAR NewBuffer;
	ULONG Offset, i;
	ULONG Value;
	NDIS_STATUS Status;

	NewBuffer = DigiAllocMem( NonPagedPool, Length + 1 );
	if ( !NewBuffer )
		return( NDIS_STATUS_RESOURCES );
	
	NdisZeroMemory( NewBuffer, Length + 1 ); // null terminate it
	NdisMoveMemory( NewBuffer, Address, Length );

	// The OrigAddress string is of the form: [adapter]-[line]-[endpoint].  Endpoint
	// defines the AddressId.  Skip over the adapter and line and get to the endpoint.
	Offset = 0;
	while ( NewBuffer[ Offset++ ] != '-' );
	while ( NewBuffer[ Offset++ ] != '-' );

	// Validate the string of decimal numbers
	for ( Status = NDIS_STATUS_SUCCESS, i = Offset; i < Length; i++ )
	{
		if ( NewBuffer[Offset] < '0' || NewBuffer[Offset] > '9' )
		{
	      DigiDump( DIGIERRORS, ("GetAddressIdFromAddress Error: Invalid Address!\n"));
			Status = NDIS_STATUS_TAPI_INVALADDRESS;
			break;
		}
	}

	// Convert the string to decimal
	for ( *pAddressId = 0, i = Offset; i < Length; i++ )
	{
		*pAddressId *= 10;
		*pAddressId += NewBuffer[i] - '0';
	}

	DigiFreeMem( NewBuffer );
	return( Status );
}

VOID
SendListen( 
	IN PDIGIWAN_PHYS_ADAPTER pPhysAdapter	)
/*++

Description:

	Send a Listen to the Adapter

Parameters:

	pPhysAdapter
		Pointer to the Adapter

--*/
{
	PUCHAR pData;
	NTSTATUS Status;

	DigiDump( DIGIFLOW|DIGITAPIFLOW, 
	          ("Enter SendListen, pPhysAdapter 0x%x\n", pPhysAdapter) );

	// At this point, we've got an open stream with no pending listen.
	// Initiate a new listen request

	Status = ListenRequest( pPhysAdapter->hListenStream );
	if ( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERRORS, ("SendListen: ListenRequest failed!\n") );
		return;	
	}

	// Wait for incoming listens by issuing a GetMsg request

	pData = DigiAllocMem( NonPagedPool, DEFAULT_EXTRA_DATA_SIZE );
	if ( !pData )
	{
		DigiDump( DIGIERRORS, ("SendListen: Alloc failed!\n") );
		return;
	}

	DigiDump( DIGIVERYLOUD, ("SetDefaultMediaComplete: Calling DsGetMsgEx\n") );
	Status = DsGetMsgEx( pPhysAdapter->hListenStream,
	                     sizeof( union X25_primitives ),
	                     pData, DEFAULT_EXTRA_DATA_SIZE,
	                     NULL, DS_PRIORITY_LOW, ListenGetMsgComplete );

	if ( !NT_SUCCESS( Status ) )
	{
		DigiDump( DIGIERRORS, ("SendListen: DsGetMsgEx failed!\n") );
	}
	
	DigiDump( DIGIFLOW|DIGITAPIFLOW, 
	          ("Leave SendListen, pPhysAdapter 0x%x, Status = 0x%x\n", pPhysAdapter,Status) );

}

VOID
SetDefaultMediaComplete( 
	IN HANDLE hOwner,
	IN HANDLE hStream,
	IN ULONG Error,
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiSetDefaultMediaDetection request to 
	DsOpen completes.

Parameters:

	hOwner
		Pointer to the CallObject for this stream

	hStream
		Handle for the newly opened stream.  All subsequent actions on the stream
		require the use of this handle.

	IoStatus
		Status returned from DigiSxb on the Open request.

--*/
{
	PDIGIWAN_PHYS_ADAPTER pPhysAdapter = hOwner;

	ASSERT( pPhysAdapter->Type == DIGIWAN_TYPE_PHYS_ADAPTER );

	DigiDump( DIGIFLOW|DIGITAPIFLOW, 
	          ("Enter SetDefaultMediaComplete, hOwner 0x%x\n", hOwner) );

	if ( IoStatus != STATUS_SUCCESS || Error )
	{
		DigiDump( DIGIERRORS, 
		          ("SetDefaultMediaComplete: Open failed, Status 0x%x, Error 0x%x!\n",
		          IoStatus, Error ) );
		return;
	}

	ASSERT( hStream );
	pPhysAdapter->hListenStream = hStream;

	// Listen on this adapter
	SendListen(pPhysAdapter);

	DigiDump( DIGIFLOW, ( "Leave SetDefaultMediaComplete\n" ) );
}



VOID
MakeCallOpenComplete( 
	IN HANDLE hOwner,
	IN HANDLE hStream,
	IN ULONG Error,
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiMakeCall request to DsOpen completes.

Parameters:

	hOwner
		Pointer to the CallObject for this stream

	hStream
		Handle for the newly opened stream.  All subsequent actions on the stream
		require the use of this handle.

	IoStatus
		Status returned from DigiSxb on the Open request.

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = hOwner;

	ASSERT( CallObject && CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	DigiDump( DIGIFLOW|DIGITAPIFLOW|DIGIMAKECALL, 
	          ( "Enter MakeCallOpenComplete, Call 0x%x, Address 0x%x, hStream 0x%x, Status 0x%x\n",
	          CallObject, CallObject->AddressID, hStream, IoStatus ) );

#if DBG
	DigiDump( DIGIDIAG2, 
	          ("DIGIWAN: MakeCall on Address 0x%x, hStream 0x%x, Status 0x%x\n", 
             CallObject->AddressID, hStream, IoStatus) );
	DigiDump( DIGIDIAG5, ("MakeCallComplete: DsOpen hStream 0x%x, Address 0x%x\n",
	                     hStream, CallObject->AddressID) );
#endif

	if ( STATUS_SUCCESS == IoStatus )
	{
		CallObject->hStream = hStream;

		AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
		                             LINECALLSTATE_PROCEEDING, 0, 0 );

		// ConnectRequest sends a N_CI PutMsg request down and MakeCallConnectComplete is 
		// called back when the request has been sent.
	   ConnectRequest( CallObject->hStream, CallObject->Snid,
	                   CallObject->RemoteAddress,  
	                   CallObject->RemoteAddressLength,
	                   MakeCallConnectComplete );
	}
	else
	{
		// Some failure occurred, notify tapi
		DigiDump( DIGIERRORS, 
		          ("*** MakeCallOpenComplete error.  Open Status 0x%x, Error 0x%x\n", 
		          IoStatus, Error) );
		CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
		AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
		                             LINECALLSTATE_DISCONNECTED,
		                             LINEDISCONNECTMODE_UNAVAIL, 0 );
	}
	DigiDump( DIGIFLOW, ( "Leave MakeCallOpenComplete\n" ) );
}



VOID
MakeCallConnectComplete( 
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PUCHAR pControl,
	IN ULONG ControlLength, 
	IN PUCHAR pData,
	IN ULONG DataLength, 
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiMakeCall N_CI (X25 ConnectRequest) request 
	via DsPutMsgEx completes.

Parameters:

	hOwner
		Pointer to the CallObject for this stream

	Context, pControl, ControlLength, 
		Not used
	
	pData
		Data alloc'd in ConnectRequest and must be freed.

	IoStatus
		Status returned from DigiSxb on the Open request.

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = hOwner;
	NTSTATUS Status;

	ASSERT( CallObject && CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	DigiDump( DIGIFLOW|DIGITAPIFLOW|DIGIMAKECALL, 
	          ( "Enter MakeCallConnectComplete, Call 0x%x, Address 0x%x, Status 0x%x\n",
	          CallObject, CallObject->AddressID, IoStatus ) );
	ASSERT( pData );
	DigiFreeMem( pData );

	if ( STATUS_SUCCESS == IoStatus )
	{
		// We dialed successfully...waiting for a response
		PUCHAR pGetData;
		CallObject->InternalCallState = INTERNAL_CALL_STATE_PROCEEDING;
		pGetData = DigiAllocMem( NonPagedPool, DEFAULT_EXTRA_DATA_SIZE );
		if ( !pGetData )
		{
			Status = STATUS_INSUFFICIENT_RESOURCES;
		}
		else
		{
			DigiDump( DIGIVERYLOUD, ("MakeCallConnectComplete: Calling DsGetMsgEx\n") );
			Status = DsGetMsgEx( CallObject->hStream,
			                     sizeof( union X25_primitives ),
			                     pGetData,
			                     DEFAULT_EXTRA_DATA_SIZE,
			                     NULL, DS_PRIORITY_LOW,
			                     X25GetMsgComplete );
		}
	}

	if ( !NT_SUCCESS( IoStatus ) || !NT_SUCCESS( Status ) )
	{
		// Some failure occurred, notify tapi
		DigiDump( DIGIERRORS, 
		          ("*** MakeCallConnectComplete error.  Connect Status 0x%x, GetMsg Status 0x%x\n", 
		          IoStatus, Status) );
		CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
		AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
		                             LINECALLSTATE_DISCONNECTED,
		                             LINEDISCONNECTMODE_UNAVAIL, 0 );
	}
	DigiDump( DIGIFLOW, ( "Leave MakeCallConnectComplete\n" ) );
}



VOID
AnswerOpenComplete( 
	IN HANDLE hOwner,
	IN HANDLE hStream,
	IN ULONG Error,
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiAnswer DsOpen request completes.

Parameters:

	hOwner
		Pointer to the CallObject for this stream

	hStream
		Handle for the newly opened stream.  All subsequent actions on the stream
		require the use of this handle.

	IoStatus
		Status returned from DigiSxb on the Open request.

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = hOwner;
	struct xccnff CallConfirm;
	NTSTATUS Status;

	ASSERT( CallObject && CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	DigiDump( DIGIFLOW|DIGITAPIFLOW, 
	          ( "Enter AnswerOpenComplete, Call 0x%x, Address 0x%x, Status 0x%x\n",
	          CallObject, CallObject->AddressID, IoStatus ) );

	if ( STATUS_SUCCESS == IoStatus )
	{
		CallObject->hStream = hStream;

	   //
	   // Send an X.25 Call Confirm to answer the incoming call!
	   //

		NdisZeroMemory( &CallConfirm, sizeof( CallConfirm ) );
	   CallConfirm.xl_type = XL_CTL;
	   CallConfirm.xl_command = N_CC;
	   CallConfirm.conn_id = CallObject->conn_id;
	   // CallConfirm->CONS_call = 0;
	   // CallConfirm->negotiate_qos = 0;

		Status = DsPutMsgEx( CallObject->hStream,
		                     (PVOID)&CallConfirm,
		                     sizeof( struct xccnff ),
		                     NULL, 0, NULL, DS_PRIORITY_LOW,
		                     AnswerConfirmComplete );
	}

	if ( !NT_SUCCESS( IoStatus ) || !NT_SUCCESS( Status ) )
	{
		// Some failure occurred, reject the call
		CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
		RejectIncoming( CallObject->PhysAdapter, CallObject->conn_id );
		CallObject->conn_id = 0;

		// Notify tapi of the failure
		CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
		AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
		                             LINECALLSTATE_DISCONNECTED,
		                             LINEDISCONNECTMODE_UNAVAIL, 0 );
	}
	else
	{
		// Do nothing here, wait for completion in AnswerConfirmComplete...
	}
	DigiDump( DIGIFLOW, ( "Leave AnswerOpenComplete\n" ) );
}



VOID
AnswerConfirmComplete( 
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PUCHAR pControl,
	IN ULONG ControlLength, 
	IN PUCHAR pData,
	IN ULONG DataLength, 
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiAnswer N_CC (X25 ConnectConfirm) request via
	DsPutMsg completes.

Parameters:

	hOwner
		Pointer to the CallObject for this stream

	Context, pControl, ControlLength, pData
		Not used
	
	IoStatus
		Status returned from DigiSxb on the Open request.

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = hOwner;
	NTSTATUS Status;
	KIRQL OldIrql;

	ASSERT( CallObject && CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	DigiDump( DIGIFLOW|DIGITAPIFLOW, 
	          ( "Enter AnswerConfirmComplete, Call 0x%x, Address 0x%x, Status 0x%x\n",
	          CallObject, CallObject->AddressID, IoStatus ) );

	if ( STATUS_SUCCESS == IoStatus )
	{
		PUCHAR pGetData;

		CallObject->TapiCallState = LINECALLSTATE_CONNECTED;
		CallObject->InternalCallState = INTERNAL_CALL_STATE_CONNECTED;

		// Remove the call from the OfferingCalls list
		KeAcquireSpinLock( &CallObject->PhysAdapter->Lock, &OldIrql );
		RemoveEntryList( &CallObject->List );
		KeReleaseSpinLock( &CallObject->PhysAdapter->Lock, OldIrql );

		// Indicate LineUp to Wan wrapper
		AdapterContextWanLineup( CallObject, NULL );

		// Indicate Connected to TAPI
		AdapterContextSendTapiEvent( CallObject,
		                             LINE_CALLSTATE,
		                             LINECALLSTATE_CONNECTED, 0, 0 );

		// Alloc GetMsg
		pGetData = DigiAllocMem( NonPagedPool, DEFAULT_EXTRA_DATA_SIZE );
		if ( !pGetData )
		{
			Status = NDIS_STATUS_RESOURCES;
		}
		else
		{
			DigiDump( DIGIVERYLOUD, ("AnswerConfirmComplete: Calling DsGetMsg\n") );
			Status = DsGetMsgEx( CallObject->hStream,
			                     sizeof( union X25_primitives ),
			                     pGetData, DEFAULT_EXTRA_DATA_SIZE,
			                     NULL, DS_PRIORITY_LOW,
			                     X25GetMsgComplete );
		}
	}

	if ( !NT_SUCCESS( IoStatus ) || !NT_SUCCESS( Status ) )
	{
		// Some failure occurred, notify tapi
		CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
		AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
		                             LINECALLSTATE_DISCONNECTED,
		                             LINEDISCONNECTMODE_UNAVAIL, 0 );
	}
	else
	{
		// Do nothing!
	}
	DigiDump( DIGIFLOW, ( "Leave AnswerConfirmComplete\n" ) );
}



VOID
DropDiscComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PUCHAR pControl,
	IN ULONG ControlLength, 
	IN PUCHAR pData,
	IN ULONG DataLength, 
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiDrop call to DisconnectRequest completes.

Parameters:

	hOwner
		Pointer to the CallObject for this stream

	Context, pControl, ControlLength, pData
		Not used
	
	IoStatus
		Status returned from DigiSxb on the Open request.

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = hOwner;

	ASSERT( CallObject && CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	DigiDump( DIGIFLOW|DIGITAPIFLOW, 
	          ( "Enter DropDiscComplete, Call 0x%x, Address 0x%x, State 0x%x\n",
	                    CallObject, CallObject->AddressID, IoStatus ) );
	ASSERT( !pData );
	CallObject->conn_id = 0;

	// There should always be an open stream when we get here!
	ASSERT( CallObject->hStream );

	DigiDump( DIGIDIAG2|DIGIDROP, 
	          ("DIGIWAN: DropDiscComplete on Address 0x%x, hStream 0x%x, InternalState 0x%x\n",
             CallObject->AddressID, CallObject->hStream, CallObject->InternalCallState) );

	// If we still have a stream open, close it down!
	if ( CallObject->hStream )
	{
		HANDLE hStream = CallObject->hStream;
		NTSTATUS Status;
		CallObject->InternalCallState = INTERNAL_CALL_STATE_CLOSING;
		KeRemoveQueueDpc( &CallObject->Dpc );
		Status = DsPurge( CallObject->hStream, DS_PURGE_ALL );
		ASSERT( Status == STATUS_SUCCESS );
		CallObject->hStream = NULL;
		DigiDump( DIGIDIAG5, ("DropDisc: DsClose hStream 0x%x, Address 0x%x\n",
		                     hStream, CallObject->AddressID) );
		Status = DsCloseEx( hStream, DropCloseComplete );
	}

	DigiDump( DIGIFLOW, ( "Leave DropDiscComplete\n" ) );
}



VOID
DropCloseComplete(
	IN HANDLE hOwner,
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiDrop call to DsClose completes.

Parameters:

	hOwner
		CallObject pointer

	IoStatus
		Status returned from DigiSxb on the Close request.

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = hOwner;

	ASSERT( CallObject && CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	ASSERT( IoStatus == STATUS_SUCCESS );

	DigiDump( DIGIFLOW|DIGITAPIFLOW|DIGIDIAG2|DIGIDROP, 
	          ("DIGIWAN: DropCloseComplete on Address 0x%x, hStream 0x%x, InternalState 0x%x\n",
             CallObject->AddressID, CallObject->hStream, CallObject->InternalCallState) );

	CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
}



VOID
CloseCallComplete(
	IN HANDLE hOwner,
	IN NTSTATUS IoStatus	)
/*++

Description:

	This routine is called when an OidTapiCloseCall call to DsClose completes.

Parameters:

	hOwner
		CallObject pointer

	IoStatus
		Status returned from DigiSxb on the Close request.

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = hOwner;
	DigiDump( DIGIFLOW|DIGITAPIFLOW, ( "CloseCallComplete\n" ) );

	ASSERT( CallObject && CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	ASSERT( IoStatus == STATUS_SUCCESS );

	DigiDump( DIGIDIAG2|DIGICLOSECALL, 
	          ("DIGIWAN: CloseComplete on Address 0x%x, hStream 0x%x, InternalState 0x%x\n",
             CallObject->AddressID, CallObject->hStream, CallObject->InternalCallState) );

	// We're closing, free the callobject
	FreeCallObject( CallObject );
}



BOOLEAN
DigiEqualString(
	IN PSTRING String1,
	IN PSTRING String2,
	IN BOOLEAN CaseInsensitive )
/*++

Description:

	This routine is functionally identical to RtlEqualString, except that it can be
	called at any IRQL.  RtlEqualString can only be used at IRQL PASSIVE_LEVEL.

Parameters:

	String1 and String2
		Point the the two strings to be compared.

	CaseInsensitive
		If TRUE, case should be ignored when doing the compare.

Return Value:

	TRUE if the two strings are equal.

--*/
{
	STRING Local1, Local2;
	USHORT Offset;
	BOOLEAN Equal;

#if DBG
	// Make sure they're valid, null-terminated strings

	ASSERT( !String1->Buffer[String1->Length] );
	ASSERT( String1->MaximumLength >= String1->Length + 1 );
	for ( Offset = 0; Offset < String1->Length; Offset++ )
	{
		ASSERT( String1->Buffer[Offset] );
	}

	ASSERT( !String2->Buffer[String2->Length] );
	ASSERT( String1->MaximumLength >= String1->Length + 1 );
	for ( Offset = 0; Offset < String2->Length; Offset++ )
	{
		ASSERT( String2->Buffer[Offset] );
	}
#endif

	if ( String1->Length != String2->Length )
		return( FALSE );

	if ( CaseInsensitive )
	{
		Local1.Length = Local2.Length = 0;
		Local1.MaximumLength = Local1.Length + 1;
		Local2.MaximumLength = Local2.Length + 1;

		Local1.Buffer = DigiAllocMem( NonPagedPool, Local1.MaximumLength );
		Local2.Buffer = DigiAllocMem( NonPagedPool, Local2.MaximumLength );
		if ( !Local1.Buffer || !Local2.Buffer )
		{
			DigiDump( DIGIERRORS, ("*** DigiEqualString couldn't alloc buffers! ***\n") );
			return( FALSE );
		}
		RtlCopyMemory( Local1.Buffer, String1->Buffer, Local1.Length );
		Local1.Buffer[Local1.Length] = 0;	// Null-terminate it
		RtlCopyMemory( Local2.Buffer, String2->Buffer, Local1.Length );
		Local1.Buffer[Local2.Length] = 0;	// Null-terminate it

		for ( Offset = 0; Offset < Local1.Length; Offset ++ )
		{
			Local1.Buffer[Offset] = 
				( String1->Buffer[Offset] >= 'a' && String1->Buffer[Offset] <= 'z' ) ?
				String1->Buffer[Offset] : String1->Buffer[Offset] - 0x20;
		}

		for ( Offset = 0; Offset < Local2.Length; Offset ++ )
		{
			Local2.Buffer[Offset] = 
				( String2->Buffer[Offset] >= 'a' && String2->Buffer[Offset] <= 'z' ) ?
				String2->Buffer[Offset] : String2->Buffer[Offset] - 0x20;
		}
	}
	else
	{
		Local1 = *String1;
		Local2 = *String2;
	}

	// Do the compare
	ASSERT( Local1.Length == Local2.Length );
	for ( Offset = 0; Offset < Local1.Length; Offset++ )
	{
		if ( Local1.Buffer[Offset] != Local2.Buffer[Offset] )
		{
			Equal = FALSE;
			goto DigiEqualStringExit;
		}
	}
	Equal = TRUE;

DigiEqualStringExit:;

	if ( CaseInsensitive )
	{
		DigiFreeMem( Local1.Buffer );
		DigiFreeMem( Local2.Buffer );
	}
	return( Equal );
}
