/*++
*******************************************************************************
* Copyright (c) 1990-98 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:

   isdnrx.c

Abstract:

   Implementation of the receive (rx) engine.  This engine is the lowest layer of 
   code which performs all dxb GetMsg requests.  The DStreams API is driven from above,
	which means that all incoming (received) messages will not be presented until we ask
	for them by sending down a GetMsg request.

	The rx engine is responsible for sending GetMsg requests.  If a requests fails due
	to resource limitations, the engine must reschedule itself and retry again later.   
   
Author:

   Michael Zarns

--*/
#include "digiisdn.h"

STATIC VOID
RxEngineGetMsgComplete( 
		IN HANDLE   hOwner,
		IN HANDLE   Context,
		IN PVOID    pControl,
		IN ULONG    cbControl,
		IN PVOID    pData,
		IN ULONG    cbData,
		IN NTSTATUS IoStatus )
/*++

Description

	Completion routine defined by Dsapi, called when a previous GetMsg request has 
	completed.  Pass message up to owner, then reschedule RxEngine to send down another
	GetMsg request.  Always try to keep RXENGINE_GETMSG_COUNT requests outstanding.

	We never call DsGetMsg directly because calling back into Dxb recursively from the 
	GetMsg completion routine could blow the kernel	stack.  Always reschedule RxEngine
	timer and let it send the request down.

--*/
{
	PISDN_RXENGINE const pEngine = Context;
	ASSERT( pEngine->Type == ISDN_TYPE_RXENGINE );

	DigiDump( DIGIRXENGINE, ( "ISDN!RxEngineGetMsgComplete: hOwner 0x%x, Context 0x%x, IoStatus 0x%x\n", hOwner, Context, IoStatus ) );

	// Decrement count of pending GetMsg requests, since we're now completing one
	DigiAcquireSpinLock( &pEngine->Lock );
	ASSERT( pEngine->nGetMsgPending != 0 );
	pEngine->nGetMsgPending--;
	DigiReleaseSpinLock( &pEngine->Lock );
	
	// If error, just free the data and reschedule another getmsg
	if ( IoStatus != STATUS_SUCCESS )
	{
		if ( IoStatus == STATUS_BUFFER_TOO_SMALL )
		{
			// One specific error case - bigger packet came up than we expected.  Purge it.
			DigiDump( DIGIWARN, ( "Isdn!RxEngine: rx packet too big, purging it!\n" ) );
			DsPurge( pEngine->hStream, DS_PURGE_PUTMSG );
		}
		else if ( IoStatus != STATUS_CANCELLED )
		{
			// Don't warn if it was cancelled.  That's an expected response when the stream
			// is being Closed.	
			DigiDump( DIGIWARN, ( "ISDN!RxEngineGetMsgComplete: Warning, IoStatus 0x%x!\n", IoStatus ) );
		}

		RxEngineFreeData( pData );
		goto Exit;
	}

	pEngine->Interpreter( hOwner, pEngine->pDispatch, pControl, cbControl, pData, cbData );

Exit:

	{
		LARGE_INTEGER Delay = {0};
		(void)KeSetTimer( &pEngine->Timer, Delay, &pEngine->Dpc );
	}
}


STATIC VOID
RxEngine(
	IN PKDPC Ignored1, 
	IN PVOID hEngine, 
	IN PVOID Ignored2, 
	IN PVOID Ignored3 )
/*++

Description:

	The worker bee routine which sends getmsg requests.

--*/
{
	PISDN_RXENGINE pEngine = hEngine;

	ASSERT( pEngine->Type == ISDN_TYPE_RXENGINE );

	for (;;)
	{
		DigiAcquireSpinLock( &pEngine->Lock );
		if ( pEngine->fHalt )
		{
			DigiDump( DIGIRXENGINE, ( "ISDN!RxEngine: Halt detected, pEngine 0x%x, nGetMsgPending 0x%x\n", pEngine, pEngine->nGetMsgPending ) );
			if ( pEngine->nGetMsgPending == 0 )
				(void)KeSetEvent( &pEngine->Stopped, EVENT_INCREMENT, FALSE );
			DigiReleaseSpinLock( &pEngine->Lock );
			break;
		}

		ASSERT( pEngine->nGetMsgPending <= RXENGINE_GETMSG_COUNT );
		if ( pEngine->nGetMsgPending == RXENGINE_GETMSG_COUNT )
		{
			DigiReleaseSpinLock( &pEngine->Lock );
			break;
		}

		pEngine->nGetMsgPending++;
		DigiReleaseSpinLock( &pEngine->Lock );
		
		// Send down a GetMsg request
		{
			NTSTATUS Status = STATUS_UNSUCCESSFUL;
			PVOID pData = DigiAllocMem( NonPagedPool, pEngine->cbMaxData );
			DigiDump( DIGIRXENGINE, ( "ISDN!RxEngine: pEngine 0x%x, sending GetMsg\n", pEngine ) );
			if ( pData )
				Status = DsGetMsgEx( pEngine->hStream, pEngine->cbMaxControl, pData, 
				                     pEngine->cbMaxData, pEngine, DS_PRIORITY_LOW, 
				                     RxEngineGetMsgComplete );

			if ( !pData || NT_ERROR( Status ) )
			{
				// Out of system resources, bail out and retry later!
				if ( pData )
					DigiFreeMem( pData );
				DigiAcquireSpinLock( &pEngine->Lock );
				pEngine->nGetMsgPending--;
				DigiReleaseSpinLock( &pEngine->Lock );
				{
					LARGE_INTEGER Delay;
					Delay.QuadPart = ONE_MILLISECOND * 1000;
					(void)KeSetTimer( &pEngine->Timer, Delay, &pEngine->Dpc );
				}
				break;
			}
		}
	}
}


VOID
RxEngineStop( 
	IN PISDN_RXENGINE pEngine )
/*++

Description:

	Starts the engine, which immediately starts sending GetMsg requests.  Requires a 
	previously opened dxb stream.

--*/
{
	DigiDump( DIGIRXENGINE, ( "ISDN!RxEngineStop: pEngine 0x%x, hOwner 0x%x\n", pEngine, pEngine->hOwner ) );

	if ( pEngine->fStarted )
	{
		DigiAcquireSpinLock( &pEngine->Lock );
		pEngine->fHalt = TRUE;
		DigiReleaseSpinLock( &pEngine->Lock );

		(void)DsPurge( pEngine->hStream, DS_PURGE_GETMSG );

		// Force the rxengine to run now!
		{
			LARGE_INTEGER Delay = {0};
			(void)KeSetTimer( &pEngine->Timer, Delay, &pEngine->Dpc );
		}

		// Wait for rxengine to finish processing/freeing all outstanding GetMsg requests.
		(void)KeWaitForSingleObject( &pEngine->Stopped, Executive, KernelMode, FALSE, NULL );

        KeCancelTimer( &pEngine->Timer ); // DEJ Must cancel timer so that we don't die when it fires. 2/15/01
        pEngine->fStarted = FALSE;
	}
}


NTSTATUS
RxEngineStart(
	IN PISDN_RXENGINE       pEngine,
	IN HANDLE               hMiniport,
	IN HANDLE               hStream, 
	IN HANDLE               hOwner, 
	IN PISDN_EVENT_HANDLERS pDispatchTable, 
	IN PISDN_INTERPRETER    pInterpreter,
	IN ULONG                cbMaxControl, 
	IN ULONG                cbMaxData )
/*++

Description:

	Starts the engine, which immediately starts sending GetMsg requests.  Requires a 
	previously opened dxb stream.

Parameters

	pEngine
		Address of struct to store engine parameters.

	hMiniport
		Miniport handle passed into MiniportInitialize (IsdnInitialize).

	hStream
		Previously opened dxb stream (opened via DsOpen or DsOpenEx).

	hOwner
		Handle passed back into the dispatch routines when received messages arrive.

	pDispatchTable
		Table of function pointers to handlers for incoming ISDN messages and data.

	pInterpreter
		Routine that knows how to interpret incoming messages for this adapter.

	cbMaxControl
		Size of Control buffer to send down to dxb in the GetMsg request.  Specifying 0 
		causes no control buffer to be sent.

	cbMaxData
		Size of Data buffer to send down in the GetMsg request.  Specifying 0 causes no
		data buffer to be sent down.

--*/
{
	DigiDump( DIGIRXENGINE, ( "ISDN!RxEngineStart: pEngine 0x%x, hOwner 0x%x\n", pEngine, hOwner ) );

	NdisZeroMemory( pEngine, sizeof *pEngine );
	pEngine->Type         = ISDN_TYPE_RXENGINE;
	pEngine->hStream      = hStream;
	pEngine->hOwner       = hOwner;
	pEngine->pDispatch    = pDispatchTable;
	pEngine->Interpreter  = pInterpreter;
	pEngine->cbMaxControl = cbMaxControl;
	pEngine->cbMaxData    = cbMaxData;
	pEngine->fHalt        = FALSE;

	KeInitializeEvent( &pEngine->Stopped, SynchronizationEvent, FALSE );
	DigiInitializeSpinLock( &pEngine->Lock );
	KeInitializeDpc( &pEngine->Dpc, RxEngine, pEngine );
	KeInitializeTimer( &pEngine->Timer );
	pEngine->fStarted = TRUE;

	// Start the engine
	RxEngine( NULL, pEngine, NULL, NULL );
	return STATUS_SUCCESS;
}


VOID
RxEngineFreeData(
	IN PVOID pData )
/*++

Description:

	Routine used to free data packets allocated by the RxEngine.  RxEngineGetMsgComplete
	passes the pData buffer into Interpreter.  Interpreter is responsible for freeing 
	the buffer by calling RxEngineFreeData.

--*/
{
	DigiFreeMem( pData );
}
