/*++

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

   tranmit.c

Abstract:


Revision History:

   $Log: /Components/Windows/NT/Sync/Wan/x25send.c $
 * 
 * 9     4/28/97 1:55p 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.
 * 
 * 8     3/17/97 12:31p Mikez
 * Added check to fail a Send request when the callstate is not CONNECTED.
 * 
 * 7     1/29/97 4:53p Mikez
 * Added more debug defines.
 * 
 * 5     1/02/97 4:54p Mikez
 * Fixed problems with Send completions being called before returning from
 * Sxb in DigiWanSend.
 * 
 * 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:29p 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:07p Mikez
 * Revision 1.7  1995/10/06  07:00:16  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:08:36  rik
 * Updated for various problems and enhancements
 * 
 * Revision 1.3  1995/01/23  09:27:11  rik
 * Got direct X25 connections working reliably
 * 
 * Revision 1.2  1994/03/23  22:17:47  rik
 * Updated to compile with first integration
 *
 * Revision 1.1  1994/03/17  10:05:35  rik
 * Initial revision
 *

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

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


extern ULONG DigiTxElapsed;

extern ULONG NdisTxBenchmark;

NDIS_SPIN_LOCK SendStatusLock;


//
// Helper functions for this module.
//
NTSTATUS 
DigiWanSendComplete( 
	IN HANDLE hOwner,
	IN HANDLE Context,
	IN PUCHAR pControl,
	IN ULONG ControlLength,
	IN PUCHAR pData,
	IN ULONG DataLength,
	IN NTSTATUS IoStatus
	);


NDIS_STATUS DigiWanSend( IN NDIS_HANDLE MiniportAdapterContext,
                         IN NDIS_HANDLE NdisLinkHandle,
                         IN PNDIS_WAN_PACKET Packet )
/*++

Routine Description:

   This routine is responsible for calling DIGISXB to send the given
   data out on the wire.

Arguments:

   LinkContext - handle returned from NDIS_LINE_UP.  This should
                 be a pointer to a DIGIWAN_CALL_OBJECT.

   Packet - pointer to NDIS_WAN_PACKET structure containing the
            packet to send.

            Note: This packet should be a contiguous buffer.

Return Value:

   NDIS_STATUS_PENDING - Indicate we are working on the packet.
	NDIS_STATUS_RESOURCES - If we can't handle the packet. Miniport send
		should then be called again later with the same packet.

--*/
{
   PDIGIWAN_CALL_OBJECT CallObject=(PDIGIWAN_CALL_OBJECT)NdisLinkHandle;
	NDIS_STATUS Status;
	LARGE_INTEGER StartTime;
   struct xdataf NData;
	PNDIS_WAN_PACKET OriginalPacket;

   DigiDump( (DIGIFLOW|DIGIWANINFO),
             ("DigiWanSend: AC=0x%x, CallObject=0x%x, Packet=0x%x\n",
               MiniportAdapterContext,
               NdisLinkHandle,
               Packet) );

   ASSERT( CallObject->Type == DIGIWAN_TYPE_CALL_OBJECT );

   //
   // We should never get a transmit request when we are in a state of
   // closing!!!
   //
   ASSERT( CallObject->InternalCallState == INTERNAL_CALL_STATE_CONNECTED );
	if ( CallObject->InternalCallState != INTERNAL_CALL_STATE_CONNECTED )
	{
		DigiDump( DIGIERRORS, 
		          ("*** Error X25Send: InternalCallState(0x%x) != CONNECTED ***\n",
		          CallObject->InternalCallState ) );
		return( NDIS_STATUS_TAPI_INVALCALLSTATE );
	}

   //
   // Initialize the MacReserved areas
   //

   Packet->MacReserved1 = NULL;
   Packet->MacReserved2 = NULL;
	if(DigiTxElapsed)			// check for elapsed Tx time
	{
		KeQuerySystemTime(&StartTime);	// time stamp this packet
		StartTime.QuadPart /= 10000; 		// convert to milliseconds
		Packet->MacReserved2 = (PVOID)( StartTime.LowPart ); // save time stamp
	}

	// Init MacReserved3 and 4.  They are protected by the SendStatusLock 
	// spinlock.  They're initialized here, and used in WanSendComplete and 
	// right after the call to DsPutMsg.  Don't acquire spinlock here, as it's 
	// only needed to synchronize access during and after the call to DsPutMsg.

   // MacReserved3 is used as boolean indicating whether the PutMsg request
	// is still in progress.  It is cleared immediately after the PutMsg call.  
	// DigiWanSendComplete will only generate a WanSendComplete indication if 
	// this flag is FALSE.
   Packet->MacReserved3 = (PVOID)TRUE;

	// MacReserved4 is the NDIS_STATUS_XXX value we should return on exit of 
	// this routine.  If DigiWanSendComplete executes before returning from the
	// PutMsg call, he will set this value to the appropriate return value, 
	// typically NDIS_STATUS_SUCCESS.
   Packet->MacReserved4 = (PVOID)NDIS_STATUS_PENDING;

	OriginalPacket = Packet;

   //
   // If we are connected with a PAD client, then we need to do
   // some special processing of the outgoing data.  Get setup
   // and ready to go.
   //

   if( CallObject->SpecialFlags & CALL_OBJ_PAD_CONNECTION )
   {
      PUCHAR NewCurrentBuffer;
      PNDIS_WAN_PACKET NewPacket;
      ULONG AllocSize;
      
      //
      // Okay, we will allocate enough memory for a worst case scenerio of
      // all the data having transparency applied, which increases the buffer
      // size * 2.  We also add 16-bits for the CRC and 2 8-bit Flag Sequences
      // to make up the rest of the frame.
      //
      
      AllocSize = (ULONG)(Packet->CurrentBuffer - Packet->StartBuffer) +
                  (Packet->CurrentLength * 2) +
                  (CallObject->PostamblePadding * 2) +
                  (2 * sizeof(UCHAR));
      
      // We save a copy of the original Wan Packet in the MacReserved1 field.
      NewPacket = (PNDIS_WAN_PACKET)DigiAllocMem( NonPagedPool, sizeof(NDIS_WAN_PACKET) );
      NewPacket->MacReserved1 = Packet;
		NewPacket->MacReserved2 = Packet->MacReserved2;
		NewPacket->MacReserved3 = Packet->MacReserved3;
		NewPacket->MacReserved4 = Packet->MacReserved4;
      
      NewCurrentBuffer = DigiAllocMem( NonPagedPool, AllocSize );
      
      NewPacket->CurrentBuffer = NewCurrentBuffer + (Packet->CurrentBuffer - Packet->StartBuffer);
      NewPacket->StartBuffer = NewCurrentBuffer;
      NewPacket->EndBuffer = NewCurrentBuffer + AllocSize;

      //
      // Okay, the client is an async PAD connection.  Which
      // framing should we use??
      //
      if( CallObject->SpecialFlags & CALL_OBJ_PPP_FRAMING )
      {
         AssemblePPPFrame( CallObject,
                           NewPacket,
                           Packet );
      }
      else
      {
         //
         // We should have never gotten this!!!!
         //
         ASSERT( FALSE && "Unknown Framing type on a send!!!!" );
      }
		Packet = NewPacket;
   }

	//
	// Send it!
	//

	NdisZeroMemory( &NData, sizeof( NData ) );

   NData.xl_type = XL_DAT;
   NData.xl_command = N_Data;
   Status = DsPutMsgEx( CallObject->hStream,
                        (PVOID)&NData, sizeof( NData ),
                        Packet->CurrentBuffer, Packet->CurrentLength,
                        Packet, DS_PRIORITY_LOW, DigiWanSendComplete );

	NdisAcquireSpinLock( &SendStatusLock );
#if DBG
	if ( Status == STATUS_SUCCESS )
		ASSERT( (ULONG)OriginalPacket->MacReserved4 == STATUS_SUCCESS );
#endif

	// This value was initialized to STATUS_PENDING and could only have been
	// changed if the PutMsg request completed and DigiWanSendComplete changed it.
	Status = (NDIS_STATUS)(ULONG_PTR)OriginalPacket->MacReserved4;

	// If DigiWanSendComplete hasn't been called yet, we tell him that he must 
	// complete the request asynchronously because we're returning STATUS_PENDING.
	OriginalPacket->MacReserved3 = FALSE;

	NdisReleaseSpinLock( &SendStatusLock );

	return( Status );

}  // end DigiWanSend


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

Routine Description:

   Completes the current transmit packet.

--*/
{
   PDIGIWAN_CALL_OBJECT CallObject = hOwner;
   PNDIS_WAN_PACKET WanPacket = Context;
	LARGE_INTEGER EndTime;
	ULONG ElapsedTime;

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

	DigiDump( DIGIFLOW, 
	          ("Enter DigiWanSendComplete, CallObject 0x%x, Packet 0x%x\n", 
	          CallObject, WanPacket) );

	if(DigiTxElapsed)
	{
		KeQuerySystemTime(&EndTime);		// get current system time
		EndTime.QuadPart /= 10000;	 		// convert to milliseconds
		ElapsedTime = (ULONG)(EndTime.LowPart - (ULONG_PTR)WanPacket->MacReserved2);
		if(ElapsedTime >= DigiTxElapsed)	// if > DigiTxElapsed second report it
		{
			DigiDump( DIGIERRORS, 
			          ("Tx processing time = %d mS, CallObj=0x%x\n", 
			          ElapsedTime, CallObject) ); 
		}
	}

   if( CallObject->SpecialFlags & CALL_OBJ_PAD_CONNECTION )
   {
		// Cleanup and restore packet original pointer

      PNDIS_WAN_PACKET OriginalPacket = WanPacket->MacReserved1;

      DigiFreeMem( WanPacket->StartBuffer );
      DigiFreeMem( WanPacket );

		WanPacket = OriginalPacket;
   }

	NdisAcquireSpinLock( &SendStatusLock );
	// If DigiWanSend's call to PutMsg is still in progress, the request can be
	// completed synchronously.  Tell him we're done with it.
	if ( WanPacket->MacReserved3 )
	{
		// Still inside DigiWanSend's call to PutMsg, let him complete the request.
		WanPacket->MacReserved4 = (PVOID)NDIS_STATUS_SUCCESS;
		NdisReleaseSpinLock( &SendStatusLock );
	}
	else
	{
		// DigiWanSend completed the request STATUS_PENDING, we must indicate the
		// completion.
		NdisReleaseSpinLock( &SendStatusLock );
	   AdapterContextWanSendComplete( CallObject,
	                                  WanPacket,
	                                  NDIS_STATUS_SUCCESS );
	}

   return( STATUS_MORE_PROCESSING_REQUIRED );

}  // end DigiWanSendComplete

