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

   dxbdpc.h

Abstract:

   Common routines used by both command and event queue dpc's.

Author:

   Michael Zarns

--*/
#ifndef DXBDPC_H
#define DXBDPC_H


__inline BOOLEAN
dxbDpcSetInProgress( 
	PDXB_DEVICE_EXTENSION pExt,
	PDXB_DPC              pDxbDpc )
{
	BOOLEAN Success = FALSE;

	DigiAcquireSpinLock( &pExt->DxbLock );
	if ( !pDxbDpc->InProgress )
	{
		pDxbDpc->InProgress = TRUE;
		Success = TRUE;
	}
	DigiReleaseSpinLock( &pExt->DxbLock );

	return Success;
}


__inline VOID
dxbDpcSetNotInProgress( 
	PDXB_DEVICE_EXTENSION pExt,
	PDXB_DPC              pDxbDpc )
{
	DigiAcquireSpinLock( &pExt->DxbLock );
	ASSERT( pDxbDpc->InProgress );
	pDxbDpc->InProgress = FALSE;
	DigiReleaseSpinLock( &pExt->DxbLock );
}


__inline VOID
dxbDpcInitializeTimer( 
	PDXB_DEVICE_EXTENSION pExt,
	PDXB_DPC              pDxbDpc,
	PKDEFERRED_ROUTINE    pRoutine )
{
	KeInitializeTimer( &pDxbDpc->Timer );
	KeInitializeDpc( &pDxbDpc->Dpc, pRoutine, pExt );
}


#define DXB_DPC_TIMER_PERIOD			10
#define DXB_DPC_TIMER_START_DELAY	((DXB_DPC_TIMER_PERIOD) * (ONE_MILLISECOND))
#define DXB_DPC_TIMER_STOP_TIMEOUT	(10 * (DXB_DPC_TIMER_PERIOD) * (ONE_MILLISECOND))


__inline VOID
dxbDpcStartTimer(
	PDXB_DPC pDxbDpc )
{
	LARGE_INTEGER Delay;
	pDxbDpc->StopDpc = FALSE;

	Delay.QuadPart = DXB_DPC_TIMER_START_DELAY;
	(void)KeSetTimerEx( &pDxbDpc->Timer, Delay, DXB_DPC_TIMER_PERIOD, &pDxbDpc->Dpc );
}


__inline VOID
dxbDpcStopTimer(
	PDXB_DPC pDxbDpc )
{
	pDxbDpc->StopDpc = TRUE;
	(void)KeCancelTimer( &pDxbDpc->Timer );
	(void)KeRemoveQueueDpc( &pDxbDpc->Dpc );

	{
		// Without a complex notification scheme between here and the DPC, we have no
		// good way to verify that the DPC really has stopped executing.  Use the brute
		// force method of simply waiting a "long" time, where a "long" time is some
		// multiple of the timer interval.
		KEVENT ForceDelay;
		LARGE_INTEGER Timeout;
		KeInitializeEvent( &ForceDelay, NotificationEvent, FALSE );
		Timeout.QuadPart = DXB_DPC_TIMER_STOP_TIMEOUT;

		ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );
		(void)KeWaitForSingleObject( &ForceDelay, Executive, KernelMode, FALSE, &Timeout );
	}
}

#endif
