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

   sohcmplt.c

Abstract:

   This module is the IRP completer thread. It completes IRPs processed by the
   DSTREAMS subsystem.

Author:

   Ron Gredvig

Revision History:

   $History: sohcmplt.c $
 * 
 * *****************  Version 4  *****************
 * User: Ron          Date: 10/09/97   Time: 9:55a
 * Updated in $/Components/Windows/NT/Sync/Build/DigiSoh/Src
 * Periodic check-in to Prepare for Sandy. Functional with external
 * loopback on 570.
 *
 * *****************  Version 3  *****************
 * User: Ron          Date: 4/11/97    Time: 5:42p
 * Updated in $/Components/Windows/NT/Sync/Build/DigiSoh/Src
 * Weekly check-in.
 *
 * *****************  Version 2  *****************
 * User: Ron          Date: 4/05/97    Time: 5:04p
 * Updated in $/Components/Windows/NT/Sync/Build/DigiSoh/Src
 * Update to work with digiutil library.
 *
 * *****************  Version 1  *****************
 * User: Ron          Date: 3/27/97    Time: 5:04p
 * Created in $/Components/Windows/NT/Sync/Build/DigiSoh/Src
 * Initial check in of DigiSOH (STREAMS On Host) driver components.

--*/

#include "digisoh.h"
#include "sohcmplt.h"
#include "sohdstrm.h"

#include "digilib.h"

//#ifndef _DSIRPCMP_DOT_C
//#define _DSIRPCMP_DOT_C
//static char RCSInfo_DsIrpcmpDotC[] = "$Header: /Components/Windows/NT/Sync/Build/DigiSoh/Src/sohcmplt.c 4     10/09/97 9:55a Ron $";

//#endif

///////////////////////////////////////////////////////////////////////////////
//
// Private Routine Prototypes
//


///////////////////////////////////////////////////////////////////////////////
//
// Exported Routines
//



VOID
SohIrpCompleter( IN PVOID pStartContext )
/*++

Routine Description:

This routine is a thread to complete STREAMS requests.

Arguments:

pStartContext - a pointer to the Device Object

Return Value:

None.

Comments:

This is a continuous loop. It monitors Stream Objects for IRPs that are ready for completion.


??? Is there a more effcient way for this loop to run ?

	KeDelayExecutionThread ?

	make event driven (KeWaitForXXX) ?

--*/
{
	PSOH_RUNTIME_ENV	pSohRunTimeEnv;
	PSOH_STREAM_OBJECT	pStreamObj;
	PIRP				pIrp;
	ULONG_PTR		iStream;
	PLIST_ENTRY			pListEntry;
	ULONG				Count;

	DigiDump( DIGIFLOW, ( "DigiSoh: Entering SohIrpCompleter\n" ) );

	// initialize local variables
	pSohRunTimeEnv = pStartContext;

	// while ( TRUE /* not canceled */  )
	while ( !gSohRuntimeEnv.Unloading )
	{

		KIRQL	OldIrqL;

		// the STREAMS thread will set events when there is work to do
		KeWaitForSingleObject( &pSohRunTimeEnv->evCompleter,
							   Executive,
							   KernelMode,
							   TRUE,
							   NULL );

		KeRaiseIrql (DISPATCH_LEVEL, &OldIrqL);

		// walk through the active list once
		for ( Count = 0; Count < gSohRuntimeEnv.StreamObjectCount; Count++ )
		{
			// get a Stream object to process

			pListEntry = ExInterlockedRemoveHeadList( 
											   &pSohRunTimeEnv->StreamObjectListHead,
											   &pSohRunTimeEnv->SpinLock );

			if ( pListEntry == 0 )
			{
				continue;		   // nothing on the list
			}

			pStreamObj = CONTAINING_RECORD( pListEntry,
											SOH_STREAM_OBJECT,
											ListEntry );


			// Keeps the stream from processing while we are in cleanup
			KeAcquireSpinLockAtDpcLevel( &pStreamObj->SpinLock );

			if (pStreamObj->CancelState == SOH_PROCESS_STATE_CANCEL_PENDING)
			{

				ExInterlockedInsertTailList( &pSohRunTimeEnv->StreamObjectListHead,
											 &pStreamObj->ListEntry,
											 &pSohRunTimeEnv->SpinLock );

				KeReleaseSpinLockFromDpcLevel( &pStreamObj->SpinLock );

				continue;
			}

			switch ( pStreamObj->State )
			{
			case SOH_STREAM_STATE_OPEN:

				KeAcquireSpinLockAtDpcLevel( &pSohRunTimeEnv->SpinLock );

				pStreamObj->State = SOH_STREAM_STATE_ACTIVE;

				KeReleaseSpinLockFromDpcLevel( &pSohRunTimeEnv->SpinLock );

				SohIoCompleteRequest( pStreamObj->pOpenIrp, IO_NO_INCREMENT );

				pStreamObj->pOpenIrp = NULL;

				break;

			case SOH_STREAM_STATE_OPENFAILED:

				KeAcquireSpinLockAtDpcLevel( &pSohRunTimeEnv->SpinLock );

				// rm from StreamObjectList
				iStream = (ULONG_PTR)pStreamObj->hObjectId;
				gapStreamObj[iStream] = ( PSOH_STREAM_OBJECT ) NULL;	// Relinquish handle
				pSohRunTimeEnv->StreamObjectCount--;

				pIrp = pStreamObj->pOpenIrp;

				DigiFreeMem( pStreamObj );	// Destroy StreamObject

				pStreamObj = NULL;

				KeReleaseSpinLockFromDpcLevel( &pSohRunTimeEnv->SpinLock );

				SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

				break;

			case SOH_STREAM_STATE_ACTIVE:

				if ( pStreamObj->IoctlState == IoctlCompleteState )	// completes
					// synchronously
				{
					pIrp = pStreamObj->pIoctlIrp;
					pStreamObj->pIoctlIrp = NULL;
					pStreamObj->IoctlState = SOH_STREAM_STATE_INACTIVE;

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				if ( pStreamObj->PushState == PushCompleteState )	// completes
					// synchronously
				{
					pIrp = pStreamObj->pPushIrp;
					pStreamObj->pPushIrp = NULL;
					pStreamObj->PushState = SOH_STREAM_STATE_INACTIVE;

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				if ( pStreamObj->LinkState == LinkCompleteState )	// completes
					// synchronously
				{
					pIrp = pStreamObj->pLinkIrp;
					pStreamObj->pLinkIrp = NULL;
					pStreamObj->LinkState = SOH_STREAM_STATE_INACTIVE;

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				while ( pListEntry = ExInterlockedRemoveHeadList( 
										 &pStreamObj->CompletedGetmsgListHead,
											&pSohRunTimeEnv->SpinLock ) )
				{
					// Remove IRP from list

					pIrp = CONTAINING_RECORD( pListEntry,
											  IRP,
											  Tail.Overlay.ListEntry );

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				while ( pListEntry = ExInterlockedRemoveHeadList( 
										 &pStreamObj->CompletedPutmsgListHead,
											&pSohRunTimeEnv->SpinLock ) )
				{
					// Remove IRP from list

					pIrp = CONTAINING_RECORD( pListEntry,
											  IRP,
											  Tail.Overlay.ListEntry );

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				break;

			case SOH_STREAM_STATE_CLOSED:

				if ( pStreamObj->pOpenIrp )	// completes
					// synchronously
				{
					pIrp = pStreamObj->pOpenIrp;
					pStreamObj->pOpenIrp = NULL;

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				if ( pStreamObj->IoctlState == IoctlCompleteState )	// completes
					// synchronously
				{
					pIrp = pStreamObj->pIoctlIrp;
					pStreamObj->pIoctlIrp = NULL;
					pStreamObj->IoctlState = SOH_STREAM_STATE_INACTIVE;

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				if ( pStreamObj->PushState == PushCompleteState )	// completes
					// synchronously
				{
					pIrp = pStreamObj->pPushIrp;
					pStreamObj->pPushIrp = NULL;
					pStreamObj->PushState = SOH_STREAM_STATE_INACTIVE;

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				if ( pStreamObj->LinkState == LinkCompleteState )	// completes
					// synchronously
				{
					pIrp = pStreamObj->pLinkIrp;
					pStreamObj->pLinkIrp = NULL;
					pStreamObj->LinkState = SOH_STREAM_STATE_INACTIVE;

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				while ( pListEntry = ExInterlockedRemoveHeadList( 
										 &pStreamObj->CompletedGetmsgListHead,
											&pSohRunTimeEnv->SpinLock ) )
				{
					// Remove IRP from list

					pIrp = CONTAINING_RECORD( pListEntry,
											  IRP,
											  Tail.Overlay.ListEntry );

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				while ( pListEntry = ExInterlockedRemoveHeadList( 
										 &pStreamObj->CompletedPutmsgListHead,
											&pSohRunTimeEnv->SpinLock ) )
				{
					// Remove IRP from list

					pIrp = CONTAINING_RECORD( pListEntry,
											  IRP,
											  Tail.Overlay.ListEntry );

					SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );
				}

				KeAcquireSpinLockAtDpcLevel( &pSohRunTimeEnv->SpinLock );

				// rm from StreamObjectList
				iStream = (ULONG_PTR)pStreamObj->hObjectId;
				gapStreamObj[iStream] = ( PSOH_STREAM_OBJECT ) NULL;	// Relinquish handle
				gSohRuntimeEnv.StreamObjectCount--;

				KeReleaseSpinLockFromDpcLevel( &pSohRunTimeEnv->SpinLock );

				pIrp = pStreamObj->pCloseIrp;

				KeReleaseSpinLockFromDpcLevel( &pStreamObj->SpinLock );

				DigiFreeMem( pStreamObj );	// Destroy StreamObject

				pStreamObj = NULL;

				SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

				break;

			case SOH_STREAM_STATE_CLOSEFAILED:

				SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

				break;
			}

			// return Stream object to list if it still exists

			if( pStreamObj )
			{

				ExInterlockedInsertTailList( &pSohRunTimeEnv->StreamObjectListHead,
											 &pStreamObj->ListEntry,
											 &gSohRuntimeEnv.SpinLock );

				KeReleaseSpinLockFromDpcLevel( &pStreamObj->SpinLock );
			}

		}

		KeLowerIrql (OldIrqL);

	}

	DigiDump( DIGIFLOW, ( "DigiSoh: Leaving SohIrpCompleter\n" ) );

	PsTerminateSystemThread( STATUS_SUCCESS );

	return;
}


VOID
SohIoCompleteRequest( IN PIRP pIrp, IN CCHAR PriorityBoost )
/*++

Routine Description:

SohIoCompleteRequest indicates the caller has completed all processing for a
given I/O request and is returning the given IRP to the I/O Manager.


Arguments:

pIrp - a pointer to the I/O Rrequest Packet

PriorityBoost - Specifies a system-defined constant by which to increment the
runtime priority of the original thread that requested the operation. This
value is IO_NO_INCREMENT if the original thread requested an operation the
driver could complete quickly (so the requesting thread is not compensated
for its assumed wait on I/O) or if the IRP is completed with an error.
Otherwise, the set of PriorityBoost constants are device-type-specific. See
ntddk.h for these constants.


Return Value:

None.

Comments:

When the driver has finished all processing for a given IRP, it calls
SohIoCompleteRequest. SohIoCompleteRequest calls DigiUnlockBuffers if the IRP
originated from user mode. Then IoConpleteRequest is called.

Callers of SohIoCompleteRequest must be running at IRQL <= DISPATCH_LEVEL.

--*/
{


	DigiDump( DIGIVERYLOUD, ( "SohIoCompleteRequest enter, Irp = 0x%x \n", pIrp ) );

	ASSERT( pIrp );

	if ( pIrp->RequestorMode == UserMode )
	{
		DigiUnlockBuffers( pIrp );
	}

	// Complete the Request.  We need to rais the IRQ Level to dispatch level
	// to act like other Digi drivers that sit below Lan NT drivers.  During
	// driver entry upper level drivers are running at passive level.  If we
	// run at our current level the upper layer drivers run before us.  When
	// we pass back error conditions the upper level drivers can unload, thus
	// we will blue screen when we try to return from the IoCompleteRequest,
	// because we are returning from a function in the upper layer driver that is
	// no unloaded (BUG CHECK 00000050).

	DigiDump( DIGIFLOW, ( "SohIoCompleteRequest exit, Irp = 0x%x \n", pIrp ) );

	IoCompleteRequest( pIrp, PriorityBoost );

}
