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

   DsDef.h

Abstract:

   This header file contains private data structures, types, and prototypes 
   which are used only within this library.

Author:

   Michael Zarns

--*/

#ifndef _DSDEF_
#define _DSDEF_

///////////////////////////////////////////////////////////////////////////////
//
// Typedefs
//

// The DSL_OBJECT_TYPE enum is used as the first field of every alloc'd struct in this
// library.

typedef enum DSL_OBJECT_TYPE
{
	DSL_TYPE_DEVICE = 0xdddd0000,	// Start high to not conflict with driver type values
	DSL_TYPE_STREAM,
	DSL_TYPE_OPEN,
	DSL_TYPE_CLOSE,
	DSL_TYPE_PUTMSG,
	DSL_TYPE_GETMSG,
	DSL_TYPE_MAX
} DSL_OBJECT_TYPE, *PDSL_OBJECT_TYPE;


// The DSL_OBJECT_STATE enum is used for the State field of Device and Stream objects.  
typedef enum DSL_OBJECT_STATE
{
	DSL_STATE_INITIALIZED,
	DSL_STATE_OPENING,
	DSL_STATE_READY,
	DSL_STATE_CLOSING,
	DSL_STATE_CLOSED,
	DSL_STATE_MAX
} DSL_OBJECT_STATE, *PDSL_OBJECT_STATE;


// DSL_DEVICE is a wrapper for an NT device object providing a  "connection" to a
// DSTREAMS service provider (eg. /Device/DigiSxb2) opened  by a client driver. It
// contains information that must be maintained until  the connection is closed. A new
// one is created for each successful DsOpenDevice call.  It is added to the global
// gDeviceList.
typedef struct DSL_DEVICE 
{
	DSL_OBJECT_TYPE  Type;
	DSL_OBJECT_STATE State;
	LIST_ENTRY       ListEntry;
	PFILE_OBJECT     FileObject;
	PDEVICE_OBJECT   DeviceObject;
	BOOLEAN          bOpenedInternal;
} DSL_DEVICE, *PDSL_DEVICE;


// DSL_STREAM is a wrapper for an open Stream. It contains information that must be 
// maintained while the Stream is open. A new one is created for each successful DsOpen
// call.  It is added to the StreamsList maintained in the device wrapper.
typedef struct DSL_STREAM 
{
	DSL_OBJECT_TYPE  Type;
	LONG             nReferences;		// Protected by DSL_DEVICE.Lock
	DSL_OBJECT_STATE State;				// Protected by DSL_STREAM.Lock
	LIST_ENTRY       ListEntry;		// Protected by DSL_DEVICE.Lock
	KSPIN_LOCK       Lock;
	HANDLE           hOwner;
	HANDLE           hProviderStream;
	PDSL_DEVICE      pDevice;
} DSL_STREAM, *PDSL_STREAM;


// DSL_LIST is used for the global device and stream lists
typedef struct DSL_LIST
{
	LIST_ENTRY Head;
	KSPIN_LOCK Lock;
} DSL_LIST, *PDSL_LIST;


///////////////////////////////////////////////////////////////////////////////
//
// Global Data
//

extern DSL_LIST gDeviceList;
extern DSL_LIST gStreamList;


///////////////////////////////////////////////////////////////////////////////
//
// Inlined routines
//
#if defined(INLINE)
#undef INLINE
#endif
#define INLINE _inline

INLINE NTSTATUS
dsReferenceStream(
	HANDLE hStream,
	BOOLEAN IsClosing )
/*++

Description:

	This routine must be called by every API routine before attempting to 
	access the Stream object.  It increments the reference count for this stream
	ensuring that the stream won't close while another request is still pending.

Parameters:

	hStream
		Handle for the stream.

	IsClosing
		Flag indicating whether the Stream's State should be changed to CLOSING.
		This would be set only by the DsClose and DsCloseEx api routines.

Return Value:

	STATUS_SUCCESS,
	STATUS_UNSUCCESSFUL

--*/
{
	KIRQL OldIrql;
	PLIST_ENTRY pEntry;
	PDSL_STREAM pStream;


	// Walk Stream list to find stream.
	KeAcquireSpinLock( &gStreamList.Lock, &OldIrql );
	for ( pEntry = gStreamList.Head.Flink, pStream = NULL; 
	      pEntry != &gStreamList.Head; 
	      pEntry = pEntry->Flink )
	{
		if ( CONTAINING_RECORD( pEntry, DSL_STREAM, ListEntry ) == hStream )
		{
			// Found the Stream on the list!
			pStream = hStream;
			pStream->nReferences++;
			break;
		}
	}
	KeReleaseSpinLock( &gStreamList.Lock, OldIrql );

	// Found it?
	if ( !pStream )
		// Nope!
		return( STATUS_UNSUCCESSFUL );

	// Fail the request if stream is *NOT* in the READY state
	KeAcquireSpinLock( &pStream->Lock, &OldIrql );
	if ( pStream->State != DSL_STATE_READY && pStream->State != DSL_STATE_OPENING )
	{
		// Must fail this request
		ASSERT( pStream->State == DSL_STATE_CLOSING || 
		        pStream->State == DSL_STATE_CLOSED );
		KeAcquireSpinLockAtDpcLevel( &gStreamList.Lock );
		pStream->nReferences--;
		if ( pStream->State == DSL_STATE_CLOSED && !pStream->nReferences )
		{
			// CloseComplete already occurred but couldn't free the stream because
			// we still had a reference to it.  Free the stream.
			RemoveEntryList( &pStream->ListEntry );
			KeReleaseSpinLockFromDpcLevel( &gStreamList.Lock );
			KeReleaseSpinLock( &pStream->Lock, OldIrql );
			DigiFreeMem( pStream );
		}
		else
		{
			// CloseComplete will come around later and free the stream.
			ASSERT( pStream->State == DSL_STATE_CLOSING );
			KeReleaseSpinLockFromDpcLevel( &gStreamList.Lock );
			KeReleaseSpinLock( &pStream->Lock, OldIrql );
		}
		return( STATUS_UNSUCCESSFUL );
	}
	if ( IsClosing )
		pStream->State = DSL_STATE_CLOSING;
	KeReleaseSpinLock( &pStream->Lock, OldIrql );
	return( STATUS_SUCCESS );
}



INLINE VOID
dsDereferenceStream(
	PDSL_STREAM pStream,
	BOOLEAN SetClosed )
/*++

Description:

	This routine must be called by completion routines for Async APIs and before
	returning from sync APIs to decrement the reference count for this stream.
	If the stream is closed and this is the last reference to the stream, it is
	also removed from the streams list and freed.

Parameters:

	pStream
		Points to the stream object.

	SetClosed
		Indicates whether to change the state of the stream to CLOSED.  This would
		only be set by DsClose or dsCloseComplete routines.

Return Value:

	None

--*/
{
	KIRQL OldIrql;

	KeAcquireSpinLock( &pStream->Lock, &OldIrql );
	if ( SetClosed )
	{
		ASSERT( pStream->State == DSL_STATE_CLOSING || 
		        pStream->State == DSL_STATE_OPENING );
		pStream->State = DSL_STATE_CLOSED;
	}

	// Decrement reference count
	KeAcquireSpinLockAtDpcLevel( &gStreamList.Lock );
	pStream->nReferences--;
	ASSERT( pStream->nReferences >= 0 );

	// Free the stream if closing and this was the last reference to it.
	if ( pStream->State == DSL_STATE_CLOSED && !pStream->nReferences )
	{
		RemoveEntryList( &pStream->ListEntry );
		KeReleaseSpinLockFromDpcLevel( &gStreamList.Lock );
		KeReleaseSpinLock( &pStream->Lock, OldIrql );
		DigiFreeMem( pStream );
	}
	else
	{
		KeReleaseSpinLockFromDpcLevel( &gStreamList.Lock );
		KeReleaseSpinLock( &pStream->Lock, OldIrql );
	}
}

#endif // _DSDEF_
