/*++

*****************************************************************************
*                                                                           *
*  This software contains proprietary and confiential 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.                                                              *
*                                                                           *
*****************************************************************************

Module Name:

   adptcntx.c

Abstract:

   Handles making sure indications back to the NDIS Wan wrapper are done
   within in the context of the adapter object.

Revision History:

   $Log: /Components/Windows/NT/Sync/Wan/ADPTCNTX.C $
 * 
 * 12    4/28/97 1:54p Mikez
 * 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.
 * 
 * 11    3/17/97 12:29p Mikez
 * Added support for LineDevStatus indications via Status Messages
 * routine.  Only need to support RINGING.
 * 
 * 10    3/12/97 3:44p Mikez
 * Misc changes to get RAS dialout working.  Still bugchecks after dialout
 * connection completes on multiprocessor NT 3.51, sp5.
 * 
 * 9     3/03/97 9:23a Mikez
 * Updated to use async completions on MakeCall, Drop and Answer TAPI OID
 * requests.
 * 
 * 8     2/28/97 8:07a Mikez
 * Corrected compile errors.
 * 
 * 7     2/26/97 10:34a Mikez
 * Added debug info to dump events sent up to tapi.
 * 
 * 6     2/18/97 12:03p Mikez
 * Added debug code to help debug TapiEvent notifications.
 * 
 * 5     1/29/97 4:53p Mikez
 * Added more debug defines.
 * 
 * 4     12/09/96 4:20p Mikez
 * Cleaned up call states.  No longer use bitmasks, check for unique
 * numeric values.
 * 
 * 3     12/06/96 2:28p Mikez
 * Modified to support new SXB paridigm of "one NT device per adapter"
 * rather than the older "one NT device per port".
 * 
 * 2     10/28/96 1:25p Mikez
 * Updated to support latest firmware release, v1.1.2b.
 * 
 * 1     3/11/96 5:06p Mikez
 * Revision 1.5  1995/10/06  06:59:29  Craig_Harrison
 * Version 1.0.1.2 
 * 
 * Revision 1.4  1995/09/15  00:00:00  Craig_Harrison
 * First beta release
 * 
 * Revision 1.2  1995/06/01  10:08:31  rik
 * Updated for various problems and enhancements
 * 
 * Revision 1.1  1995/01/23  09:21:55  rik
 * Initial revision
 * 

--*/

#include <ntddk.h>
#include <ntddndis.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>

///////////////////////////////////////////////////////////////////////////////
//
// Typedefs and structs
//

typedef enum _ADAPTER_CONTEXT_REQUEST_TYPE_
{
   ACRT_UNKNOWN,
   SEND_TAPI_EVENT,
   WAN_LINE_UP,
   WAN_LINE_DOWN,
   WAN_FRAGMENT,
   WAN_SEND_COMPLETE,
   WAN_INDICATE_RECV,
	SET_INFO_COMPLETE,
	QUERY_INFO_COMPLETE,
   ADAPTER_CONTEXT_MAX
} ADAPTER_CONTEXT_REQUEST_TYPE;


typedef struct _SEND_TAPI_EVENT_STRUCT_
{
	PDIGIWAN_CALL_OBJECT CallObject;
   ULONG ulMsg;
   ULONG_PTR Param1;
   ULONG_PTR Param2;
   ULONG_PTR Param3;
} SEND_TAPI_EVENT_STRUCT, *PSEND_TAPI_EVENT_STRUCT;

typedef struct _WAN_LINE_INDICATION_STRUCT_
{
   PDIGIWAN_CALL_OBJECT CallObject;
   NDIS_HANDLE EndPoint;
   ULONG Error;
} WAN_LINE_INDICATION_STRUCT, *PWAN_LINE_INDICATION_STRUCT;

typedef struct _NDIS_MWAN_SEND_COMPLETE_STRUCT_
{
   PDIGIWAN_CALL_OBJECT CallObject;
   PNDIS_WAN_PACKET WanPacket;
   NDIS_STATUS StatusToComplete;
} NDIS_MWAN_SEND_COMPLETE_STRUCT, *PNDIS_MWAN_SEND_COMPLETE_STRUCT;

typedef struct _WAN_INDICATE_RECV_STRUCT_
{
   PDIGIWAN_CALL_OBJECT CallObject;
   PIRP Irp;
   PCHAR Packet;
   LONG PacketLength;
} WAN_INDICATE_RECV_STRUCT, *PWAN_INDICATE_RECV_STRUCT;

typedef struct SET_INFO_COMPLETE_STRUCT
{
	NDIS_STATUS Status;
} SET_INFO_COMPLETE_STRUCT, *PSET_INFO_COMPLETE_STRUCT;

typedef struct QUERY_INFO_COMPLETE
{
	NDIS_STATUS Status;
} QUERY_INFO_COMPLETE_STRUCT, *PQUERY_INFO_COMPLETE_STRUCT;

//
// Adapter Context request
//
typedef struct _ADAPTER_CONTEXT_REQUEST_
{
   DIGIWAN_TYPE Type;

   ADAPTER_CONTEXT_REQUEST_TYPE ReqType;

   LIST_ENTRY ListEntry;

   union {
      SEND_TAPI_EVENT_STRUCT TapiEvent;
      WAN_LINE_INDICATION_STRUCT WanLine;
      NDIS_MWAN_SEND_COMPLETE_STRUCT WanSendComplete;
      WAN_INDICATE_RECV_STRUCT IndicateRecv;
      SET_INFO_COMPLETE_STRUCT SetInfoComplete;
      QUERY_INFO_COMPLETE_STRUCT QueryInfoComplete;
   } Indication;

} ADAPTER_CONTEXT_REQUEST, *PADAPTER_CONTEXT_REQUEST;


#if DBG
VOID DumpTapiEvent( PDIGIWAN_CALL_OBJECT CallObject, 
                    ULONG ulMsg, ULONG_PTR p1, ULONG_PTR p2, ULONG_PTR p3 );
#endif



VOID AdapterContextTimerFunction( IN PVOID SystemSpecific1,
                                  IN PVOID FunctionContext,
                                  IN PVOID SystemSpecific2,
                                  IN PVOID SystemSpecific3 )
/*++

Routine Description:

   This is a work around so we can make sure we give all indications back to
   in our intialization routine.

   We process all outstanding requests on the AdapterContextQueue.

Arguments:

   FunctionContext - pointer to an AdapterObject which contains the
                     a queue for processing request in a specific adapter
                     context.

Return Value:

   None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = (PDIGIWAN_ADAPTER_OBJECT)FunctionContext;
   PADAPTER_CONTEXT_REQUEST Request;
   ASSERT( AdapterObject->Type == DIGIWAN_TYPE_ADAPTER_OBJECT );

	// Protect against reentrancy in the AdapterContextTimerFunction
   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
	if( AdapterObject->AdapterContext.InTimerFunc )
	{
	   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );
		return;
	}

	AdapterObject->AdapterContext.InTimerFunc = TRUE;

	// Hold onto the lock while we look at the queue

   while( !IsListEmpty( &AdapterObject->AdapterContext.Queue ) )
   {
		PLIST_ENTRY Entry = RemoveHeadList(&AdapterObject->AdapterContext.Queue);
      Request = CONTAINING_RECORD( Entry,
                                   ADAPTER_CONTEXT_REQUEST,
                                   ListEntry );

      ASSERT( (Request->ReqType > ACRT_UNKNOWN) && (Request->ReqType < ADAPTER_CONTEXT_MAX) );

      NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

      //
      // process the outstanding request.
      //
      switch( Request->ReqType )
      {
         case SEND_TAPI_EVENT:
         {
            PDIGIWAN_CALL_OBJECT CallObject;

            CallObject = Request->Indication.TapiEvent.CallObject;

#if DBG
				DumpTapiEvent( CallObject, 
				               Request->Indication.TapiEvent.ulMsg, 
				               Request->Indication.TapiEvent.Param1, 
				               Request->Indication.TapiEvent.Param2, 
				               Request->Indication.TapiEvent.Param3 );
#endif

            SendTapiEvent( AdapterObject->MiniportAdapterHandle,
                           AdapterObject->Line.htLine,
                           CallObject->htCall,
                           Request->Indication.TapiEvent.ulMsg,
                           &Request->Indication.TapiEvent.Param1,
                           &Request->Indication.TapiEvent.Param2,
                           &Request->Indication.TapiEvent.Param3 );

            if( Request->Indication.TapiEvent.ulMsg == LINE_NEWCALL )
            {
               // The htCall handle returned by TAPI.  It better be nonzero!
               ASSERT( Request->Indication.TapiEvent.Param2 );
               CallObject->htCall = Request->Indication.TapiEvent.Param2;
            }
            break;
         }

			case QUERY_INFO_COMPLETE:
				DigiDump( DIGITAPIFLOW, ("NdisMQueryInformationComplete\n") );
				NdisMQueryInformationComplete( 
					AdapterObject->MiniportAdapterHandle,
					Request->Indication.QueryInfoComplete.Status );
				break;

			case SET_INFO_COMPLETE:
				DigiDump( DIGITAPIFLOW, ("NdisMSetInformationComplete\n") );
				NdisMSetInformationComplete( 
					AdapterObject->MiniportAdapterHandle,
					Request->Indication.SetInfoComplete.Status );
				break;

         case WAN_LINE_UP:

            WanLineup( Request->Indication.WanLine.CallObject,
                       Request->Indication.WanLine.EndPoint );
            break;

         case WAN_LINE_DOWN:

            WanLinedown( Request->Indication.WanLine.CallObject );
            break;

         case WAN_FRAGMENT:

            WanFragment( Request->Indication.WanLine.CallObject,
                         Request->Indication.WanLine.Error );
            break;

         case WAN_SEND_COMPLETE:
         {
            PDIGIWAN_CALL_OBJECT CallObject;
            PNDIS_WAN_PACKET Packet;							  

            CallObject = Request->Indication.WanSendComplete.CallObject;

            DigiDump( (DIGIWANINFO), ("SendComplete: AdapterHandle=0x%x, "
                                   "WanPacket=0x%x, Status=0x%x\n",
                                   AdapterObject->MiniportAdapterHandle,
                                   Request->Indication.WanSendComplete.WanPacket,
                                   Request->Indication.WanSendComplete.StatusToComplete) );

            DigiDumpData( DIGITXDATA,
                          Request->Indication.WanSendComplete.WanPacket->CurrentBuffer,
                          Request->Indication.WanSendComplete.WanPacket->CurrentLength );

            (CallObject->PacketsSend)++;

            Packet = Request->Indication.WanSendComplete.WanPacket;

            NdisMWanSendComplete( AdapterObject->MiniportAdapterHandle,
                                  Request->Indication.WanSendComplete.WanPacket,
                                  Request->Indication.WanSendComplete.StatusToComplete );

            break;

         }  // end case WAN_SEND_COMPLETE

         case WAN_INDICATE_RECV:
         {
            NDIS_STATUS Status;
            PDIGIWAN_CALL_OBJECT CallObject;
				KIRQL OldIrql;

            CallObject = Request->Indication.IndicateRecv.CallObject;

				// We should never try to give a packet larger the the max. frame
				// size previously indicated.
				ASSERT( Request->Indication.IndicateRecv.PacketLength <= MAX_MTU_SIZE );

				//
				// Check if still connected before calling NdisMWanIndicateReceive.
				// It's possible not connected because a hangup could occur after
				// the call to AdapterContextWanIndicateReceive but before
				// the Adapter Context Timer goes off.
				//
				KeAcquireSpinLock( &CallObject->Lock, &OldIrql );
				if( CallObject->InternalCallState == INTERNAL_CALL_STATE_CONNECTED &&
				    CallObject->WrapperLinkHandle != NULL )
				{
            	DigiDump( (DIGIWANINFO|DIGIDIAG4),
               	    ("IndicateRecv: AdapterHandle=0x%x, "
                  	  "LinkContext=0x%x, Packet=0x%x, Length=0x%x\n",
                        AdapterObject->MiniportAdapterHandle,
                        CallObject->WrapperLinkHandle,
                        Request->Indication.IndicateRecv.Packet,
                        Request->Indication.IndicateRecv.PacketLength) );

            	DigiDumpData( DIGIRECVDATA,
                          Request->Indication.IndicateRecv.Packet,
                          Request->Indication.IndicateRecv.PacketLength );

            	CallObject->PacketsRecv++;
            	NdisMWanIndicateReceive( &Status,
            	                         AdapterObject->MiniportAdapterHandle,
            	                         CallObject->WrapperLinkHandle,
            	                         Request->Indication.IndicateRecv.Packet,
            	                         Request->Indication.IndicateRecv.PacketLength );

            	KeReleaseSpinLock( &CallObject->Lock, OldIrql );

            	DigiDump( DIGIWANINFO, ("IndicateRecv: Status=0x%x\n", Status) );
            	ASSERT( Status == NDIS_STATUS_SUCCESS );

            	NdisMWanIndicateReceiveComplete( AdapterObject->MiniportAdapterHandle,
            	                                 CallObject->WrapperLinkHandle );

            	DigiFreeMem( Request->Indication.IndicateRecv.Packet );
				}
				else	// NOT connected - clean up
				{
            	KeReleaseSpinLock( &CallObject->Lock, OldIrql );
            	DigiFreeMem( Request->Indication.IndicateRecv.Packet );
				}

            break;
         }  // end case WAN_INDICATE_RECV
      }

      //
      // Free the request.
      //
     	DigiFreeMem( Request );

      NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   }

	AdapterObject->AdapterContext.InTimerFunc = FALSE;   
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

}  // end AdapterContextTimerFunction



VOID 
AdapterContextWanLineup( PDIGIWAN_CALL_OBJECT CallObject,
                         NDIS_HANDLE EndPoint )
/*++

Routine Description:

   Places a LINE_UP indication on the parent AdapterObject AdapterContext.Queue
   for processing within the adapter objects context.

Arguments:

   CallObject - Pointer to the call object which will be associated
                with this line up indication.

   EndPoint - Context to give the Connectection Wrapper.  If NULL, then
              we use the CallObjects TapiCallHandle.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create the request
   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );
   if( Request == NULL )
      return;	// We don't have the resources, skip it.

   // Initialize the request.

   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = WAN_LINE_UP;
   Request->Indication.WanLine.CallObject = CallObject;
   Request->Indication.WanLine.EndPoint = EndPoint;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &(Request->ListEntry) );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.
   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );

} // end AdapterContextWanLineup



VOID 
AdapterContextWanLinedown( PDIGIWAN_CALL_OBJECT CallObject,
                           NDIS_HANDLE EndPoint )
/*++

Routine Description:

   Places a LINE_DOWN indication on the parent AdapterObject AdapterContext.Queue
   for processing within the adapter objects context.

Arguments:

   CallObject - Pointer to the call object which will be associated
                with this indication.

   EndPoint - Context to give the Connection Wrapper.  If NULL, then
              we use the CallObjects TapiCallHandle.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create a request

   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );
   if( Request == NULL )
      return;	// We don't have the resources, skip it.

   // Initialize the request.

   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = WAN_LINE_DOWN;
   Request->Indication.WanLine.CallObject = CallObject;
   Request->Indication.WanLine.EndPoint = EndPoint;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &(Request->ListEntry) );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.
   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );

} // end AdapterContextWanLinedown



VOID AdapterContextSendTapiEvent( PDIGIWAN_CALL_OBJECT CallObject,
                                  ULONG ulMsg,
                                  ULONG_PTR Param1,
                                  ULONG_PTR Param2,
                                  ULONG_PTR Param3 )
/*++

Routine Description:

   Places a tapi event indication on the AdapterObject AdapterContext.Queue
   for processing within the adapter objects context.

Arguments:

   CallObject - pointer to the callobject which this indication is associated.

   ulMsg - Tapi message to indicate

   Param1, Param2, Param3 - Dependent on ulMsg.

Return Value:

    None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create the request

   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );
   if( Request == NULL )
      // We don't have the resources, just drop the request on the floor
      return;

   // Initialize the request.

   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = SEND_TAPI_EVENT;
   Request->Indication.TapiEvent.CallObject = CallObject;
   Request->Indication.TapiEvent.ulMsg = ulMsg;
   Request->Indication.TapiEvent.Param1 = Param1;
   Request->Indication.TapiEvent.Param2 = Param2;
   Request->Indication.TapiEvent.Param3 = Param3;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &Request->ListEntry );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.

   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );

} // end AdapterContextSendTapiEvent



VOID AdapterContextWanSendComplete( PDIGIWAN_CALL_OBJECT CallObject,
                                    PNDIS_WAN_PACKET WanPacket,
                                    NDIS_STATUS StatusToComplete )
/*++

Routine Description:

   Places a tapi event indication on the AdapterObject AdapterContextQueue
   for processing within the adapter objects context.

Arguments:

   CallObject - pointer to the callobject which this indication is associated.

   WanPacket - pointer to the wan packet being completed.

   StatusToComplete - status to complete the request with.

Return Value:

    None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create the request

   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );
   if( Request == NULL )
      // We don't have the resources, just drop the request on the floor
      return;

   // Initialize the request.

   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = WAN_SEND_COMPLETE;
   Request->Indication.WanSendComplete.CallObject = CallObject;
   Request->Indication.WanSendComplete.WanPacket = WanPacket;
   Request->Indication.WanSendComplete.StatusToComplete = StatusToComplete;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &Request->ListEntry );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.

   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );

} // end AdapterContextWanSendComplete



VOID 
AdapterContextWanIndicateReceive( 
	IN PDIGIWAN_CALL_OBJECT CallObject,
	IN PUCHAR pBuffer,
	IN ULONG Length
	)
/*++

Routine Description:

   Places a indicate received data event on the AdapterObject AdapterContext.Queue
   for processing within the adapter objects context.

Arguments:

   CallObject - pointer to the callobject which this indication is associated.

	pBuffer - pointer to data buffer containing packet to indicate.

	Length - length of packet.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create a request
   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );
   if( Request == NULL )
      // We don't have the resources, just drop the request on the floor
      return;

   // Initialize the request.
   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = WAN_INDICATE_RECV;
   Request->Indication.IndicateRecv.CallObject = CallObject;
   Request->Indication.IndicateRecv.Packet = pBuffer;
   Request->Indication.IndicateRecv.PacketLength = Length;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &(Request->ListEntry) );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.
   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );
}  // end AdapterContextWanIndicateReceive



VOID AdapterContextWanIndicateFragment( PDIGIWAN_CALL_OBJECT CallObject,
                                        ULONG Error )
/*++

Routine Description:

   Places a Wan Fragment Status indication on the AdapterObject AdapterContextQueue
   for processing within the adapter objects context.

Arguments:

   CallObject - pointer to the callobject which this indication is associated.

   Error - error which to indicate.

Return Value:

    None.

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create the request 

   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );

   if( Request == NULL )
      // We don't have the resources, just drop the request on the floor
      return;

   // Initialize the request.

   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = WAN_FRAGMENT;
   Request->Indication.WanLine.CallObject = CallObject;
   Request->Indication.WanLine.Error = Error;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &(Request->ListEntry) );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.

   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );

} // end AdapterContextWanIndicateFragment


#if DBG
VOID DumpTapiEvent( PDIGIWAN_CALL_OBJECT CallObject, 
                    ULONG ulMsg, ULONG_PTR p1, ULONG_PTR p2, ULONG_PTR p3 )
{
	UCHAR szLocal[128];
	sprintf( szLocal, "TapiEvent on AddressId 0x%x: ", CallObject->AddressID );
	switch ( ulMsg )
	{
		case LINE_NEWCALL:
			sprintf( szLocal+strlen(szLocal), "NewCall" );
			break;

		case LINE_CALLSTATE:
			sprintf( szLocal+strlen(szLocal), "CallState: " );
			switch ( p1 )
			{
				case LINECALLSTATE_IDLE:
					sprintf( szLocal+strlen(szLocal), "Idle, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_OFFERING:
					sprintf( szLocal+strlen(szLocal), "Offering, 0x%x, 0x%x, htCall 0x%x",
					         p2, p3, CallObject->htCall );
					break;
				case LINECALLSTATE_ACCEPTED:
					sprintf( szLocal+strlen(szLocal), "Accepted, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_DIALTONE:
					sprintf( szLocal+strlen(szLocal), "Dialtone, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_DIALING:
					sprintf( szLocal+strlen(szLocal), "Dialing, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_RINGBACK:
					sprintf( szLocal+strlen(szLocal), "RingBack, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_BUSY:
					sprintf( szLocal+strlen(szLocal), "Busy, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_SPECIALINFO:
					sprintf( szLocal+strlen(szLocal), "SpecialInfo, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_CONNECTED:
					sprintf( szLocal+strlen(szLocal), "Connected, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_PROCEEDING:
					sprintf( szLocal+strlen(szLocal), "Proceeding, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_ONHOLD:
					sprintf( szLocal+strlen(szLocal), "Onhold, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_CONFERENCED:
					sprintf( szLocal+strlen(szLocal), "Conferenced, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_ONHOLDPENDCONF:
					sprintf( szLocal+strlen(szLocal), "OnHoldPendConf, 0x%x, 0x%x", p2, p3);
					break;
				case LINECALLSTATE_ONHOLDPENDTRANSFER:
					sprintf(szLocal+strlen(szLocal),"OnHoldPendTransfer, 0x%x, 0x%x",p2,p3);
					break;
				case LINECALLSTATE_DISCONNECTED:
					sprintf( szLocal+strlen(szLocal), "Disconnected, 0x%x, 0x%x", p2, p3 );
					break;
				case LINECALLSTATE_UNKNOWN:
					sprintf( szLocal+strlen(szLocal), "Unknown, 0x%x, 0x%x", p2, p3 );
					break;
				default:
					sprintf(szLocal+strlen(szLocal),"Not recognized! 0x%x, 0x%x, 0x%x",p1,p2,p3);
					break;
			}
			break;

		case LINE_LINEDEVSTATE:
			
			sprintf( szLocal+strlen(szLocal), "LineDevState: " );
			switch ( p1 )
			{
				case LINEDEVSTATE_OTHER         :
					sprintf( szLocal+strlen(szLocal), "Other, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_RINGING       :
					sprintf( szLocal+strlen(szLocal), "Ringing, RingMode 0x%x, Rings 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_CONNECTED     :
					sprintf( szLocal+strlen(szLocal), "Connected, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_DISCONNECTED  :
					sprintf( szLocal+strlen(szLocal), "Disconnected, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_MSGWAITON     :
					sprintf( szLocal+strlen(szLocal), "MsgWaitOn, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_MSGWAITOFF    :
					sprintf( szLocal+strlen(szLocal), "MsgWaitOff, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_INSERVICE     :
					sprintf( szLocal+strlen(szLocal), "InService, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_OUTOFSERVICE  :
					sprintf( szLocal+strlen(szLocal), "OutOfService, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_MAINTENANCE   :
					sprintf( szLocal+strlen(szLocal), "Maintenance, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_OPEN          :
					sprintf( szLocal+strlen(szLocal), "Open, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_CLOSE         :
					sprintf( szLocal+strlen(szLocal), "Close, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_NUMCALLS      :
					sprintf( szLocal+strlen(szLocal), "NumCalls, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_NUMCOMPLETIONS:
					sprintf( szLocal+strlen(szLocal), "NumCompletions, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_TERMINALS     :
					sprintf( szLocal+strlen(szLocal), "Terminals, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_ROAMMODE      :
					sprintf( szLocal+strlen(szLocal), "RoamMode, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_BATTERY       :
					sprintf( szLocal+strlen(szLocal), "Battery, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_SIGNAL        :
					sprintf( szLocal+strlen(szLocal), "Signal, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_DEVSPECIFIC   :
					sprintf( szLocal+strlen(szLocal), "DevSpecific, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_REINIT        :
					sprintf( szLocal+strlen(szLocal), "ReInit, 0x%x, 0x%x", p2, p3 );
					break;
				case LINEDEVSTATE_LOCK          :
					sprintf( szLocal+strlen(szLocal), "Lock, 0x%x, 0x%x", p2, p3 );
					break;
				default:
					sprintf(szLocal+strlen(szLocal),"Not recognized! 0x%x, 0x%x, 0x%x",p1,p2,p3);
					break;
			}
			break;

		default:
			sprintf(szLocal+strlen(szLocal),"Not recognized! 0x%x, 0x%x, 0x%x, 0x%x",ulMsg,p1,p2,p3);
			break;
	}
	ASSERT( strlen( szLocal ) < sizeof( szLocal ) );
	DigiDump( DIGITAPIEVENT, ( "%s\n", szLocal ) );
}
#endif



VOID
AdapterContextQueryInfoComplete(
	IN PDIGIWAN_CALL_OBJECT CallObject,
	IN NDIS_STATUS Status )
/*++

Description:

	Queues an NdisMQueryInformationComplete indication to tell NDIS that a 
	previously pended QueryInformation request has completed.

Parameters:

	CallObject - Call on which this indication originated

	Status - Status to complete the request with.

Return Value:

	None

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create a request
   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );
   if( Request == NULL )
      // We don't have the resources, just drop the request on the floor
      return;

   // Initialize the request.
   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = QUERY_INFO_COMPLETE;
   Request->Indication.QueryInfoComplete.Status = Status;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &(Request->ListEntry) );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.
   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );
}



VOID
AdapterContextSetInfoComplete(
	IN PDIGIWAN_CALL_OBJECT CallObject,
	IN NDIS_STATUS Status )
/*++

Description:

	Queues an NdisMQueryInformationComplete indication to tell NDIS that a 
	previously pended SetInformation request has completed.

Parameters:

	CallObject - Call on which this indication originated

	Status - Status to complete the request with.

Return Value:

	None

--*/
{
   PDIGIWAN_ADAPTER_OBJECT AdapterObject = CallObject->pAdapter;
   PADAPTER_CONTEXT_REQUEST Request;
   BOOLEAN Empty;

   // Create a request
   Request = DigiAllocMem( NonPagedPool, sizeof(ADAPTER_CONTEXT_REQUEST) );
   if( Request == NULL )
      // We don't have the resources, just drop the request on the floor
      return;

   // Initialize the request.
   Request->Type = DIGIWAN_TYPE_ADAPTER_CONTEXT;
   Request->ReqType = SET_INFO_COMPLETE;
   Request->Indication.SetInfoComplete.Status = Status;

	// Insert this request on the adapter context queue

   NdisAcquireSpinLock( &AdapterObject->AdapterContext.Lock );
   Empty = IsListEmpty( &AdapterObject->AdapterContext.Queue );
   InsertTailList( &AdapterObject->AdapterContext.Queue, &(Request->ListEntry) );
   NdisReleaseSpinLock( &AdapterObject->AdapterContext.Lock );

   // Fire the timer if appropriate.
   if( Empty )
      NdisMSetTimer( &AdapterObject->AdapterContext.Timer, 0 );
}
