/*++

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

Module Name:

   connect.c

Abstract:

   This module provides needed functionality for managing connection
   requests.

Revision History:

   $Log: /Components/Windows/NT/Sync/Wan/CONNECT.C $
 * 
 * 17    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.
 * 
 * 16    3/18/97 12:40p Mikez
 * Corrected memory leak.  Previously alloc'd Rx Buffers were lost when
 * New Call Object routine zeroed out the structure.
 * 
 * 15    3/17/97 12:32p Mikez
 * Removed async completion of Make Call.  Removed IDLE Tapi indication on
 * an incoming N_DI message.
 * 
 * 14    3/14/97 7:07a Mikez
 * Make Call routine now completes sync, uses tapi events to indicate call
 * progress.
 * 
 * 13    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.
 * 
 * 12    3/04/97 2:56p Mikez
 * Added a Purge before sending down a Disc during a TAPI Drop request to
 * help speed up the drop completion.
 * 
 * 11    3/03/97 3:39p Mikez
 * Updated to use new SxbWrap SxbClose with async completion.
 * 
 * 10    3/03/97 9:23a Mikez
 * Updated to use async completions on MakeCall, Drop and Answer TAPI OID
 * requests.
 * 
 * 9     2/26/97 10:31a Mikez
 * Minor formatting changes.
 * 
 * 8     1/28/97 11:08a Mikez
 * Removed broken downlevel ras support.
 * 
 * 7     1/09/97 10:55a Mikez
 * Tapi includes a terminating null in the called X25 address field in
 * MakeCall.  Added code to strip it before passing the X25 address down
 * to firmware.
 * 
 * 6     12/18/96 12:56p Mikez
 * Corrected missing offset in moving more-bit concatenated frames into
 * RxBuffer.
 * 
 * 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.
 * 
 * 3     7/22/96 2:44p Mikez
 * Changed bad invalid assert on frame data being a ppp frame (0xff, 0x03)
 * into an if, then drop frame if not valid.
 * 
 * 1     3/11/96 5:06p Mikez
 * Revision 1.8  1996/01/04  17:05:10  unknown
 * Added code to respond to reset indication (N_RI) with a reset confirm.
 * 
 * Revision 1.7  1995/10/06  06:59:33  Craig_Harrison
 * Version 1.0.1.2 
 * 
 * Revision 1.6  1995/09/15  00:00:00  Craig_Harrison
 * First beta release
 * 
 * Revision 1.4  1995/06/01  10:09:05  rik
 * Updated for various problems and enhancements
 * 
 * Revision 1.3  1995/01/23  09:26:44  rik
 * Got direct X25 connections working reliably
 * 
 * Revision 1.2  1994/03/23  22:17:37  rik
 * Updated to compile with first integration
 *
 * Revision 1.1  1994/03/17  10:05:58  rik
 * Initial revision
 *

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

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

//
// Prototypes
//

NTSTATUS
PPPAppendFrame(
	IN PDIGIWAN_CALL_OBJECT CallObject,
	IN PUCHAR pData,
	IN ULONG DataLength );


NDIS_STATUS 
ConnectRequest( 
	IN HANDLE hStream,
	IN ULONG Snid,
	IN PUCHAR pRemoteAddress,
	IN ULONG AddressLength,
	IN PDSL_PUTMSG_COMPLETION_ROUTINE CompletionRoutine )
/*++

Routine Description:

	Initiate an X.25 call request by sending an N_CI request.

Arguments:

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

	Snid - subnet id of the line on which the call should be initiated.

	pRemoteAddress - X.25 called address in ascii format

	AddressLength - length (in bytes) of called address

	CompletionRoutine - The completion routine to be called when the ConnectRequest
	                    has been sent.

Return Value:

   NDIS_STATUS_PENDING - Waiting for confirmation of the call.
   NDIS_STATUS_RESOURCES - No resources available for the request.
	NDIS_STATUS_FAILURE - Invalid address (too long, too short, out of range)

--*/
{
   NTSTATUS Status;
   struct xcallf Connect;
	PUCHAR pExtraData;
   ULONG i;
	ULONG Offset;

	if ( AddressLength == 0 || AddressLength > DTEMAXSIZE )
		return( NDIS_STATUS_FAILURE );

   //
   // Send down the connect request, which causes an X.25 CallRequest packet to
	// be sent.
	//

	pExtraData = DigiAllocMem( NonPagedPool, sizeof( UCHAR ) );
	if ( !pExtraData )
		return( NDIS_STATUS_RESOURCES );

	*pExtraData = 0xCF; // PPP NLPID

   RtlZeroMemory( &Connect, sizeof(struct xcallf) );
   Connect.xl_type = XL_CTL;
   Connect.xl_command = N_CI;
   Connect.calledaddr.sn_id = Snid;
   Connect.calledaddr.aflags = EXT_ADDR;
   // Connect.calledaddr.nsap_len = 0;
   // Connect.conn_id = 0;
   // Connect.CONS_call = 0;
   // Connect.negotiate_qos = 0;
   // RtlZeroMemory( &Connect.calledaddr.NSAP[0], NSAPMAXSIZE );
   // RtlZeroMemory( &Connect.callingaddr, sizeof(struct xaddrf) );
   // RtlZeroMemory( &Connect.qos, sizeof(struct qosformat) );

	// Strip off terminating null if tapi included it 
	Connect.calledaddr.DTE_MAC.lsap_len = pRemoteAddress[AddressLength - 1] == 0 ?
		(UCHAR)AddressLength - 1 : (UCHAR)AddressLength;

	for ( Offset = 0, i = 0; i < Connect.calledaddr.DTE_MAC.lsap_len; i++ )
	{
		UCHAR Digit;
		
		if ( pRemoteAddress[i] < '0' || pRemoteAddress[i] > '9' )
			return( NDIS_STATUS_TAPI_INVALADDRESS );

		Digit = pRemoteAddress[i] ? pRemoteAddress[i] - '0' : 0;
		if ( i % 2 == 0 )
			Connect.calledaddr.DTE_MAC.lsap_add[Offset] = Digit << 4;
		else
			Connect.calledaddr.DTE_MAC.lsap_add[Offset++] |= Digit;
	}

   DigiDump( DIGIVERYLOUD, (" lsap_len = %d \n", 
             Connect.calledaddr.DTE_MAC.lsap_len) );

   for ( i = 0; i < LSAPMAXSIZE; i++ )
	   DigiDump( DIGIVERYLOUD, (" lsap_add[%d] = 0x%x\n", i, 
	             Connect.calledaddr.DTE_MAC.lsap_add[i]) );
   
	Status = DsPutMsgEx( hStream,
	                     (PVOID)&Connect, sizeof( Connect ),
	                     (PVOID)pExtraData, sizeof( UCHAR ),
	                     NULL, DS_PRIORITY_LOW, CompletionRoutine );

	// The ConnectRequest we just passed down, will be accepted or rejected with an N_CC
	// or N_DI, respectively.  N_CC is received when an X.25 CallConfirm packet is
	// received.  N_DI when an X.25 ClearIndication packet is received.
   //
   
   return( Status );
}  // end ConnectRequest



NDIS_STATUS 
DisconnectRequest( 
	IN HANDLE hStream,
	IN ULONG ConnId,
	IN PDSL_PUTMSG_COMPLETION_ROUTINE CompletionRoutine )
/*++

Routine Description:

   Sends a Disconnect request down, causing the firmware to send an X.25 ClearRequest
	packet on this circuit.

Arguments:

   hStream - handle on which to send the disconnect request.

	ConnId - connection id identifying an incoming call, or zero if an outgoing call.
	         Only one DisconnectRequest may be sent for a given ConnId, so caller is
				responsible for zeroing out his ConnId upon return.

	CompletionRoutine - DisconnectRequest sends an async PutMsg request down to Sxb.  
	                    This completion routine will be called when the request has
							  been sent.
--*/
{
   NTSTATUS Status;
   struct xdiscf Disconnect;

   RtlZeroMemory( &Disconnect, sizeof(struct xdiscf) );
   Disconnect.xl_type = XL_CTL;
   Disconnect.xl_command = N_DI;
   Disconnect.conn_id = ConnId;

	ASSERT( CompletionRoutine );
	Status = DsPutMsgEx( hStream,
	                    (PVOID)&Disconnect, sizeof( Disconnect ),
	                    NULL, 0,
	                    NULL, DS_PRIORITY_LOW,CompletionRoutine );

	return( Status );
}  // end DisconnectRequest



VOID
X25GetMsgComplete(
	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

Arguments:


Return Value:


--*/
{
   PDIGIWAN_CALL_OBJECT CallObject=(PDIGIWAN_CALL_OBJECT)hOwner;
   KIRQL OldIrql;
	S_X25_HDR *pMsgHeader = (PVOID)pControl;

   DigiDump( DIGIFLOW, ("X25GetMsgComplete: CallObject=0x%p\n", CallObject) );
	ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

   if( IoStatus == STATUS_CANCELLED )
   {
		if ( CallObject->InternalCallState == INTERNAL_CALL_STATE_CLOSING )
		{
	      // We are in the process of closing the VC.  Cleanup and exit.
			ASSERT( pData );
			DigiFreeMem( pData );
			return;
		}
		else
			// Likely did a purge to clear out before sending a disc request.
			goto X25GetMsgContinue;
   }

   ASSERT( IoStatus == STATUS_SUCCESS );
	ASSERT( ControlLength );

   switch( pMsgHeader->xl_type )
   {
      case XL_CTL:
      {
         DigiDump( DIGISXBINFO, ("XL_CTL:") );
         switch( pMsgHeader->xl_command )
         {
            case N_CI:	// X.25 Call Indication
					DigiDump( DIGIERRORS, 
					          ("*** Unexpected message received (N_CI) on "
					          "CallObject 0x%x! ***\n",
					          CallObject) );
					break;

            case N_CC:	// X.25 Call Confirm
            {
               struct xccnff *ConnectConfirm = (PVOID)pControl;

               DigiDump( DIGISXBINFO, ("N_CC: conn_id = 0x%x\n"
                                       "      CONS_call = 0x%x\n"
                                       "      negotiate_qos = 0x%x\n"
                                       "      responder.sn_id = '%c' 0x%x\n",
                                       ConnectConfirm->conn_id,
                                       ConnectConfirm->CONS_call,
                                       ConnectConfirm->negotiate_qos,
                                       ConnectConfirm->responder.sn_id,
                                       ConnectConfirm->responder.sn_id) );

					ASSERT( CallObject->InternalCallState == INTERNAL_CALL_STATE_PROCEEDING );
					if ( CallObject->InternalCallState == INTERNAL_CALL_STATE_PROCEEDING )
					{
	               // Send Lineup and connected indications to wan and tapi, respectively.

	               AdapterContextWanLineup( CallObject, NULL );

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

	               AdapterContextSendTapiEvent( CallObject,
	                                            LINE_CALLSTATE,
	                                            LINECALLSTATE_CONNECTED, 0, 0 );
					}
				   break;
            }

            case N_RI:	// X.25 Reset Indication
            {
               struct xrstf *pReset = (PVOID)pControl;
					struct xrscf ResetConfirm;

               DigiDump( DIGISXBINFO, ("N_RI\n"
                                       "   originator = 0x%x\n"
                                       "   reason = 0x%x\n"
                                       "   cause = 0x%x\n"
                                       "   diag = 0x%x\n",
                                       pReset->originator,
                                       pReset->reason,
                                       pReset->cause,
                                       pReset->diag) );

               //
               // Confirm the reset.
               //

               RtlZeroMemory( &ResetConfirm, sizeof( ResetConfirm ) );
               ResetConfirm.xl_type = XL_CTL;
               ResetConfirm.xl_command = N_RC;

					DsPutMsg( CallObject->hStream,
					          (PVOID)&ResetConfirm, sizeof( ResetConfirm ),
					          NULL, 0 ,DS_PRIORITY_LOW);
					if ( CallObject->InternalCallState == INTERNAL_CALL_STATE_PROCEEDING )
					{
						CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;
	               AdapterContextSendTapiEvent( CallObject,
	                                            LINE_CALLSTATE,
	                                            LINECALLSTATE_DISCONNECTED, 
	                                            LINEDISCONNECTMODE_NORMAL, 0 );
					}
               break;
            }

            case N_RC:	// X.25 Reset Confirm
            {
					DigiDump( DIGIERRORS, 
					          ("*** X25GetMsgComplete: Unexpected N_RC indication! ***\n") );
				   break;
				}  // end case N_RC

				case N_Abort:
				case N_DI:	// X.25 Clear Indication
				{
				   struct xdiscf *pDisconnect = (PVOID)pControl;
				   ULONG PreviousCallState;

				   DigiDump( DIGISXBINFO, ("%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) );

				   PreviousCallState = CallObject->InternalCallState;
				   CallObject->InternalCallState = INTERNAL_CALL_STATE_IDLE;

				   // Tell NdisWan the connection is down
				   if( PreviousCallState == INTERNAL_CALL_STATE_CONNECTED )
				      AdapterContextWanLinedown( CallObject, NULL );

				   // Tell Tapi the connection is down
				   CallObject->TapiCallState = LINECALLSTATE_DISCONNECTED;
				   AdapterContextSendTapiEvent( CallObject,
				                                LINE_CALLSTATE,
				                                LINECALLSTATE_DISCONNECTED,
				                                LINEDISCONNECTMODE_NORMAL, 0 );

					// Don't force another GetMsg.  This call will be closed or 
					// OidTapiMakeCall will initiate a new connection on it.
					ASSERT( pData );
					DigiFreeMem( pData );
					return;
				}  // end case N_DI

				case N_DC:	// X.25 Clear Confirm
				{
				   DigiDump( DIGISXBINFO, ("N_DC\n") );

					// Don't force another GetMsg.  This call will be closed or 
					// OidTapiMakeCall will initiate a new connection on it.
					ASSERT( pData );
					DigiFreeMem( pData );
					return;
            }  // end case N_DC

            case N_Xlisten:	// Confirmation of previous XListen request
					DigiDump( DIGIERRORS, ("*** X25GetMsgComplete: "
					                      "Unexpected N_Xlisten indication! ***\n") );
					break;

            case N_Xcanlis:	// Confirmation of previous listen cancel request
					DigiDump( DIGIERRORS, ("*** X25GetMsgComplete: "
					                      "Unexpected N_Xcanlis indication! ***\n") );
					break;

            case N_PVC_ATTACH:
            {
               DigiDump( DIGISXBINFO, ("N_PVC_ATTACH\n") );
					ASSERT( 0 && "*** Unexpected message received: N_PVC_ATTACH ***\n" );
               break;
            }  // end case N_PVC_ATTACH

            case N_PVC_DETACH:
            {
               DigiDump( DIGISXBINFO, ("N_PVC_DETACH\n") );
					ASSERT( 0 && "*** Unexpected message received: N_PVC_DETACH ***\n" );
               break;
            }  // end case N_PVC_DETACH

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

         break;
      }  // end case XL_CTL

      case XL_DAT:
      {
         DigiDump( DIGISXBINFO, ("XL_DAT: ") );
         switch( pMsgHeader->xl_command  )
         {
            case N_Data:	// X.25 Data packet
            {
               struct xdataf *pNData = (PVOID)pControl;

					DigiDump( DIGISXBINFO, ("N_Data\n"
					                        "   Dbit 0x%x, Qbit 0x%x, More 0x%x\n",
					                        pNData->setDbit,
					                        pNData->setQbit,
					                        pNData->More) );

               // Using delivery-bit?
               if ( pNData->setDbit )
               {
						// Yes, acknowledge the packet.
	               struct xdatacf DataAck;

	               RtlZeroMemory( &DataAck, sizeof(struct xdatacf) );
	               DataAck.xl_type = XL_DAT;
	               DataAck.xl_command = N_DAck;

						DsPutMsg( CallObject->hStream,
						          (PVOID)&DataAck, sizeof( DataAck ),
						          NULL, 0, DS_PRIORITY_LOW );
               }

					// Drop and ignore received data if we're not in the connected state.
					if ( CallObject->InternalCallState != INTERNAL_CALL_STATE_CONNECTED )
						break;

					if( CallObject->SpecialFlags & CALL_OBJ_PAD_CONNECTION )
					{
						// PAD Connection
						if( pNData->setQbit ) // Q-bit set means X.29 packet
						{
							DigiDump( DIGINOTIMPLEMENTED, ("Received X29 packet on CallObj (0x%p)\n", CallObject));
						}
						else	// Data packet
							// Process received bytes
							QueuePadByteStream( CallObject, pData, DataLength );
					}
					else
					{
						// X25 Connection - remote is *not* a PAD
						//
						// Received X.25 packet.  If the More bit is clear, then we 
						// have a complete PPP Frame, indicate a receive.  Otherwise, 
						// we've got only a portion of the PPP frame, store it and wait.
						//

						DigiDump( (DIGIWANINFO|DIGIDIAG4),
						          ("NData recv: length=%d, RxOffset=%d\n",
						          DataLength,
						          CallObject->RxOffset) );

						KeAcquireSpinLock( &CallObject->Lock, &OldIrql );

						// Don't waste time copying if we must drop the frame
						if ( !( CallObject->SpecialFlags & CALL_OBJ_DROP_FRAME ) )
						{
							// This frame's a keeper, process it.
							NTSTATUS Status;
							
							// There could be previous data marked with the More-bit stored
							// in the CallObject RxBuffer, append this packet to it.  If no 
							// previous data, insert at the beginning of RxBuffer.
							Status = PPPAppendFrame( CallObject, pData, DataLength );

							if ( !NT_SUCCESS( Status ) )
								// Rx buffer has overrun!
								CallObject->SpecialFlags |= CALL_OBJ_DROP_FRAME;

							ASSERT( CallObject->RxOffset < CallObject->CurrentMaxMtuSize );
						}

						KeReleaseSpinLock( &CallObject->Lock, OldIrql );

	               if( !pNData->More )
	               {
	                  // We've reached the end of a packet.  Indicate it.
							if ( CallObject->SpecialFlags & CALL_OBJ_DROP_FRAME )
							{
								// It's a bad packet. Tell Ndis we're dropping it.
								AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_ALIGNMENT );
								CallObject->SpecialFlags &= ~CALL_OBJ_DROP_FRAME;
								ASSERT( CallObject->RxOffset == 0 );
								CallObject->RxOffset = 0;
							}
							else
							{
		                  ASSERT( CallObject->RxOffset < CallObject->CurrentMaxMtuSize );
		                  AdapterContextWanIndicateReceive( CallObject,
		                                                    CallObject->RxBuffer,
		                                                    CallObject->RxOffset );

								// AdapterContext now owns that buffer.  Must alloc a new one.
		                 	CallObject->RxOffset = 0;
		               	CallObject->RxBuffer = DigiAllocMem( NonPagedPool, 
		               	                       CallObject->CurrentMaxMtuSize );
							}
	               }
					}
               break;

            }  // end case N_Data

            case N_DAck:	// Dbit data acknowledge
					DigiDump( DIGIERRORS, ("*** X25GetMsgComplete: "
					                      "Unexpected N_DAck indication! ***\n") );
					break;

				// Expedited data, this is the user data from an interrupt packet.
				// Apparently the only time these would be received is when the user
				// on a remote PAD hits BREAK. For now discard the data and return
				// N_EAck, which will cause the FEP to send an interrupt confirm packet.
            case N_EData:	// X.25 Interrupt Packet
            {
               NTSTATUS Status;
               struct xedatacf EDataConfirm;

               DigiDump( DIGISXBINFO, ("N_EData\n") );

               RtlZeroMemory( &EDataConfirm, sizeof(struct xedatacf) );
               EDataConfirm.xl_type = XL_DAT;
               EDataConfirm.xl_command = N_EAck;

					Status = DsPutMsg( CallObject->hStream,
					                   (PVOID)&EDataConfirm, sizeof( EDataConfirm ),
					                   NULL, 0, DS_PRIORITY_LOW );
               break;
            }  // end case N_EData

            case N_EAck:
					DigiDump( DIGIERRORS, ("*** X25GetMsgComplete: "
					                      "Unexpected N_Xlisten indication! ***\n") );
					break;

            default:
               DigiDump( DIGIERRORS, 
                         ("Unknown XL_DAT command from controller (0x%x)\n",
                         pMsgHeader->xl_command) );
               ASSERT( FALSE && "Unknown XL_DAT command" );
               break;
         }  // end switch( pMsgHeader->xl_command )

         break;
      }  // end case XL_DAT

      default:
      {
         DigiDump( DIGIERRORS, ("Unknown xl_type command from controller (0x%x)\n",
                                pMsgHeader->xl_type) );
         ASSERT( FALSE );
         break;
      }
   }  // end switch( pMsgHeader->xl_type )

X25GetMsgContinue:;

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

	DigiDump( DIGIVERYLOUD, ("X25GetMsgComplete: Calling SxbGetMsg\n") );

	ScheduleGetMsg( &CallObject->Dpc,
	                CallObject->hStream,
	                sizeof( union X25_primitives ),
	                pData, DEFAULT_EXTRA_DATA_SIZE,
	                CallObject, X25GetMsgComplete );
}  // end X25GetMsgComplete



NTSTATUS
PPPAppendFrame(
	IN PDIGIWAN_CALL_OBJECT CallObject,
	IN PUCHAR pData,
	IN ULONG DataLength )
/*++

Description:

	This routine moves received data into rx frame buffer.  If rx frame buffer
	does not exist, a new one is alloc'd.

Return Value:

	STATUS_SUCCESS if successful, or error.

--*/
{
	NDIS_STATUS Status = NDIS_STATUS_SUCCESS;

	if ( !CallObject->RxBuffer )
	{
		CallObject->RxBuffer = DigiAllocMem( NonPagedPool, 
		                                     CallObject->CurrentMaxMtuSize );
		CallObject->RxOffset = 0;

		if ( !CallObject->RxBuffer )
			return( NDIS_STATUS_RESOURCES );
	}

	if ( CallObject->RxOffset + DataLength < CallObject->CurrentMaxMtuSize )
	{
		NdisMoveMemory( CallObject->RxBuffer + CallObject->RxOffset, pData, DataLength );
		CallObject->RxOffset += DataLength;
	}
	else
	{
		// Framing error!  Indicate error and reset buffer.
		CallObject->RxOffset = 0;
		CallObject->MtuOverrun++;
		AdapterContextWanIndicateFragment( CallObject, WAN_ERROR_ALIGNMENT );
		Status = NDIS_STATUS_FAILURE;
	}

	return( Status );
}



VOID
ScheduleGetMsg(
	IN PKDPC pDpc,
	IN HANDLE hStream,
	IN ULONG ControlLength,
	IN PVOID pData,
	IN ULONG DataLength,
	IN HANDLE Context,
	IN PDSL_GETMSG_COMPLETION_ROUTINE CompletionRoutine )
/*++

Description:

	Calls KeInsertQueueDpc to start the DPC to send down a GetMsg request.  Used by 
	GetMsgComplete routines rather than calling GetMsg directly to avoid recursion.

Parameters:

	pDpc
		Pointer to the dpc object for this stream.

	hSxbStream
		Sxb handle on which the msg will be sent.

	ControlLength
		Count of bytes to alloc for the control portion of the getmsg.

	pData
		Pointer to preallocated data buffer for this getmsg.

	DataLength
		Length of data buffer.

	Context
		Context value specific to this request.  This value will be returned
		to the GetMsgCompletionRoutine.  Typically, the buffer ptr (pData) 
		passed in will be at some offset inside a larger alloc'd buffer and
		the real alloc'd pointer is stored here.

	CompletionRoutine
		This routine will be called when the GetMsg request has completed and there
		is receive data to process.

Return Value:

	None

--*/
{
	PDIGIWAN_DEFERRED_GETMSG SchedGetMsg;

	SchedGetMsg = DigiAllocMem( NonPagedPool, sizeof( DIGIWAN_DEFERRED_GETMSG ) );
	if ( !SchedGetMsg )
	{
		DigiDump( DIGIERRORS,
		          ("*** Couldn't alloc memory for getmsg request! ***\n" ) );

		// Use the data buffer for this request, and alloc a new data buffer later.
		ASSERT( DataLength >= sizeof( DIGIWAN_DEFERRED_GETMSG ) );

		SchedGetMsg = (PVOID)pData;
		pData = NULL;
	}

	SchedGetMsg->Type = DIGIWAN_TYPE_DEFERRED_GETMSG;
	SchedGetMsg->pDpc = pDpc;
	SchedGetMsg->hStream = hStream;
	SchedGetMsg->ControlLength = ControlLength;
	SchedGetMsg->pData = pData;
	SchedGetMsg->DataLength = DataLength;
	SchedGetMsg->Context = Context;
	SchedGetMsg->CompletionRoutine = CompletionRoutine;

	KeInsertQueueDpc( pDpc, SchedGetMsg, NULL );
}



VOID
DigiWanDeferredGetMsg(
	IN PKDPC Dpc,
	IN PVOID DeferredContext,
	IN PVOID SystemContext1,
	IN PVOID SystemContext2 )
/*++

Description:

	This routine is scheduled to start a new SxbGetMsg request.  The 
	GetMsgCompletion routines don't call GetMsg directly to avoid recursion.

Parameters:

	DeferredContext
		This is a pointer to either a CallObject or PhysAdapter, depending on
		who requested the Deferred GetMsg.  

	SystemContext1
		Pointer to a DIGIWAN_DEFERRED_GETMSG struct defining the GetMsg to be
		processed.

--*/
{
	PDIGIWAN_DEFERRED_GETMSG DeferredGetMsg = SystemContext1;
	BOOLEAN bFreeDeferredGetMsg = TRUE;

	ASSERT( DeferredGetMsg->Type == DIGIWAN_TYPE_DEFERRED_GETMSG );

	//
	// If no data buffer, ScheduleGetMsg borrowed it for the DeferredGetMsg, 
	// so use the DeferredGetMsg struct for pData again.
	//

	if ( !DeferredGetMsg->pData )
	{
		// Yup, we borrowed it.  Put it back.

		DeferredGetMsg->pData = (PVOID)DeferredGetMsg;
		bFreeDeferredGetMsg = FALSE;
	}

	ASSERT( DeferredGetMsg->Context && ( 
			  *(DIGIWAN_TYPE *)DeferredGetMsg->Context == DIGIWAN_TYPE_CALL_OBJECT ||
			  *(DIGIWAN_TYPE *)DeferredGetMsg->Context == DIGIWAN_TYPE_PHYS_ADAPTER ) );

	// If it's a call object and is in the closing process, don't send the GetMsg, 
	// just free it.
	if ( *(DIGIWAN_TYPE *)DeferredGetMsg->Context == DIGIWAN_TYPE_CALL_OBJECT )
	{
		PDIGIWAN_CALL_OBJECT CallObject = DeferredGetMsg->Context;
		if ( CallObject->InternalCallState == INTERNAL_CALL_STATE_CLOSING )
		{
			DigiFreeMem( DeferredGetMsg->pData );
			if ( bFreeDeferredGetMsg )
				DigiFreeMem( DeferredGetMsg );
			return;
		}
	}

	DsGetMsgEx( DeferredGetMsg->hStream,
	            DeferredGetMsg->ControlLength,
	            DeferredGetMsg->pData,
	            DeferredGetMsg->DataLength,
	            DeferredGetMsg->Context,
				DS_PRIORITY_LOW,
	            DeferredGetMsg->CompletionRoutine );

	if ( bFreeDeferredGetMsg )
		DigiFreeMem( DeferredGetMsg );
}
