/*++

*******************************************************************************
* Copyright (c) 1994-95 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 copyright work  Digi   International *
* Inc.    Except   as   permitted   by  federal  law,  17 USC 117, copying is *
* strictly prohibited.                                                        *
*                                                                             *
* Digi  International  Inc.  CONFIDENTIAL  -   (Digi    International    Inc. *
* Confidential  when   combined   with   the   aggregated  modules  for  this *
* product) OBJECT CODE ONLY SOURCE MATERIALS                                  *
*                                                                             *
* (C) COPYRIGHT Digi International Inc. 1994-95                               *
*                                                                             *
* All Rights Reserved                                                         *
*                                                                             *
* US   Government   Users   Restricted   Rights   -   Use,   duplication   or *
* disclosure  restricted by GSA ADP Schedule Contract with Digi International *
* Inc.                                                                        *
*                                                                             *
*******************************************************************************

Module Name:

   msg.c

Abstract:

	This module encapsulates all transactions to and from the DStreams (or DsApi)
	interface.  Protocol specific modules (FR, X25, etc.) use routines in this
	module rather than directly accessing the DsApi.  This keeps common mgmt 
	issues isolated to this one module.

	All manipulations of these structs (alloc'ing, freeing, setting params) is 
	managed by routines in this module to hide implementation details of the 
	actual structures used.  Reason: we	encapsulate the S_xxx structs inside our 
	own DIGILAN_xxx structs so we can 1)	reduce the number of allocations per 
	struct, and 2) keep extra information along with the struct.  See msg.h 
	for details on the DIGILAN_xxx structs.

Author:

   Michael Zarns

Revision History:

   $History: msg.c $
 * 
 * *****************  Version 10  *****************
 * User: Mikez        Date: 6/19/97    Time: 12:05p
 * Updated in $/Components/Windows/NT/Sync/Build/Lan/Src
 * Mods to support new SNID based firmware api for Frame Relay.
 * 
 * *****************  Version 9  *****************
 * User: Mikez        Date: 4/28/97    Time: 1:55p
 * Updated in $/Components/Windows/NT/Sync/Build/Lan/Src
 * Updated to use DsApi library.
 * 
 * *****************  Version 8  *****************
 * User: Mikez        Date: 10/25/96   Time: 8:39a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * 
 * *****************  Version 7  *****************
 * User: Mikez        Date: 10/24/96   Time: 1:55p
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Added support for new firmware release, 1.1.1B.  Changes to support new
 * synchronous SxbWrapper api SxbOpenWait.
 * 
 * *****************  Version 6  *****************
 * User: Mikez        Date: 10/03/96   Time: 10:15a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Pulled Sxb interface code out of this driver and use newly created Sxb
 * interface wrapper library, SxbWrap.
 * 
 * *****************  Version 5  *****************
 * User: Mikez        Date: 9/26/96    Time: 11:29a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Added code to reschedule a GetMsg if one could not be allocated
 * immediately.
 * 
 * *****************  Version 4  *****************
 * User: Mikez        Date: 9/26/96    Time: 8:23a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Moved all Sxb interface routines into two source files - msg.c and
 * irp.c.  
 * 
 * *****************  Version 3  *****************
 * User: Mikez        Date: 9/19/96    Time: 9:23a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Added X25 PVC support.
 * 
 * *****************  Version 2  *****************
 * User: Mikez        Date: 8/13/96    Time: 10:48a
 * Updated in $/Components/Windows/NT/Sync/Lan
 * Fully abstracted Frame Relay code into three routines:  FrSend,
 * FrReceive and FrInit, and one AdapterObject field: RxControlLength.
 * This revision works and support Frame Relay only.
 * 
 * *****************  Version 1  *****************
 * User: Mikez        Date: 8/09/96    Time: 9:08a
 * Created in $/Components/Windows/NT/Sync/Lan

--*/
#include "digilan.h"
#include "DsApi.h"

///////////////////////////////////////////////////////////////////////////////
//
// Internal Function Prototypes
//

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

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



NDIS_STATUS
DlwOpen(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PCSZ pcszDeviceName )
/*++

Description:

	This routine encapsulates the DStreams Open Stream (IOCTL_DS_OPEN IRP) 
	request.  The returned hStream value in the adapter object's hStream field.

Parameters:

	pAdapter
		The adapter object which defines both the ndis virtual adapter and X.25
		PVC (these two are synonymous in this context).

	pcszDeviceName
		String containing the name of the device to open, of the form "X250" or "FR".

Return Value:

	NDIS_STATUS_SUCCESS, or some error.

--*/
{
	NDIS_STATUS Status;
	ULONG Error = 0;

	DigiDump( DIGIFLOW, ("Enter DlwOpen\n") );
	if ( !pAdapter )
		return( STATUS_INVALID_PARAMETER_1 );
	if ( !pcszDeviceName )
		return( STATUS_INVALID_PARAMETER_2 );

	// Synchronous Open Stream request
	Status = DsOpen( pAdapter->hDevice, pcszDeviceName, pAdapter, NULL, 0,
	                 &pAdapter->hStream, &Error );

	if ( NT_ERROR( Status ) || Error )
		return( NDIS_STATUS_FAILURE );

	DigiDump( DIGIFLOW, ("Leave DlwOpen\n") );
	return( Status );
}



NDIS_STATUS
DlwIoctl(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN ULONG Ioctl,
	IN PUCHAR pData,
	IN ULONG DataLength,
	OUT PULONG pReturnCode )
/*++

Description:

	This routine encapsulates a DStreams Ioctl request used to send ioctls down to 
	the firmware.

Parameters:

	pAdapter
		The adapter object which defines both the ndis virtual adapter and X.25
		PVC (these two are synonymous in this context) on which this ioctl is to
		sent.

	Ioctl
		The ioctl command to send.

	pData
		The data field of the ioctl.

	DataLength
		Length in bytes of the data field.

	pReturnCode
		ReturnCode returned by sxb firmware.  

Return Value:

	NDIS_STATUS_SUCCESS, or appropriate error.

--*/
{
	NTSTATUS Status;

	ENTER( "DlwIoctl" );

	Status = DsIoctl( pAdapter->hStream, Ioctl, pData, DataLength, pReturnCode );

	DigiDump( DIGIFLOW, ("Leave DlwIoctl, status 0x%x\n", Status) );
	return( Status );
}



NTSTATUS
DlwPutMsg(
	IN PDIGILAN_LOGICAL_ADAPTER pAdapter,
	IN PUCHAR pControlBuffer,
	IN ULONG ControlLength,
	IN PNDIS_PACKET pNdisPacket,
	IN ULONG DataOffset,
	IN ULONG DataLength
	)
/*++

Description:

	This routine along with its counterpart, PutMsgComplete encapsulate the 
	PutMsg and Irp usage for sending packets.  Allocs, initializes and sends
	the requested data in a PutMsg wrapped in an Irp.

Parameters:

	pAdapter
		The adapter object which describes the VC on which this data should be sent.

	pControlBuffer
		Address of control header.  The entire control buffer is copied into the 
		PutMsg so caller may free it immediately after return from this routine.

	ControlLength
		Length of control header.  This defines how many bytes at pControlBuffer
		are copied into the PutMsg.

	pNdisPacket
		Pointer to original NdisPacket.  Inserted into the PutMsg to be retrieved at
		CompletionRoutine time when we send up the NdisSendComplete acknowledgement.
	
	DataOffset
		Offset into the ndis packet at which to start copying data.  This defines
		the payload of the packet which should be sent over the wire.  A new 
		contiguous buffer will be alloc'd to hold the packet.

	DataLength
		Length of data packet.  This value is inserted into the PutMsg.  This
		defines the size of data buffer alloc'd.

Return Value:

	The return value from IoCallDriver, or some error.

--*/
{
	NDIS_STATUS Status;
	PUCHAR pData = NULL;

	ENTER( "DlwPutMsg" );

	ASSERT( pAdapter && pControlBuffer && ControlLength );

	if ( pNdisPacket )
	{
		ASSERT( DataLength );
		pData = DigiAllocMemEx( NonPagedPool, DataLength, '7naL' );

		if ( !pData )
		{
			DigiDump( DIGIERRORS, ("***DlwPutMsg: Could not alloc data buffer!\n") );
			LEAVE( "DlwPutMsg", NDIS_STATUS_RESOURCES );
		}

		Status = DigiGetNdisPacketData( pData, pNdisPacket, DataLength, DataOffset );

		if ( !NT_SUCCESS( Status ) )
		{
			DigiDump( DIGIERRORS, ("***DlwPutMsg: Error in send NdisPacket!\n") );
			DigiFreeMem( pData );
			LEAVE( "DlwPutMsg", Status );
		}
	}

	Status = DsPutMsgEx( pAdapter->hStream, pControlBuffer, ControlLength,
	                     pData, DataLength, 
	                     pNdisPacket, DS_PRIORITY_LOW, DlwPutMsgComplete );

	if ( !NT_SUCCESS( Status ) )
	{
		// DsPutMsgEx only returns PENDING or ERROR, and when it returns ERROR, 
		// that means the Irp couldn't be sent and the completion routine won't
		// be called.  Free the buffer now.
		DigiDump( DIGIERRORS, ("***DlwPutMsg: DsPutMsgEx failed (0x%x)\n", Status ) );
		DigiFreeMem( pData );
	}

	LEAVE( "DlwPutMsg", Status );
}


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

Description:

	This is the completion routine for DStreams PutMsg ioctls sent down to the
	DStreams driver.  It handles freeing the memory allocated for the Irp, and 
	queuing up an NDIS SendComplete indication on the adapter context queue.

Parameters:

	hOwner
		Contains the address of the DIGILAN_LOGICAL_ADAPTER on which this request
		originated.

	Context
		Pointer to original NdisPacket.

	pControl
		Pointer to control portion of PutMsg.

	ControlLength
		Length of control portion.

	pData
		Pointer to data portion of PutMsg.

	DataLength
		Length of data block.  Not used.

	IoStatus
		IoStatus.Status returned from Sxb.

Return Value:

	None

--*/
{
	PDIGILAN_LOGICAL_ADAPTER pAdapter;

	DigiDump( DIGIFLOW, ("Enter DlwPutMsgComplete\n") );
	
	pAdapter = (PDIGILAN_LOGICAL_ADAPTER) hOwner;
	ASSERT( pAdapter->Type == DIGI_TYPE_ADAPTER_OBJECT );

	pAdapter->SyncProtocol.SendComplete( pAdapter,
	                                     pControl,
	                                     ControlLength,
	                                     (PVOID)Context,
	                                     IoStatus );

	// Free the buffers we alloc'd in Send routine
	if ( pData )
		DigiFreeMem( pData );
	DigiDump( DIGIFLOW, ("Leave DlwPutMsgComplete\n") );
}



NTSTATUS
DlwGetMsg(
	PDIGILAN_LOGICAL_ADAPTER pAdapter )
/*++

Description:

	This routine alloc's, initializes and sends a new GetMsg request down to 
	the DStreams provider driver.

Parameters:

	pAdapter
		Address of the adapter object.

Return Value:

	STATUS_SUCCESS to indicate the request was sent, or
	STATUS_INSUFFICIENT_RESOURCES if the request could not be allocated.

--*/
{
	PDIGILAN_FRAME_BUFFER pBuffer;	
	NTSTATUS              NtStatus = STATUS_SUCCESS;
	DS_GETINFO            Info;
	unsigned int          CntBuffer;


	DigiDump( DIGIFLOW, ("Enter DlwGetMsg\n") );

	//
	// Ask stream how many data buffers are already queued.
	// I then issue that many read requests.
	//
	NtStatus = DsGetInfo( pAdapter->hStream, &Info );
	if ( NT_ERROR( NtStatus ) ) goto Exit;

	// I will issue one requested read request and additional read requests for the queued buffers:
	CntBuffer = Info.CntQueued + 1;
	

	while ( CntBuffer-- )
	{
		//
		// Alloc data frame
		//
		
		pBuffer = DigiAllocMemEx( NonPagedPool, sizeof( DIGILAN_FRAME_BUFFER ), '8naL' );
		if ( !pBuffer )
		{
			DigiDump( (DIGIERRORS|DIGIINIT), 
					  ("*** DlwGetMsg: Could not alloc data buffer!\n") );
			NtStatus = STATUS_INSUFFICIENT_RESOURCES;
			break;
		}		
		else
		{
			// Send the request
			NtStatus = DsGetMsgEx( pAdapter->hStream,
								 pAdapter->SyncProtocol.RxControlLength,
								 pBuffer->Data, ETHERNET_MAX_FRAME_SIZE,
								 pBuffer, DS_PRIORITY_LOW, DlwGetMsgComplete );				

		}

		if ( NT_ERROR( NtStatus ) )
		{
			// DsGetMsgEx only returns PENDING or ERROR, and when it returns ERROR, 
			// that means the Irp couldn't be sent and the completion routine won't
			// be called.  Free the buffer now.  A PENDING return is not an ERROR.
			DigiDump( DIGIERRORS, ("*** DlwGetMsg: GetMsg request failed (0x%x)\n",NtStatus ) );
			
			if ( pBuffer ) DigiFreeMem( pBuffer );

			break;
		}
	}

Exit:
	DigiDump( DIGIFLOW, ("Leave DlwGetMsg, status 0x%x\n", NtStatus) );
	return( NtStatus );

}


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

Description:

	This routine is called when the DStreams provider completes a GetMsg 
	request, which is sent down by DlwGetMsg.

Parameters:

	hOwner
		Contains the address of the DIGILAN_LOGICAL_ADAPTER on which this request
		originated.

	Context
		Pointer to beginning of originally allocated data buffer.

	pControl
		Pointer to control portion of GetMsg.

	ControlLength
		Length of control portion.

	pData
		Pointer to start of data inside data buffer of GetMsg.

	DataLength
		Length of data, starting at pData.

	IOStatus
		IoStatus.Status returned from Sxb.

Return Value:

	None

--*/
{
	PDIGILAN_LOGICAL_ADAPTER pAdapter;
	DigiDump( DIGIFLOW, ("Enter DlwGetMsgComplete\n") );

	pAdapter = (PDIGILAN_LOGICAL_ADAPTER)hOwner;
	ASSERT( pAdapter->Type == DIGI_TYPE_ADAPTER_OBJECT );

	if ( IoStatus == STATUS_SUCCESS )
	{

		DigiDump( DIGIRX, 
				  ( "DigiLan GetMsg completed:\n"
				  "   pAdapter =  0x%x\n"
				  "   Line = 0x%x\n"
				  "   Length: %d (0x%x)\n",
				  pAdapter,
				  pAdapter->Snid,
				  DataLength,
				  DataLength ) );

		// Process received frame
		pAdapter->SyncProtocol.Receive( pAdapter,
										pControl,
										Context,
										DataLength );

		// Trigger new get message:
		{
			LARGE_INTEGER Timeout;
			Timeout.QuadPart = -10000;	// 1 ms timeout
			KeSetTimer( &pAdapter->GetMsgTimer, Timeout, &pAdapter->GetMsgDpc );
		}

	}
	else
	{
		//BUGBUG ? Even in error case we need to trigger GetMsg to prevent that we run dry

		DigiDump( DIGIERRORS, ("*** DlwGetMsgComplete: IoStatus 0x%x\n", IoStatus ) );
		DigiFreeMem( Context );
	}
	
	DigiDump( DIGIFLOW, ("Leave DlwGetMsgComplete\n") );
}


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

Description:

	This routine is called when a previous getmsg was unsuccessful and needed 
	to be rescheduled.

Parameters:

	DeferredContext
		This is a pointer to the Adapter object for this PVC.

--*/
{
	PDIGILAN_LOGICAL_ADAPTER pAdapter = DeferredContext;
	ASSERT( pAdapter->Type == DIGI_TYPE_ADAPTER_OBJECT );

	DlwGetMsg( pAdapter );
}


