/*++

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

   listen.c

Abstract:

   This module defines and implements the listen object used to send and manage 
   listens sent down to X.25 firmware.

Author:

   Michael Zarns

Revision History:

   $History: listen.c $
 * 
 * *****************  Version 11  *****************
 * User: Mikez        Date: 6/03/97    Time: 11:42a
 * Updated in $/Components/Windows/NT/Sync/Build/Wan/Src
 * Modified to use async completing versions of PutMsg and GetMsg, since
 * they may be called at IRQL > PASSIVE_LEVEL and can't wait, thus can't
 * use the sync versions.
 * 
 * *****************  Version 10  *****************
 * User: Mikez        Date: 4/28/97    Time: 1:54p
 * 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 9  *****************
 * User: Mikez        Date: 3/17/97    Time: 12:29p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Added support for LineDevStatus indications via Status Messages
 * routine.  Only need to support RINGING.
 * 
 * *****************  Version 8  *****************
 * 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 7  *****************
 * User: Mikez        Date: 3/04/97    Time: 11:16p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Removed references to AdapterObject CallObjects field.
 * 
 * *****************  Version 6  *****************
 * 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 5  *****************
 * User: Mikez        Date: 2/26/97    Time: 10:25a
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Added disconnect request on unsupported incoming calls.
 * 
 * *****************  Version 4  *****************
 * User: Mikez        Date: 1/28/97    Time: 11:07a
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Modified listen code to get PAD connections working...  Needs
 * verification!
 * 
 * *****************  Version 3  *****************
 * User: Mikez        Date: 12/06/96   Time: 2:28p
 * Updated in $/Components/Windows/NT/Sync/Wan
 * Modified to support new SXB paridigm of "one NT device per adapter"
 * rather than the older "one NT device per port".
 * 
 * *****************  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 "DsApi.h"
#include "digix25.h"
#include "digix25p.h"
#include "digifile.h"
#include <digialloc.h>

///////////////////////////////////////////////////////////////////////////////
//
// Prototypes
//

VOID 
ListenDiscComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PVOID pControl,
	IN ULONG ControlLength, 
	IN PVOID pData,
	IN ULONG DataLength, 
	IN NTSTATUS IOStatus	);

VOID
ListenComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PVOID pControl,
	IN ULONG ControlLength, 
	IN PVOID pData,
	IN ULONG DataLength, 
	IN NTSTATUS IOStatus	);

VOID
CancelListenComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PVOID pControl,
	IN ULONG ControlLength, 
	IN PVOID pData,
	IN ULONG DataLength, 
	IN NTSTATUS IOStatus	);


NDIS_STATUS 
ListenRequest( 
	IN HANDLE hStream
	)
/*++

Routine Description:

	Send down listen requests.  This allows the adapter to accept incoming
	calls, and passes the indication up this stream.  Two listen requests
	are sent, one for PPP (direct) connections, and another for PAD connections.

Arguments:

	hStream - Handle of the stream on which to send the request.

--*/
{
   NDIS_STATUS Status;
	struct xlistenf Listen;
	PUCHAR pPPPData, pPADData;
//   struct xlistenf Listen;
//   UCHAR ExtraData[7];

	pPPPData = DigiAllocMem( NonPagedPool, 7 );
	pPADData = DigiAllocMem( NonPagedPool, 7 );
	if ( !pPPPData || !pPADData )
	{
		if ( pPPPData )
			DigiFreeMem( pPPPData );
		if ( pPADData )
			DigiFreeMem( pPADData );
		return( NDIS_STATUS_RESOURCES );
	}

	// Send the Listen Request to listen for PPP (direct) connections
	NdisZeroMemory( &Listen, sizeof( struct xlistenf ) );
   Listen.xl_type = XL_CTL;
   Listen.xl_command = N_Xlisten;
   Listen.lmax = 256;
   // Listen.l_result = 0;

   pPPPData[0] = X25_IDENTITY; // l_cumode
   pPPPData[1] = 1;            // l_culength
   pPPPData[2] = 0xCF;         // l_cubytes = PPP connection
   pPPPData[3] = X25_DONTCARE;

   DigiDump( DIGIVERYLOUD, 
             ("ListenRequest: Sending ListenRequest for PPP connections\n") );

	Status = DsPutMsgEx( hStream, 
	                     (PVOID)&Listen, sizeof( struct xlistenf ), 
	                     pPPPData, 4,
	                     NULL, DS_PRIORITY_LOW,ListenComplete );

	// Send the Listen Request to listen for PPP (direct) connections
	NdisZeroMemory( &Listen, sizeof( struct xlistenf ) );
   Listen.xl_type = XL_CTL;
   Listen.xl_command = N_Xlisten;
   Listen.lmax = 256;

   pPADData[0] = X25_STARTSWITH; // l_cumode
   pPADData[1] = 1;              // l_culength
   pPADData[2] = 0x01;           // l_cubytes = PAD == 0x01, 0x00, 0x00, 0x00
   // pPADData[3] = 0x00;        // 
   // pPADData[4] = 0x00;        // 
   // pPADData[5] = 0x00;        // 
   pPADData[3] = X25_DONTCARE;

   DigiDump( DIGIVERYLOUD, 
             ("ListenRequest: Sending ListenRequest for PAD connections\n") );

	Status = DsPutMsgEx( hStream, 
	                     (PVOID)&Listen, sizeof( struct xlistenf ),
	                     pPADData, 4,
	                     NULL, DS_PRIORITY_LOW,ListenComplete );
   return( Status );

}  // end ListenRequest



NDIS_STATUS CancelListen( IN HANDLE hStream )
/*++

Routine Description:

   We do everything necessary to cancel listens on a Virtual Connection.

Arguments:

   CallObject - pointer to the call object which represents the
                virtual connection to disconnect.

Return Value:

   NDIS_STATUS_PENDING - Waiting for confirmation of the call.

--*/
{
   NDIS_STATUS Status;

   struct xcanlisf Cancel;

	ASSERT( hStream );
	DigiDump( DIGIFLOW, ("Enter CancelListen, hStream 0x%x\n", hStream) );

   NdisZeroMemory( &Cancel, sizeof(struct xcanlisf) );

   Cancel.xl_type = XL_CTL;
   Cancel.xl_command = N_Xcanlis;
   // ListenCancel.c_result = 0;

   DigiDump( DIGIVERYLOUD, ("CancelListen: Calling DsPutMsgEx\n") );
	Status = DsPutMsgEx( hStream,
	                     (PVOID)&Cancel, sizeof( struct xcanlisf ),
	                     NULL, 0,
	                     NULL, DS_PRIORITY_LOW, CancelListenComplete );
   return( Status );
}  // end CancelListen



VOID
ListenGetMsgComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PVOID pControl,
	IN ULONG ControlLength,
	IN PVOID pData,
	IN ULONG DataLength,
	IN NTSTATUS IOStatus
   )
/*++

Routine Description:
	
	Completion routine for GetMsg requests passed down on the listen stream.

--*/
{
	PDIGIWAN_PHYS_ADAPTER PhysAdapter=hOwner;
	S_X25_HDR *pMsgHeader = (PVOID)pControl;

   KIRQL OldIrql;
   int i;

	ASSERT( PhysAdapter->Type == DIGIWAN_TYPE_PHYS_ADAPTER );
   DigiDump( DIGIFLOW, ("ListenGetMsgComplete: PhysAdapter (0x%p)\n", PhysAdapter) );

   if( IOStatus == STATUS_CANCELLED )
   {
      // We are in the process of closing the VC.  Cleanup and exit.
		ASSERT( pData );
		DigiFreeMem( pData );
		DigiDump( DIGIVERYLOUD, ("ListenGetMsgComplete: closing\n") );
		return;
   }

   ASSERT( IOStatus == STATUS_SUCCESS );
	ASSERT( ControlLength );

	switch ( pMsgHeader->xl_type )
	{
		case XL_CTL:

         DigiDump( DIGISXBINFO, ("XL_CTL: \n") );
         switch( pMsgHeader->xl_command )
         {
            case N_CI:	// X.25 Call Indication
            {
               //
               // Connect Indication.  Incoming call
               //
               struct xcallf *pConnect = (PVOID)pControl;
					PDIGIWAN_CALL_OBJECT CallObject;

					CallObject = NewCallObject( PhysAdapter->WanAdapter, DIGI_ADDRESSID_ANY );
					if ( !CallObject )
					{
						DigiDump( DIGIERRORS, ("N_CI: couldn't alloc call object!\n") );
						DisconnectRequest( PhysAdapter->hListenStream, 
						                   pConnect->conn_id,
						                   ListenDiscComplete );
						break;
					}
					
               CallObject->conn_id = pConnect->conn_id;
					CallObject->PhysAdapter = PhysAdapter;

               DigiDump( DIGISXBINFO, ("   N_CI: "
                                       "   conn_id = 0x%x\n"
                                       "   CONS_call = 0x%x\n"
                                       "   negotiate_qos = 0x%x\n"
                                       "   lsap_len = 0x%x\n"
                                       "   calledaddr.sn_id = '%c' 0x%x\n",
                                       pConnect->conn_id,
                                       pConnect->CONS_call,
                                       pConnect->negotiate_qos,
                                       pConnect->calledaddr.DTE_MAC.lsap_len,
                                       pConnect->calledaddr.sn_id,
                                       pConnect->calledaddr.sn_id) );

					DigiDump( DIGISXBINFO, ("   Calledaddr.lsap_add: ") );
               for ( i = 0; i < LSAPMAXSIZE; i++)
	               DigiDump(DIGISXBINFO,("%x-",pConnect->calledaddr.DTE_MAC.lsap_add[i]));
					DigiDump( DIGISXBINFO, ("\n") );

					DigiDump( DIGISXBINFO, ("   Callingaddr.lsap_add: ") );
               for ( i = 0; i < LSAPMAXSIZE; i++)
	              DigiDump(DIGISXBINFO,("%x-",pConnect->callingaddr.DTE_MAC.lsap_add[i]));
					DigiDump( DIGISXBINFO, ("\n") );

               //
               // Check the Call User Data field and determine if it is
               // a connection we wish to accept.  Currently, we only
               // accept connections for PPP and PAD clients.
               //
               if( ((PUCHAR)pData)[0] == 0xCF )
               {
                  CallObject->SpecialFlags |= CALL_OBJ_PPP_FRAMING;
                  CallObject->CurrentMaxMtuSize = MAX_MTU_SIZE;
               }
               else if( ((PUCHAR)pData)[0] == 0x01 )
               {
                  CallObject->SpecialFlags |= CALL_OBJ_PAD_CONNECTION;
                  CallObject->CurrentMaxMtuSize = MAX_MTU_SIZE * 2;
               }
               else
               {
						DigiDump( DIGIERRORS, 
						          ("*** Unexpected call user data 0x%x ***\n",
						          ((PUCHAR)pData)[0] ) );
						FreeCallObject( CallObject );
						DisconnectRequest( PhysAdapter->hListenStream, 
						                   pConnect->conn_id,
						                   ListenDiscComplete );
						break;
               }
               if( pConnect->negotiate_qos )
               {
                  DigiDump( DIGISXBINFO, ("   negotiate_qos:"
                                          "      qos.reqtclass=0x%x\n"
                                          "      qos.locthroughput=0x%x\n"
                                          "      qos.remthroughput=0x%x\n"
                                          "      qos.reqminthruput=0x%x\n"
                                          "      qos.locminthru=0x%x\n"
                                          "      qos.remminthru=0x%x\n"
                                          "      qos.reqtransitdelay=0x%x\n"
                                          "      qos.transitdelay=0x%x\n"
                                          "      qos.reqmaxtransitdelay=0x%x\n"
                                          "      qos.acceptable=0x%x\n"
                                          "      qos.reqpriority=0x%x\n"
                                          "      qos.reqprtygain=0x%x\n"
                                          "      qos.reqprtykeep=0x%x\n",
                                          pConnect->qos.reqtclass,
                                          pConnect->qos.locthroughput,
                                          pConnect->qos.remthroughput,
                                          pConnect->qos.reqminthruput,
                                          pConnect->qos.locminthru,
                                          pConnect->qos.remminthru,
                                          pConnect->qos.reqtransitdelay,
                                          pConnect->qos.transitdelay,
                                          pConnect->qos.reqmaxtransitdelay,
                                          pConnect->qos.acceptable,
                                          pConnect->qos.reqpriority,
                                          pConnect->qos.reqprtygain,
                                          pConnect->qos.reqprtykeep) );
                  DigiDump( DIGISXBINFO, ("      qos.prtydata=0x%x\n"
                                          "      qos.prtygain=0x%x\n"
                                          "      qos.prtykeep=0x%x\n"
                                          "      qos.reqlowprtydata=0x%x\n"
                                          "      qos.reqlowprtygain=0x%x\n"
                                          "      qos.reqlowprtykeep=0x%x\n"
                                          "      qos.lowprtydata=0x%x\n"
                                          "      qos.lowprtygain=0x%x\n"
                                          "      qos.lowprtykeep=0x%x\n"
                                          "      qos.protection_type=0x%x\n"
                                          "      qos.prot_len=0x%x\n"
                                          "      qos.lowprot_len=0x%x\n"
                                          "      qos.reqexpedited=0x%x\n"
                                          "      qos.reqackservice=0x%x\n",
                                          pConnect->qos.prtydata,
                                          pConnect->qos.prtygain,
                                          pConnect->qos.prtykeep,
                                          pConnect->qos.reqlowprtydata,
                                          pConnect->qos.reqlowprtygain,
                                          pConnect->qos.reqlowprtykeep,
                                          pConnect->qos.lowprtydata,
                                          pConnect->qos.lowprtygain,
                                          pConnect->qos.lowprtykeep,
                                          pConnect->qos.protection_type,
                                          pConnect->qos.prot_len,
                                          pConnect->qos.lowprot_len,
                                          pConnect->qos.reqexpedited,
                                          pConnect->qos.reqackservice) );
						if( CallObject->SpecialFlags & CALL_OBJ_PAD_CONNECTION )
						{
							CallObject->RemoteLinkSpeed = 
								PADThroughputClassTable[pConnect->qos.remthroughput];

                  	CallObject->LocalLinkSpeed = 
                  		PADThroughputClassTable[pConnect->qos.locthroughput];
						}
						else
						{
							// Should use configured line speed here... how to get it?
							CallObject->RemoteLinkSpeed = 2048000;
                  	CallObject->LocalLinkSpeed = 2048000;
						}
               }
					else
					{
						// Should use configured line speed here... how to get it?
						CallObject->RemoteLinkSpeed = 2048000;
               	CallObject->LocalLinkSpeed = 2048000;
					}
					DigiDump( DIGISXBINFO, ("   RemoteLinkSpeed = 0x%x\n"
					                        "   LocalLinkSpeed = 0x%x\n",
					                        CallObject->RemoteLinkSpeed,
					                        CallObject->LocalLinkSpeed ) );

					// Insert the callobject on the OfferingCalls list.  Later it will
					// be moved to the AdapterObject->Calls list once the connection has 
					// been either accepted or rejected.
					KeAcquireSpinLock( &PhysAdapter->Lock, &OldIrql );
					InsertTailList( &PhysAdapter->OfferingCalls, &CallObject->List );
					KeReleaseSpinLock( &PhysAdapter->Lock, OldIrql );
					 
					// Set timeout to cancel the call if no response from RAS in 10 seconds
					NdisMSetTimer( &CallObject->OfferingTimeout, 10 * 1000 );
					
               //
               // Indicate a new incoming call to TAPI
               //

					CallObject->InternalCallState = INTERNAL_CALL_STATE_OFFERING;
               CallObject->TapiCallState = LINECALLSTATE_OFFERING;
               CallObject->htCall = 0;
               AdapterContextSendTapiEvent( CallObject,
                                            LINE_NEWCALL,
                                            (ULONG_PTR)CallObject,
                                            0,
                                            0 );

					CallObject->pAdapter->Line.CurrentState |= LINEDEVSTATE_RINGING;
					if ( CallObject->pAdapter->Line.NotifyStates & LINEDEVSTATE_RINGING )
					{
						AdapterContextSendTapiEvent( CallObject,
						                             LINE_LINEDEVSTATE,
						                             LINEDEVSTATE_RINGING,
						                             1, 1 );
					}

               AdapterContextSendTapiEvent( CallObject,
                                            LINE_CALLSTATE,
                                            CallObject->TapiCallState,
                                            0,
                                            LINEMEDIAMODE_DIGITALDATA );
               break;

            }  // end case N_CI

            case N_CC:	// X.25 Call Confirm
					DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
					                      "Unexpected N_CC indication! ***\n") );
				   break;

            case N_RI:	// X.25 Reset Indication
					DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
					                      "Unexpected N_RI indication! ***\n") );
               break;

            case N_RC:	// X.25 Reset Confirm
					DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
					                      "Unexpected N_CC indication! ***\n") );
               break;

            case N_Abort:
            case N_DI:	// X.25 Clear Indication
            {
               struct xdiscf *pDisconnect = (PVOID)pControl;
					PDIGIWAN_CALL_OBJECT CallObject;
					PLIST_ENTRY ListEntry;
					BOOLEAN Found;

               DigiDump( DIGISXBINFO, ("ListenGetMsgComplete: %s\n"
                                       "   originator = 0x%x\n"
                                       "   reason = 0x%x\n"
                                       "   cause = 0x%x\n"
                                       "   diag = 0x%x\n"
                                       "   conn_id = 0x%x\n"
                                       "   indicated_qos = 0x%x\n",
                                       pMsgHeader->xl_command==N_DI?"N_DI":"N_Abort",
                                       pDisconnect->originator,
                                       pDisconnect->reason,
                                       pDisconnect->cause,
                                       pDisconnect->diag,
                                       pDisconnect->conn_id,
                                       pDisconnect->indicated_qos) );

					// Find the CallObject in the OfferingCalls list which has a matching
					// connection ID so we can disconnect it.

					KeAcquireSpinLock( &PhysAdapter->Lock, &OldIrql );
					ListEntry = &PhysAdapter->OfferingCalls;
					Found = FALSE;
					while ( ListEntry->Flink != &PhysAdapter->OfferingCalls )
					{
						CallObject = CONTAINING_RECORD( ListEntry->Flink,
						                                DIGIWAN_CALL_OBJECT,
						                                List );
						ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
						if ( CallObject->conn_id == (ULONG)pDisconnect->conn_id )
						{
							RemoveEntryList( &CallObject->List );
							Found = TRUE;
							break;
						}
					}

					KeReleaseSpinLock( &PhysAdapter->Lock, OldIrql );

					if ( !Found )
						// Couldn't find it, do nothing!
						break;

               // Send Tapi a disconnect indication
               
               CallObject->TapiCallState = LINECALLSTATE_DISCONNECTED;
               AdapterContextSendTapiEvent( CallObject,
                                            LINE_CALLSTATE,
                                            LINECALLSTATE_DISCONNECTED,
                                            LINEDISCONNECTMODE_NORMAL,
                                            0 );
					break;

            }  // end case N_DI

            case N_DC:	// X.25 Clear Confirm
					DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
					                      "Unexpected N_DC indication! ***\n") );
               break;

            case N_Xlisten:	// Confirmation of previous XListen request
            {
               struct xlistenf *pListen = (PVOID)pControl;
               DigiDump( DIGISXBINFO, ("N_Xlisten, lmax = 0x%x, l_result = 0x%x\n",
                                       pListen->lmax,
                                       pListen->l_result) );
               break;
            }  // end case N_Xlisten

            case N_Xcanlis:	// Confirmation of previous listen cancel request
            {
               struct xcanlisf *pListenCancel = (PVOID)pControl;
               DigiDump( DIGISXBINFO, ("N_Xcanlis, c_result = 0x%x\n",
                                       pListenCancel->c_result) );
               break;

            }  // end case N_Xcanlis

            case N_PVC_ATTACH:
					DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
					                      "Unexpected N_PVC_ATTACH indication! ***\n") );
               break;


            case N_PVC_DETACH:
					DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
					                      "Unexpected N_PVC_DETACH indication! ***\n") );
               break;

            default:
            {
               DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
                                     "Unknown XL_CTL command 0x%x received! ***\n",
                                     pMsgHeader->xl_command) );
               break;
            }
         }  // end switch( pMsgHeader->xl_command )

			break;

		case XL_DAT:
			DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: "
			                      "Unexpected XL_DAT message received! ***\n") );
			break;

		default:
			DigiDump( DIGIERRORS, ("*** ListenGetMsgComplete: Bad xl_type 0x%x\n", 
			                      pMsgHeader->xl_type ) );
			break;
	}

   //
   // Send down a new GetMsg request, reusing the old data buffer
   //
#if DBG
   RtlZeroMemory( pData, DEFAULT_EXTRA_DATA_SIZE );
#endif

	DigiDump( DIGIVERYLOUD, ("ListenGetMsgComplete: Scheduling DsGetMsg\n") );

	ScheduleGetMsg( &PhysAdapter->Dpc,
						 PhysAdapter->hListenStream,
	                sizeof( union X25_primitives ),
	                pData, DEFAULT_EXTRA_DATA_SIZE,
	                PhysAdapter, ListenGetMsgComplete );
}


NDIS_STATUS
RejectIncoming(
	PDIGIWAN_PHYS_ADAPTER PhysAdapter,
	ULONG ConnId )
/*++

Description:

	Used by TapiDrop to reject an incoming call without answering it first.  The 
	callobject must currently in the offering state.

Parameters:

	PhysAdapter
		The device on which the incoming call originated.

	ConnId
		The connection id passed up (from firmware) identifying this particular 
		incoming call.

--*/
{
	NTSTATUS Status;
	ASSERT( PhysAdapter );
	ASSERT( ConnId );

	Status = DisconnectRequest( PhysAdapter->hListenStream, 
	                            ConnId, 
	                            ListenDiscComplete );

	return( Status );
}



VOID
ListenDiscComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PVOID pControl,
	IN ULONG ControlLength, 
	IN PVOID pData,
	IN ULONG DataLength, 
	IN NTSTATUS IOStatus	)
/*++

Description:

	This routine is called when a DisconnectRequest request to Sxb completes.
	We have nothing to do, so we provide this empty callback routine.

Parameters:

	hOwner, Context, pControl, ControlLength, pData, IOStatus
		Not used

Return Value:

	None

--*/
{
	DigiDump( DIGIFLOW, ( "ListenDiscComplete\n" ) );
}



VOID
ListenComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PVOID pControl,
	IN ULONG ControlLength, 
	IN PVOID pData,
	IN ULONG DataLength, 
	IN NTSTATUS IOStatus	)
/*++

Description:

	This routine is called when a ListenRequest request to Sxb completes.
	Just free the control buffer.

Parameters:

	hOwner, Context, pControl, ControlLength, pData, IOStatus
		Not used

Return Value:

	None

--*/
{
	DigiDump( DIGIFLOW, ( "ListenComplete\n" ) );
	ASSERT( pControl );	// Don't free this, it's owned by dsapi library
	ASSERT( pData );		// Free this, we alloc'd it before calling DsPutMsgEx!
	DigiFreeMem( pData );
}



VOID
CancelListenComplete(
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PVOID pControl,
	IN ULONG ControlLength, 
	IN PVOID pData,
	IN ULONG DataLength, 
	IN NTSTATUS IOStatus	)
/*++

Description:

	This routine is called when a CancelListen request to Sxb completes.
	We have nothing to do, so we provide this empty callback routine.

Parameters:

	hOwner, Context, pControl, ControlLength, pData, IOStatus
		Not used

Return Value:

	None

--*/
{
	ASSERT( pControl );
	ASSERT( !pData );
	DigiDump( DIGIFLOW, ( "CancelListenComplete\n" ) );
}


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

Description:

	This routine is scheduled on every N_CI incoming call indication.  If the call 
	has not been answered when this routine fires, an N_DI disconnect request is 
	sent and a disconnect indication sent to tapi to cleanup the unanswered call.

Parameters:

	FunctionContext
		Pointer to the CallObject for which this timeout has been scheduled.

	SystemSpecific1, SystemSpecific2, SystemSpecific3
		Not used.

Return Value:

	None

--*/
{
	PDIGIWAN_CALL_OBJECT CallObject = FunctionContext;
	ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );
	DigiDump( DIGIFLOW, ("Enter CallOfferingTimeout, CallObject 0x%x, AddressID 0x%x\n",
	                    CallObject, CallObject->AddressID) );

	if ( CallObject->InternalCallState  == INTERNAL_CALL_STATE_OFFERING )
	{
		KIRQL OldIrql;
		// Still offering, give up!
		AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
		                             LINECALLSTATE_DISCONNECTED, 
		                             LINEDISCONNECTMODE_NORMAL, 0 );

		CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
		CallObject->TapiCallState = LINECALLSTATE_IDLE;
		AdapterContextSendTapiEvent( CallObject, LINE_CALLSTATE,
		                             LINECALLSTATE_IDLE, 0, 0 );

		// Force all pending indications now!
		AdapterContextTimerFunction( NULL, CallObject->pAdapter, NULL, NULL );

		KeAcquireSpinLock( &CallObject->PhysAdapter->Lock, &OldIrql );
		RemoveEntryList( &CallObject->List );
		KeReleaseSpinLock( &CallObject->PhysAdapter->Lock, OldIrql );

		DisconnectRequest( CallObject->PhysAdapter->hListenStream,
		                   CallObject->conn_id,
		                   ListenDiscComplete );
	}
	else
	{
		// Do nothing.  Call has already left the OFFERING state.
	}
}
