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

   sohalloc.c

Abstract:

   This module contains functions to allocate and manage STREAMS components.

Author:

   Ron Gredvig

Revision History:

   $History: sohalloc.c $
 * 
 * *****************  Version 1  *****************
 * User: Ron          Date: 10/09/97   Time: 11:21a
 * Created in $/Components/Windows/NT/Sync/Build/DigiSoh/Src
 * New files for DigiSoh. Checked in to prepare for Sandy. Functional with
 * external loopback on 570.

--*/

#include "digisoh.h"

#include "types.h"
#include "stream.h"
#include "timer.h"
#include "queue.h"
#include "cnt_arry.h"

#include "sohmsg.h"
#include "sohreg.h"
#include "sohalloc.h"
#include "sohapi.h"
#include "param.h"
#include "digilib.h"

#if !NT5_BUILD
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,SohAllocInitTable)
#pragma alloc_text(INIT,SohAllocInit)
#pragma alloc_text(INIT,SohLogComponentUsage)
#pragma alloc_text(INIT,SohLogDataBlockUsage)
#pragma alloc_text(INIT,SohReadMsgBlkSizes)
#endif
#endif

// Local strucures

typedef struct SohTimeOutArgs
{
	struct SohTimeOutArgs *Next;
	PFI             pFunc;
	caddr_t         Arg;
	time_t          Time;
	int             TimeoutId;
} SOHTIMEOUTARGS, *PSOHTIMEOUTARGS;


typedef struct SohBufCallArgs
{
	struct SohBufCallArgs *Next;
	int             Size;
	int             Priority;
	PFI             pFunc;
	caddr_t         Arg;
	BOOLEAN			bFreeMemory;  // The strucute is not part of the minimum allocation


} bufcall_args_t, SOHBUFCALLARGS, *PSOHBUFCALLARGS;

typedef struct SohClassInfo
{
	uint16           Min;
	uint16           Free;
	uint16           Used;
	uint16           Max;
	union
	{
		dblk_t         *DataBs;
		mblk_t         *MsgBs;
		queue_t        *QPairs;
		strm_t         *Streams;
		SOHBUFCALLARGS *BufCallArgs;
		SOHTIMEOUTARGS *TimeOutArgs;
	}               pFree;
	uint32           DataBSize;
	uint32           AllocSize;
	uint32           Stop[NPRI];
	uint32           Fail;
	uint32           Hwm;
	union
	{
		SOHBUFCALLARGS *BufCallArgs;
		SOHTIMEOUTARGS *TimeOutArgs;
	}               pRun;
	PVOID			pMimimumMemoryAllocation;
} class_info_t, CLASSINFO, *PCLASSINFO;

   
// Local Global Data

CLASSINFO       gInfoMsgB;
CLASSINFO       gaInfoDataB[SOH_NDBCLASSES];
CLASSINFO       gInfoQPair;
CLASSINFO       gInfoStream;
CLASSINFO       gInfoBufCall;
CLASSINFO       gInfoTimeOut;

//
// gaDBClass is used for fast conversion of size to class. 
//

unchar          gaDBClass[SOH_DBSIZE_MAX];

unsigned gCalling;
unsigned gCalltime;
int gTimeoutId;
ulong gNTHZ;

#define N100NS_PER_SEC 10000000


///////////////////////////////////////////////////////////////////////////////
//
// Exported functions
//


void
SohAllocInitTable( PSOHALLOCTABLE pAlloc,
				   ULONG ulPortCount,
				   HANDLE hParametersKey,
				   PDRIVER_OBJECT pDriverObject)
/*++

Description:

	This routine determines the needed resources for STREAMS components,
	and initializes the allocation table.  The needed resources are calculated
	using the input number of streams, however, any parameter can be overridden
	with a registry entry.

	Note***

		FUTURE ENHANCEMENTS
			1. call MmQuerySystemSize/MmIsThisAnNtAsSystem to adjust limits.
			2. Use Lookaside lists and zones to avoid nonpaged memory pool 
			   fragmentation.  Lists like these are nice to use, but they
			   are usually missing something that you might wish to implemment
			   (e.g. High water marks for the list).

Parameters:

	pAlloc - a pointer to an allocation table

Return Value:

	NT_STATUS_SUCCESS for success.
	Non-zero for failure.

--*/
{
	SHORT           Class;
	SHORT           Size;
	DATABLK_SIZES	DataBlkSizes[SOH_NDBCLASSES];

	////////////////////////////////////////////////////////////////////////////////
	// Restrict the number of ports to the memory size.
	////////////////////////////////////////////////////////////////////////////////
    switch (MmQuerySystemSize()) {

        case MmLargeSystem:

			ulPortCount = (ulPortCount > 16) ? 16 : ulPortCount;
			break;	

        case MmMediumSystem:

			ulPortCount = (ulPortCount > 8) ? 8 : ulPortCount;
			break;

        case MmSmallSystem:
		default:

			ulPortCount = (ulPortCount > 4) ? 4 : ulPortCount;
			break;

    }

	////////////////////////////////////////////////////////////////////////////////
	// Set up the Min and Max Queue Pairs
	////////////////////////////////////////////////////////////////////////////////
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_QPAIRS_PER_PORT,
							 &pAlloc->QPairsMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_QPAIRS_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_QPAIRS_PER_PORT,
							 &pAlloc->QPairsMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_QPAIRS_PER_PORT_DEFAULT );

	// Some Sanity checks
	if (pAlloc->QPairsMin < 0)
	{
		pAlloc->QPairsMin = DIGISOH_REGVAL_MIN_QPAIRS_PER_PORT_DEFAULT;
	}
	if (pAlloc->QPairsMax < pAlloc->QPairsMin )
	{
		pAlloc->QPairsMin = DIGISOH_REGVAL_MIN_QPAIRS_PER_PORT_DEFAULT;
		pAlloc->QPairsMax = DIGISOH_REGVAL_MAX_QPAIRS_PER_PORT_DEFAULT;
	}

	// Now multiply by the number of ports
	pAlloc->QPairsMin *= ulPortCount;
	pAlloc->QPairsMax *= ulPortCount;

	////////////////////////////////////////////////////////////////////////////////
	// Set up the Min and Max Stream Control Structures
	////////////////////////////////////////////////////////////////////////////////

	// The Minimum streams equals the max
	pAlloc->StreamsMin = pAlloc->StreamsMax  = gulMaxStreams;

	////////////////////////////////////////////////////////////////////////////////
	// Set up the Min and Max Message Blocks
	////////////////////////////////////////////////////////////////////////////////
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_MSGBLOCKS_PER_PORT,
							 &pAlloc->MsgBsMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_MSGBLOCKS_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_MSGBLOCKS_PER_PORT,
							 &pAlloc->MsgBsMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_MSGBLOCKS_PER_PORT_DEFAULT );

	// Some Sanity checks
	if (pAlloc->MsgBsMin < 0)
	{
		pAlloc->MsgBsMin = DIGISOH_REGVAL_MIN_MSGBLOCKS_PER_PORT_DEFAULT;
	}
	if (pAlloc->MsgBsMax < pAlloc->MsgBsMin )
	{
		pAlloc->MsgBsMin = DIGISOH_REGVAL_MIN_MSGBLOCKS_PER_PORT_DEFAULT;
		pAlloc->MsgBsMax = DIGISOH_REGVAL_MAX_MSGBLOCKS_PER_PORT_DEFAULT;
	}

	// Now multiply by the number of ports
	pAlloc->MsgBsMin *= ulPortCount;
	pAlloc->MsgBsMax *= ulPortCount;

	////////////////////////////////////////////////////////////////////////////////
	// Set up the Min and Max Data Blocks for the various classes
	////////////////////////////////////////////////////////////////////////////////

	// Go read the data block sizes from the registry
	SohReadMsgBlkSizes(DataBlkSizes, hParametersKey);
	
	for ( Class = 0, Size = SOH_DBSIZE_MIN;
		  Class < SOH_NDBCLASSES;
		  Class++, Size *= 2 )
	{

		pAlloc->aDBAlloc[Class].DataBsMin = DataBlkSizes[Class].ulMin * ulPortCount;
		pAlloc->aDBAlloc[Class].DataBsMax = DataBlkSizes[Class].ulMax * ulPortCount;
		pAlloc->aDBAlloc[Class].Size = Size;
	}

	////////////////////////////////////////////////////////////////////////////////
	// Set up Block Priorities
	////////////////////////////////////////////////////////////////////////////////
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MSGLIMIT_LOPRI,
							 &pAlloc->LoPriMsgLimit,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MSGLIMIT_LOPRI_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MSGLIMIT_MEDPRI,
							 &pAlloc->MedPriMsgLimit,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MSGLIMIT_MEDPRI_DEFAULT );

	// Some Sanity checks
	if ((pAlloc->MedPriMsgLimit < 0) || (pAlloc->MedPriMsgLimit > 100))
	{
		pAlloc->MedPriMsgLimit = DIGISOH_REGVAL_MSGLIMIT_MEDPRI_DEFAULT;
	}
	if ((pAlloc->LoPriMsgLimit <= pAlloc->MedPriMsgLimit)  || (pAlloc->LoPriMsgLimit > 100))
	{
		pAlloc->MedPriMsgLimit	= DIGISOH_REGVAL_MSGLIMIT_MEDPRI_DEFAULT;
		pAlloc->LoPriMsgLimit	= DIGISOH_REGVAL_MSGLIMIT_LOPRI_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// Set up the Min and Max Number of Timeout Structures
	////////////////////////////////////////////////////////////////////////////////
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_BUFCALLS_PER_PORT,
							 &pAlloc->BufCallMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_BUFCALLS_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_BUFCALLS_PER_PORT,
							 &pAlloc->BufCallMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_BUFCALLS_PER_PORT_DEFAULT );

	// Some Sanity checks
	if (pAlloc->BufCallMin < 0)
	{
		pAlloc->BufCallMin = DIGISOH_REGVAL_MIN_BUFCALLS_PER_PORT_DEFAULT;
	}
	if (pAlloc->BufCallMax < pAlloc->BufCallMin )
	{
		pAlloc->BufCallMin = DIGISOH_REGVAL_MIN_BUFCALLS_PER_PORT_DEFAULT;
		pAlloc->BufCallMax = DIGISOH_REGVAL_MAX_BUFCALLS_PER_PORT_DEFAULT;
	}

	// Now multiply by the number of ports
	pAlloc->BufCallMin *= ulPortCount;
	pAlloc->BufCallMax *= ulPortCount;

	////////////////////////////////////////////////////////////////////////////////
	// Set up the Min and Max Number of Timeout Structures
	////////////////////////////////////////////////////////////////////////////////
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_TIMEOUTS_PER_PORT,
							 &pAlloc->TimeoutMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_TIMEOUTS_PER_PORT_DEFAULT );

	// Some Sanity checks
	if (pAlloc->TimeoutMax < 0)
	{
		pAlloc->TimeoutMax = DIGISOH_REGVAL_MAX_TIMEOUTS_PER_PORT_DEFAULT;
	}

	// Now multiply by the number of ports
	pAlloc->TimeoutMax *= ulPortCount;

#if DBG
	// Log the Component resources	
	SohLogComponentUsage( pAlloc, pDriverObject );

	// Log the Data Block resources	
	SohLogDataBlockUsage( pAlloc, pDriverObject );
#endif // #if DBG

}


VOID 
SohReadMsgBlkSizes(
	IN DATABLK_SIZES	*pDataBlkSizesArray, 
	IN HANDLE			hParametersKey
	)
/*++

Routine Description:

    Read the Data Block Min and Max allocation numbers from the registry.

Arguments:

	pDataBlkSizes
		an array of maxmimum data block sizes.
	hParametersKey
		the Parameters Key of the Registry.

Return Value:

    None.

--*/
{

	int				Class = 0;
	DATABLK_SIZES	*pDataBlkSizes;

	////////////////////////////////////////////////////////////////////////////////
	// 16 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes = pDataBlkSizesArray;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA16_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA16_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA16_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA16_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA16_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA16_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA16_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 32 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA32_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA32_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA32_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA32_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA32_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA32_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA32_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 64 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA64_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA64_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA64_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA64_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA64_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA64_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA64_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 128 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA128_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA128_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA128_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA128_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA128_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA128_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA128_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 256 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA256_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA256_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA256_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA256_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA256_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA256_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA256_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 512 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA512_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA512_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA512_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA512_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA512_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA512_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA512_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 1024 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA1024_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA1024_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA1024_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA1024_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA1024_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA1024_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA1024_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 2048 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA2048_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA2048_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA2048_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA2048_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA2048_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA2048_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA2048_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 4096 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA4096_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA4096_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA4096_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA4096_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA4096_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA4096_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA4096_PER_PORT_DEFAULT;
	}

	////////////////////////////////////////////////////////////////////////////////
	// 8192 Byte Blocks
	////////////////////////////////////////////////////////////////////////////////
	pDataBlkSizes++;
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MIN_DATA8192_PER_PORT,
							 &pDataBlkSizes->ulMin,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MIN_DATA8192_PER_PORT_DEFAULT );
	(void)DigiRegQueryDword( hParametersKey,
							 DIGISOH_REGVAL_MAX_DATA8192_PER_PORT,
							 &pDataBlkSizes->ulMax,
							 DigiRegFlagOptional,
							 DIGISOH_REGVAL_MAX_DATA8192_PER_PORT_DEFAULT );

	// Some Sanity checks
	if ( (LONG)pDataBlkSizes->ulMin < 0 )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA8192_PER_PORT_DEFAULT;
	}
	if (pDataBlkSizes->ulMax < pDataBlkSizes->ulMin )
	{
		pDataBlkSizes->ulMin = DIGISOH_REGVAL_MIN_DATA8192_PER_PORT_DEFAULT;
		pDataBlkSizes->ulMax = DIGISOH_REGVAL_MAX_DATA8192_PER_PORT_DEFAULT;
	}

}



VOID 
SohLogComponentUsage(
	IN PSOHALLOCTABLE	pAlloc, 
	IN PVOID			pObject)
/*++

Routine Description:

    Log the STREAMS component resource usage. 

Arguments:

	pObject
		Device object

Return Value:

    None.

--*/
{

	PIO_ERROR_LOG_PACKET	pLog;
	ULONG					cbLog;
	ULONG					InsertOffset;
    WCHAR					buffer[12][SOH_STRING_BUFFER_SIZE];
	WCHAR					wcNewLine[] = L"\r\n";

	UNICODE_STRING			ucQpairsMin;
	UNICODE_STRING			ucQpairsMax;
	UNICODE_STRING			ucStreamsMin;
	UNICODE_STRING			ucStreamsMax;
	UNICODE_STRING			ucBufCallMin;
	UNICODE_STRING			ucBufCallMax;
	UNICODE_STRING			ucTimeoutMax;
	UNICODE_STRING			ucMsgBlkMin;
	UNICODE_STRING			ucMsgBlkMax;
	UNICODE_STRING			ucLoPriPercentage;
	UNICODE_STRING			ucMedPriPercentage;
	UNICODE_STRING			ucNewLine;

	// Initialize the UNICODE strings
	ucQpairsMin.Length					= 0;
    ucQpairsMin.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucQpairsMin.Buffer					= &buffer[0][0];
	ucQpairsMax.Length					= 0;
    ucQpairsMax.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucQpairsMax.Buffer					= &buffer[1][0];

	ucStreamsMin.Length					= 0;
    ucStreamsMin.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucStreamsMin.Buffer					= &buffer[2][0];
	ucStreamsMax.Length					= 0;
    ucStreamsMax.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucStreamsMax.Buffer					= &buffer[3][0];

	ucBufCallMin.Length					= 0;
    ucBufCallMin.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucBufCallMin.Buffer					= &buffer[4][0];
	ucBufCallMax.Length					= 0;
    ucBufCallMax.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucBufCallMax.Buffer					= &buffer[5][0];

	ucTimeoutMax.Length					= 0;
    ucTimeoutMax.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucTimeoutMax.Buffer					= &buffer[6][0];

	ucMsgBlkMin.Length					= 0;
    ucMsgBlkMin.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucMsgBlkMin.Buffer					= &buffer[7][0];
	ucMsgBlkMax.Length					= 0;
    ucMsgBlkMax.MaximumLength			= SOH_STRING_BUFFER_SIZE;
    ucMsgBlkMax.Buffer					= &buffer[8][0];

	ucLoPriPercentage.Length			= 0;
    ucLoPriPercentage.MaximumLength		= SOH_STRING_BUFFER_SIZE;
    ucLoPriPercentage.Buffer			= &buffer[9][0];
	ucMedPriPercentage.Length			= 0;
    ucMedPriPercentage.MaximumLength	= SOH_STRING_BUFFER_SIZE;
    ucMedPriPercentage.Buffer			= &buffer[10][0];

	// New line
	ucNewLine.Length					= 0;
    ucNewLine.MaximumLength				= SOH_STRING_BUFFER_SIZE;
    ucNewLine.Buffer					= &buffer[11][0];

	// Set up the Strings
	RtlIntegerToUnicodeString(pAlloc->QPairsMin,		10, &ucQpairsMin);
	RtlIntegerToUnicodeString(pAlloc->QPairsMax,		10, &ucQpairsMax);
	RtlIntegerToUnicodeString(pAlloc->StreamsMin,		10, &ucStreamsMin);
	RtlIntegerToUnicodeString(pAlloc->StreamsMax,		10, &ucStreamsMax);
	RtlIntegerToUnicodeString(pAlloc->BufCallMin,		10, &ucBufCallMin);
	RtlIntegerToUnicodeString(pAlloc->BufCallMax,		10, &ucBufCallMax);
	RtlIntegerToUnicodeString(pAlloc->TimeoutMax,		10, &ucTimeoutMax);
	RtlIntegerToUnicodeString(pAlloc->MsgBsMin,			10, &ucMsgBlkMin);
	RtlIntegerToUnicodeString(pAlloc->MsgBsMax,			10, &ucMsgBlkMax);
	RtlIntegerToUnicodeString(pAlloc->LoPriMsgLimit,	10, &ucLoPriPercentage);
	RtlIntegerToUnicodeString(pAlloc->MedPriMsgLimit,   10, &ucMedPriPercentage);

	RtlInitUnicodeString(&ucNewLine,wcNewLine);

	cbLog = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) +
			ucQpairsMin.Length			+ sizeof( WCHAR ) +	    
			ucQpairsMax.Length			+ sizeof( WCHAR ) +	    
			ucStreamsMin.Length			+ sizeof( WCHAR ) +	    
			ucStreamsMax.Length			+ sizeof( WCHAR ) +	    
			ucBufCallMin.Length			+ sizeof( WCHAR ) +	    
			ucBufCallMax.Length			+ sizeof( WCHAR ) +	    
			ucTimeoutMax.Length			+ sizeof( WCHAR ) +	
			ucMsgBlkMin.Length			+ sizeof( WCHAR ) +	    
			ucMsgBlkMax.Length			+ sizeof( WCHAR ) +	
			ucLoPriPercentage.Length	+ sizeof( WCHAR ) +	    
			ucMedPriPercentage.Length	+ sizeof( WCHAR ) +	
			ucNewLine.Length			+ sizeof( WCHAR );	
	
	if ( cbLog > ERROR_LOG_MAXIMUM_SIZE )
	{
		DigiDump( DIGIINIT, ( "DigiSohComponentUsage: ErrorLogEntry size (0x%x) exceeds maximum allowed size (0x%x)!\n",
		          cbLog, ERROR_LOG_MAXIMUM_SIZE ) );
		goto Exit;
	}

	pLog = IoAllocateErrorLogEntry( pObject, (UCHAR)cbLog );
	if ( pLog == NULL )
	{
		goto Exit;
	}

	RtlZeroMemory( pLog, cbLog );
	pLog->ErrorCode			= SOH_STREAMS_COMPONENT_RESOURCES;
	pLog->UniqueErrorValue	= 200;
	pLog->DumpDataSize		= 0;
	pLog->NumberOfStrings	= 12;
	pLog->FinalStatus		= STATUS_SUCCESS ;
	InsertOffset			= FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData );
	pLog->StringOffset		= (USHORT) InsertOffset;

	///////////////// Qpairs ////////////////////////////////////////////////////

	// Minimum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucQpairsMin.Buffer,
					ucQpairsMin.Length  + sizeof( WCHAR ) );
	InsertOffset += ucQpairsMin.Length  + sizeof( WCHAR ) ;

	// Maximum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucQpairsMax.Buffer,
					ucQpairsMax.Length  + sizeof( WCHAR ) );
	InsertOffset += ucQpairsMax.Length  + sizeof( WCHAR ) ;

	///////////////// Streams ////////////////////////////////////////////////////

	// Minimum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucStreamsMin.Buffer,
					ucStreamsMin.Length  + sizeof( WCHAR ) );
	InsertOffset += ucStreamsMin.Length  + sizeof( WCHAR ) ;

	// Maximum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucStreamsMax.Buffer,
					ucStreamsMax.Length  + sizeof( WCHAR ) );
	InsertOffset += ucStreamsMax.Length  + sizeof( WCHAR ) ;

	///////////////// BuffCalls ////////////////////////////////////////////////////

	// Minimum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucBufCallMin.Buffer,
					ucBufCallMin.Length  + sizeof( WCHAR ) );
	InsertOffset += ucBufCallMin.Length  + sizeof( WCHAR ) ;

	// Maximum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucBufCallMax.Buffer,
					ucBufCallMax.Length  + sizeof( WCHAR ) );
	InsertOffset += ucBufCallMax.Length  + sizeof( WCHAR ) ;

	///////////////// Timeout ////////////////////////////////////////////////////

	// Maximum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucTimeoutMax.Buffer,
					ucTimeoutMax.Length  + sizeof( WCHAR ) );
	InsertOffset += ucTimeoutMax.Length  + sizeof( WCHAR ) ;

	///////////////// MsgBlks ////////////////////////////////////////////////////

	// Minimum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucMsgBlkMin.Buffer,
					ucMsgBlkMin.Length  + sizeof( WCHAR ) );
	InsertOffset += ucMsgBlkMin.Length  + sizeof( WCHAR ) ;

	// Maximum
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucMsgBlkMax.Buffer,
					ucMsgBlkMax.Length  + sizeof( WCHAR ) );
	InsertOffset += ucMsgBlkMax.Length  + sizeof( WCHAR ) ;

	///////////////// Percentages ////////////////////////////////////////////////////

	// Lo
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucLoPriPercentage.Buffer,
					ucLoPriPercentage.Length  + sizeof( WCHAR ) );
	InsertOffset += ucLoPriPercentage.Length  + sizeof( WCHAR ) ;

	// Med
	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucMedPriPercentage.Buffer,
					ucMedPriPercentage.Length  + sizeof( WCHAR ) );
	InsertOffset += ucMedPriPercentage.Length  + sizeof( WCHAR ) ;

	///////////////// Newline //////////////////////////////////////////////////////

	RtlCopyMemory( (PCHAR)pLog + InsertOffset,
					ucNewLine.Buffer,
					ucNewLine.Length  + sizeof( WCHAR ) );
	InsertOffset += ucNewLine.Length  + sizeof( WCHAR ) ;

	///////////////////////////////// Finished /////////////////////////////////////

	ASSERT( InsertOffset == cbLog );

	IoWriteErrorLogEntry( pLog );
Exit:
	return ;
}



VOID 
SohLogDataBlockUsage(
	IN PSOHALLOCTABLE	pAlloc, 
	IN PVOID			pObject
	)
/*++

Routine Description:

    Log the STREAMS component resource usage.

Arguments:

	pObject
		Device object

Return Value:

    None.

--*/
{
	PIO_ERROR_LOG_PACKET	pLog;
	ULONG					cbLog;
	ULONG					InsertOffset;
    WCHAR					buffer[2 * SOH_NDBCLASSES][SOH_STRING_BUFFER_SIZE];
	int						Class,Cnt,PassStart,PassEnd;
	ULONG					ulTotalLength;
	WCHAR					wcNewLine[] = L"\r\n";
    WCHAR					nlBuffer[SOH_STRING_BUFFER_SIZE];

	UNICODE_STRING			ucDataMin[SOH_NDBCLASSES];
	UNICODE_STRING			ucDataMax[SOH_NDBCLASSES];
	UNICODE_STRING			ucNewLine;

	// Set up the New Line
	ucNewLine.Length		= 0;
    ucNewLine.MaximumLength	= SOH_STRING_BUFFER_SIZE;
    ucNewLine.Buffer		= nlBuffer;
	RtlInitUnicodeString(&ucNewLine,wcNewLine);

	// We Need to Log Two separate messages, otherwise we exceed the Message size Limit,
	// We could try to use the same message and pass in the size, but this gets us too
	// close to the message limit.  If the user changes parameters we could hit the
	// limit.
	//
	// NOTE* this works OK if SOH_NDBCLASSES is an even number.
	//
	for ( PassStart = 0, PassEnd = SOH_NDBCLASSES/2;
		  PassStart < SOH_NDBCLASSES;
		  PassStart+=(SOH_NDBCLASSES/2), PassEnd+=(SOH_NDBCLASSES/2) )
	{
		// Initialize the UNICODE strings
		for ( Class = PassStart, Cnt = PassStart * 2;  Class < PassEnd;  Class++, Cnt+=2 )
		{
			ucDataMin[Class].Length			= 0;
			ucDataMin[Class].MaximumLength	= SOH_STRING_BUFFER_SIZE;
			ucDataMin[Class].Buffer			= &buffer[Cnt][0];
			ucDataMax[Class].Length			= 0;
			ucDataMax[Class].MaximumLength	= SOH_STRING_BUFFER_SIZE;
			ucDataMax[Class].Buffer			= &buffer[Cnt + 1][0];
		}

		// Calculate the Length and Initialize the Data
		ulTotalLength = 0;
		for ( Class = PassStart;  Class < PassEnd;  Class++ )
		{

			// Set up the Strings
			RtlIntegerToUnicodeString(pAlloc->aDBAlloc[Class].DataBsMin,  10, &ucDataMin[Class]);
			RtlIntegerToUnicodeString(pAlloc->aDBAlloc[Class].DataBsMax,  10, &ucDataMax[Class]);

			ulTotalLength += ucDataMin[Class].Length  + sizeof( WCHAR );
			ulTotalLength += ucDataMax[Class].Length  + sizeof( WCHAR );

		}

		// Add the NewLine Character
		ulTotalLength += ucNewLine.Length  + sizeof( WCHAR );
		
	
		cbLog = FIELD_OFFSET(IO_ERROR_LOG_PACKET, DumpData) + ulTotalLength;
		
		if ( cbLog > ERROR_LOG_MAXIMUM_SIZE )
		{
			DigiDump( DIGIINIT, ( "SohLogDataBlockUsage: ErrorLogEntry size (0x%x) exceeds maximum allowed size (0x%x)!\n",
					  cbLog, ERROR_LOG_MAXIMUM_SIZE ) );
			goto Exit;
		}

		pLog = IoAllocateErrorLogEntry( pObject, (UCHAR)cbLog );
		if ( pLog == NULL )
		{
			goto Exit;
		}

		RtlZeroMemory( pLog, cbLog );
		if (PassStart == 0)
		{
			pLog->ErrorCode			= SOH_STREAMS_DATABLK0_RESOURCES;
		}
		else
		{
			pLog->ErrorCode			= SOH_STREAMS_DATABLK1_RESOURCES;
		}
		pLog->UniqueErrorValue	= 201;
		pLog->DumpDataSize		= 0;
		pLog->NumberOfStrings	= SOH_NDBCLASSES + 1;
		pLog->FinalStatus		= STATUS_SUCCESS ;
		InsertOffset			= FIELD_OFFSET( IO_ERROR_LOG_PACKET, DumpData );
		pLog->StringOffset		= (USHORT) InsertOffset;

		// Add the strings
		for ( Class = PassStart;  Class < PassEnd;  Class++ )
		{

			// Minimum
			RtlCopyMemory( (PCHAR)pLog + InsertOffset,
							ucDataMin[Class].Buffer,
							ucDataMin[Class].Length  + sizeof( WCHAR ) );
			InsertOffset += ucDataMin[Class].Length  + sizeof( WCHAR ) ;

			// Maximum
			RtlCopyMemory( (PCHAR)pLog + InsertOffset,
							ucDataMax[Class].Buffer,
							ucDataMax[Class].Length  + sizeof( WCHAR ) );
			InsertOffset += ucDataMax[Class].Length  + sizeof( WCHAR ) ;

		}

		// NewLine
		RtlCopyMemory( (PCHAR)pLog + InsertOffset,
						ucNewLine.Buffer,
						ucNewLine.Length  + sizeof( WCHAR ) );
		InsertOffset += ucNewLine.Length  + sizeof( WCHAR ) ;

		ASSERT( InsertOffset == cbLog );

		IoWriteErrorLogEntry( pLog );

	}	

Exit:
	return ;
}


NTSTATUS
SohAllocInit( PSOHALLOCTABLE pAlloc )
/*++

Description:

	This routine initializes allocation structures for STREAMS components.
	It allocates the minimum number of structures for each resource as
	large blocks (avoids NONPAGED MEMORY pool fragmentation).

Parameters:

	pAlloc - a pointer to an allocation table

Return Value:

	NT_STATUS_SUCCESS for success.
	Non-zero for failure.

--*/
{
	PCLASSINFO      pInfoDataB;
	PSOHTIMEOUTARGS pTimeOut;
	int16           n;
	int32           Cnt;
	int32           Next;

	// Zero Out Class Structures first, in case we return with no memory

	RtlZeroMemory( &gInfoMsgB, sizeof( CLASSINFO ) );
	RtlZeroMemory( &gInfoStream, sizeof( CLASSINFO ) );
	RtlZeroMemory( gaDBClass, SOH_DBSIZE_MAX );
	for ( Cnt = 0; Cnt < SOH_NDBCLASSES; Cnt++ )
	{
		pInfoDataB	= &gaInfoDataB[Cnt];
		RtlZeroMemory( pInfoDataB, sizeof( CLASSINFO ) );
	}
	RtlZeroMemory( &gInfoTimeOut, sizeof( CLASSINFO ) );
	RtlZeroMemory( &gInfoBufCall, sizeof( CLASSINFO ) );
	RtlZeroMemory( &gInfoQPair, sizeof( CLASSINFO ) );

	//////////////////////////////////////////////////////////////////////////////////
	// Initialize message block info structures
	//////////////////////////////////////////////////////////////////////////////////

	DigiDump( DIGIINIT, ( "start mblks\n" ) );

	gInfoMsgB.Max = (unsigned short) pAlloc->MsgBsMax;

#if defined USE_ALLOCB_MEM_POOL
   // NOTE: NOTE: Do not use a memory pool for message blocks
   // Streams subsystems may alloc a new block and copy old info into it including the
   // bFreeMemory flag, which breaks the pooling scheme

   gInfoMsgB.Min = (unsigned short) pAlloc->MsgBsMin;

	if (gInfoMsgB.Min)
	{
		gInfoMsgB.AllocSize = sizeof( MSGB );

		// Set up enough Msg Blk structures for the minimum allocation.
		gInfoMsgB.pMimimumMemoryAllocation = DigiAllocMem( NonPagedPool,
												   gInfoMsgB.Min * sizeof( MSGB ) );

		if (gInfoMsgB.pMimimumMemoryAllocation)
		{
			PMSGB pMsgB = gInfoMsgB.pMimimumMemoryAllocation;

			RtlZeroMemory( gInfoMsgB.pMimimumMemoryAllocation, gInfoMsgB.Min * sizeof( MSGB ) );
		
			// Build the linked list of free structures
			for (Cnt = 0; Cnt < gInfoMsgB.Min; Cnt++, pMsgB++)
			{
				pMsgB->b_next = gInfoMsgB.pFree.MsgBs;
				gInfoMsgB.pFree.MsgBs = pMsgB;
			
			}
			
			gInfoMsgB.Free = gInfoMsgB.Min;

		}
		else
		{
			return (STATUS_INSUFFICIENT_RESOURCES);
		}

	}
#else // #if defined USE_ALLOCB_MEM_POOL
  	gInfoMsgB.Min = 0;
#endif // #if defined USE_ALLOCB_MEM_POOL


	gInfoMsgB.Stop[BPRI_HI]		= 0;
	gInfoMsgB.Stop[BPRI_MED]	= ( gInfoMsgB.Min * pAlloc->MedPriMsgLimit ) / 100;
	gInfoMsgB.Stop[BPRI_LO]		= ( gInfoMsgB.Min * pAlloc->LoPriMsgLimit )  / 100;

	DigiDump( DIGIINIT, ( "done mblks\n" ) );

	//////////////////////////////////////////////////////////////////////////////////
	// Initialize data block block info structures
	//////////////////////////////////////////////////////////////////////////////////

	DigiDump( DIGIINIT, ( "start dblks\n" ) );

	for ( Cnt = 0, Next = 0; Cnt < SOH_NDBCLASSES; Cnt++ )
	{
		int32           DataSize;
		int32           Size;

		pInfoDataB	= &gaInfoDataB[Cnt];
		DataSize	= pAlloc->aDBAlloc[Cnt].Size;
		Size		= DataSize + sizeof( DATAB );

		pInfoDataB->Max = (unsigned short) pAlloc->aDBAlloc[Cnt].DataBsMax;

		// The DATAB size, plus the buffer size
		pInfoDataB->DataBSize = Size;
		pInfoDataB->AllocSize = Size;
#if defined USE_ALLOCB_MEM_POOL
		pInfoDataB->Min	= (unsigned short) pAlloc->aDBAlloc[Cnt].DataBsMin;
		if (pInfoDataB->Min)
		{

			// Set up enough Data Bloc structures for the minimum allocation.
			pInfoDataB->pMimimumMemoryAllocation = DigiAllocMem( NonPagedPool,
										 pInfoDataB->Min * pInfoDataB->DataBSize );

			if (pInfoDataB->pMimimumMemoryAllocation)
			{
				int		CntBlks;
				PDATAB	pDataB = pInfoDataB->pMimimumMemoryAllocation;

				RtlZeroMemory( pInfoDataB->pMimimumMemoryAllocation, pInfoDataB->Min * pInfoDataB->DataBSize );

				// Build the linked list of free structures
				for (CntBlks = 0; CntBlks < pInfoDataB->Min; CntBlks++, (PUCHAR) pDataB += pInfoDataB->DataBSize)
				{
					pDataB->db_freep = pInfoDataB->pFree.DataBs;
					pInfoDataB->pFree.DataBs = pDataB;
					
					// initilize data block
					pDataB->db_base = ( bufp_t ) pDataB + sizeof( DATAB );
					pDataB->db_lim = ( bufp_t ) pDataB + pInfoDataB->DataBSize;
					pDataB->db_class = (unsigned char) Cnt;

				}
				
				pInfoDataB->Free = pInfoDataB->Min;

			}
			else
			{
				return (STATUS_INSUFFICIENT_RESOURCES);
			}

		}
#else // #if defined USE_ALLOCB_MEM_POOL
      pInfoDataB->Min	= 0;
#endif // #if defined USE_ALLOCB_MEM_POOL

		// Set the priority limits
		pInfoDataB->Stop[BPRI_HI]  = 0;
		pInfoDataB->Stop[BPRI_MED] = ( pInfoDataB->Min * pAlloc->MedPriMsgLimit ) / 100;
		pInfoDataB->Stop[BPRI_LO]  = ( pInfoDataB->Min * pAlloc->LoPriMsgLimit ) / 100;

		if ( DataSize < Next || DataSize > SOH_DBSIZE_MAX )
		{
			// Shouldn't happen!!!
			ASSERT(0);
			return (STATUS_INVALID_PARAMETER);
		}

		while ( Next < DataSize )
		{
			gaDBClass[Next++] = (unsigned char) Cnt;
		}
	}

	DigiDump( DIGIINIT, ( "done dblks\n" ) );

	//////////////////////////////////////////////////////////////////////////////////
	// Initialize Streams info structures
	//////////////////////////////////////////////////////////////////////////////////

	DigiDump( DIGIINIT, ( "start Streams\n" ) );

	gInfoStream.Min = (unsigned short) pAlloc->StreamsMin;
	gInfoStream.Max = (unsigned short) pAlloc->StreamsMax;

	// Set up enough Streams structures for the minimum allocation.
	gInfoStream.pMimimumMemoryAllocation = DigiAllocMem( NonPagedPool,
		                                       gInfoStream.Min * sizeof( STREAM ) );

	if (gInfoStream.Min)
	{

		if (gInfoStream.pMimimumMemoryAllocation)
		{
			PSTREAM		pStream = gInfoStream.pMimimumMemoryAllocation;

			gInfoStream.AllocSize = sizeof( STREAM );

			RtlZeroMemory( gInfoStream.pMimimumMemoryAllocation, gInfoStream.Min * sizeof( STREAM ) );
		
			// Build the linked list of free structures
			for (Cnt = 0; Cnt < gInfoStream.Min; Cnt++, pStream++)
			{
				pStream->str_cnext = gInfoStream.pFree.Streams;
				gInfoStream.pFree.Streams = pStream;
			
			}
			
			gInfoStream.Free = gInfoStream.Min;

		}
		else
		{
			return (STATUS_INSUFFICIENT_RESOURCES);
		}

	}

	DigiDump( DIGIINIT, ( "done Streams\n" ) );

	//////////////////////////////////////////////////////////////////////////////////
	// Initialize queue pair info structures
	//////////////////////////////////////////////////////////////////////////////////

	DigiDump( DIGIINIT, ( "start queues\n" ) );

	gInfoQPair.Min = (unsigned short) pAlloc->QPairsMin;
	gInfoQPair.Max = (unsigned short) pAlloc->QPairsMax;

	if (gInfoQPair.Min)
	{

		// Set up enough QPairs structures for the minimum allocation.
		gInfoQPair.pMimimumMemoryAllocation = DigiAllocMem( NonPagedPool,
												   gInfoQPair.Min * 2 * sizeof( QUEUE ) );

		if (gInfoQPair.pMimimumMemoryAllocation)
		{
			PQUEUE pQ = gInfoQPair.pMimimumMemoryAllocation;

			gInfoQPair.AllocSize = 2 * sizeof( QUEUE );

			RtlZeroMemory( gInfoQPair.pMimimumMemoryAllocation, gInfoQPair.Min * 2 * sizeof( QUEUE ) );
		
			// Build the linked list of free structures
			for (Cnt = 0; Cnt < gInfoQPair.Min; Cnt++, pQ+=2)
			{
				pQ->q_next = gInfoQPair.pFree.QPairs;
				gInfoQPair.pFree.QPairs = pQ;
			
			}
			
			gInfoQPair.Free = gInfoQPair.Min;

		}
		else
		{
			return (STATUS_INSUFFICIENT_RESOURCES);
		}

	}

	DigiDump( DIGIINIT, ( "done queues\n" ) );

	//////////////////////////////////////////////////////////////////////////////////
	// Initialize BufCall info structures
	//////////////////////////////////////////////////////////////////////////////////

	DigiDump( DIGIINIT, ( "start bufcallargs\n" ) );

	gInfoBufCall.Min = (unsigned short) pAlloc->BufCallMin;
	gInfoBufCall.Max = (unsigned short) pAlloc->BufCallMax;

	if (gInfoBufCall.Min)
	{

		// Set up enough QPairs structures for the minimum allocation.
		gInfoBufCall.pMimimumMemoryAllocation = DigiAllocMem( NonPagedPool,
												   gInfoBufCall.Min * sizeof( SOHBUFCALLARGS ) );

		if (gInfoBufCall.pMimimumMemoryAllocation)
		{
			PSOHBUFCALLARGS pBufCall = gInfoBufCall.pMimimumMemoryAllocation;

			gInfoBufCall.AllocSize = sizeof( SOHBUFCALLARGS );

			RtlZeroMemory( gInfoBufCall.pMimimumMemoryAllocation, gInfoBufCall.Min * sizeof( SOHBUFCALLARGS ) );
		
			// Build the linked list of free structures
			for (Cnt = 0; Cnt < gInfoBufCall.Min; Cnt++, pBufCall++)
			{
				pBufCall->Next = gInfoBufCall.pFree.BufCallArgs;
				gInfoBufCall.pFree.BufCallArgs = pBufCall;
			
			}
			
			gInfoBufCall.Free = gInfoBufCall.Min;

		}
		else
		{
			return (STATUS_INSUFFICIENT_RESOURCES);
		}

	}

	DigiDump( DIGIINIT, ( "done bufcallargs\n" ) );

	//////////////////////////////////////////////////////////////////////////////////
	// Initialize timeout info structures
	//////////////////////////////////////////////////////////////////////////////////

	DigiDump( DIGIINIT, ( "start timeout\n" ) );

	gInfoTimeOut.Max = (unsigned short) pAlloc->TimeoutMax; 

	// Set up enough Timeout structures (allocate all no minimum).
	gInfoTimeOut.pMimimumMemoryAllocation = DigiAllocMem( NonPagedPool,
		                                gInfoTimeOut.Max * sizeof( SOHTIMEOUTARGS ) );

	if (gInfoTimeOut.pMimimumMemoryAllocation)
	{
		pTimeOut = gInfoTimeOut.pMimimumMemoryAllocation;

		gInfoTimeOut.AllocSize = sizeof( SOHTIMEOUTARGS );

		RtlZeroMemory( gInfoTimeOut.pMimimumMemoryAllocation, gInfoTimeOut.Max * sizeof( SOHTIMEOUTARGS ) );
	
		// Build the linked list of free structures
		for (Cnt = 0; Cnt < gInfoTimeOut.Max; Cnt++, pTimeOut++)
		{
			pTimeOut->Next = gInfoTimeOut.pFree.TimeOutArgs;
			gInfoTimeOut.pFree.TimeOutArgs = pTimeOut;
		
		}
		
		gInfoTimeOut.Free = gInfoTimeOut.Max;

	}
	else
	{
		return (STATUS_INSUFFICIENT_RESOURCES);
	}

	DigiDump( DIGIINIT, ( "done timeout\n" ) );

	///////////////////// Done Initializing Pools ///////////////////////////////////

	gNTHZ = (N100NS_PER_SEC/KeQueryTimeIncrement());

	return (STATUS_SUCCESS);
}								   // init_alloc

void
SohAllocUnload(  )
/*++

Description:

	Free the memory allocated for the minimum allocation for each
	STREAMS resource type.

Parameters:

Return Value:

	

--*/
{

	int Cnt;

	//////////////////////////////////////////////////////////////////////////////////
	// Deallocate Streams info structure
	//////////////////////////////////////////////////////////////////////////////////

	// All streams should be closed at this time
	ASSERT(gInfoStream.Used == 0);

	if (gInfoStream.pMimimumMemoryAllocation)
	{
		DigiFreeMem( gInfoStream.pMimimumMemoryAllocation);
		gInfoStream.pMimimumMemoryAllocation = NULL;
	}

	// All QPairs should be deallocated at this time
	ASSERT(gInfoQPair.Used == 0);

	if (gInfoQPair.pMimimumMemoryAllocation)
	{
		DigiFreeMem( gInfoQPair.pMimimumMemoryAllocation);
		gInfoQPair.pMimimumMemoryAllocation = NULL;
	}

	// Timeout structues are not deallocated at run time, simply free the
	// memory.
	if (gInfoTimeOut.pMimimumMemoryAllocation)
	{
		DigiFreeMem( gInfoTimeOut.pMimimumMemoryAllocation);
		gInfoTimeOut.pMimimumMemoryAllocation = NULL;
	}

	// BuffCall structues are not deallocated at run time, simply free the
	// memory.
	if (gInfoBufCall.pMimimumMemoryAllocation)
	{
		DigiFreeMem( gInfoBufCall.pMimimumMemoryAllocation);
		gInfoBufCall.pMimimumMemoryAllocation = NULL;
	}

	// All Msg Blocks should be deallocated by QUEUE flushes
	ASSERT(gInfoMsgB.Used == 0);

	if (gInfoMsgB.pMimimumMemoryAllocation)
	{
		DigiFreeMem( gInfoMsgB.pMimimumMemoryAllocation);
		gInfoMsgB.pMimimumMemoryAllocation = NULL;
	}

	// All Data Blocks should be deallocated by QUEUE flushes
	for ( Cnt = 0; Cnt < SOH_NDBCLASSES; Cnt++ )
	{
		PCLASSINFO      pInfoDataB;

		pInfoDataB	= &gaInfoDataB[Cnt];
		ASSERT(pInfoDataB->Used == 0);
		if (pInfoDataB->pMimimumMemoryAllocation)
		{
			DigiFreeMem( pInfoDataB->pMimimumMemoryAllocation);
			pInfoDataB->pMimimumMemoryAllocation = NULL;
		}
	}

}



PMSGB
allocb( int Size, int Priority )
/*++

Description:

	This routine allocates a message block with a data block of at least the
	requested size.

Parameters:

	Size - the minimum size of the data buffer.
	Priority - the priority of this request.

Return Value:

	A valid pointer to the message block on success.
	Zero on failure.
--*/
{
	PMSGB           pMsgB;
	PDATAB          pDataB;
	PCLASSINFO      pInfoDataB;
	unchar          iClass;

	// Check size

	if ( Size < 0 || Size > SOH_DBSIZE_MAX )
	{
		return 0;
	}

	// Get priority into range

	Priority &= ALLOC_PRI_MASK;

	if ( Priority < BPRI_LO )
	{
		Priority = BPRI_LO;
	}
	else if ( Priority > BPRI_HI )
	{
		Priority = BPRI_HI;
	}

	// Get a message block

#if defined USE_ALLOCB_MEM_POOL
	if ( gInfoMsgB.Free <= gInfoMsgB.Stop[Priority] )
	{
		// alloc more if we haven't reached max
#endif // #if defined USE_ALLOCB_MEM_POOL

		if ( gInfoMsgB.Used < gInfoMsgB.Max )
		{
			pMsgB = DigiAllocMem( NonPagedPool, sizeof( MSGB ) );

			if ( !pMsgB )
			{
				gInfoMsgB.Fail++;

				return 0;
			}

			RtlZeroMemory( pMsgB, sizeof( MSGB ) );

			// Mark the message block to be free upon deallocation
			pMsgB->bFreeMemory = TRUE;

			// update count and high water mark
			gInfoMsgB.Used++;

			if ( ( gInfoMsgB.Used ) > gInfoMsgB.Hwm )
			{
				gInfoMsgB.Hwm = gInfoMsgB.Used;
			}
		}
		else
		{
			gInfoMsgB.Fail++;

			return 0;
		}
#if defined USE_ALLOCB_MEM_POOL
	}
	else
	{
		// Remove a message block from the free list

		ASSERT( gInfoMsgB.pFree.MsgBs );

		pMsgB = gInfoMsgB.pFree.MsgBs;
		gInfoMsgB.pFree.MsgBs = pMsgB->b_next;

		// update counts

		gInfoMsgB.Free--;
		gInfoMsgB.Used++;

		if ( ( gInfoMsgB.Used ) > gInfoMsgB.Hwm )
		{
			gInfoMsgB.Hwm = gInfoMsgB.Used;
		}

	}
#endif // #if defined USE_ALLOCB_MEM_POOL

	if ( Size == 0 )
	{
      // No data associated with this message
		return ( pMsgB );
	}

   // Choose a size class for the data block
	iClass = gaDBClass[Size - 1];
	pInfoDataB = &gaInfoDataB[iClass];

	// Get a data block

#if defined USE_ALLOCB_MEM_POOL
	for ( ;; )
	{
		if ( pInfoDataB->Free <= pInfoDataB->Stop[Priority] )
		{
			// alloc more if we haven't reached max

			if ( pInfoDataB->Used < pInfoDataB->Max )
			{
				pDataB = DigiAllocMem( NonPagedPool, pInfoDataB->DataBSize );

#else // #if defined USE_ALLOCB_MEM_POOL

         if ( pInfoDataB->Used < pInfoDataB->Max )
			{
				pDataB = DigiAllocMem( NonPagedPool, (Size + sizeof( DATAB )));

#endif // #if defined USE_ALLOCB_MEM_POOL

				if ( pDataB )
				{
					// initilize data block
#if defined USE_ALLOCB_MEM_POOL
					RtlZeroMemory( pDataB,  pInfoDataB->DataBSize );
					pDataB->db_lim = ( bufp_t ) pDataB + pInfoDataB->DataBSize;
#else // #if defined USE_ALLOCB_MEM_POOL
					RtlZeroMemory( pDataB,  (Size + sizeof( DATAB )));
					pDataB->db_lim = ( bufp_t ) pDataB + Size + sizeof( DATAB );
#endif // #if defined USE_ALLOCB_MEM_POOL

					pDataB->db_base = ( bufp_t ) pDataB + sizeof( DATAB );
					pDataB->db_class = iClass;

					// Mark the buffer to be freed upon deallocation
					pDataB->bFreeMemory = TRUE;

					// update count and high water mark
					pInfoDataB->Used++;

					if ( ( pInfoDataB->Used ) > pInfoDataB->Hwm )
					{
						pInfoDataB->Hwm = pInfoDataB->Used;
					}
#if !defined USE_ALLOCB_MEM_POOL
			      else
			      {
				      pInfoDataB->Fail++;
			      }
            }
			   else
			   {
				   pInfoDataB->Fail++;
			   }
#else // #if !defined USE_ALLOCB_MEM_POOL
					break;
				}
				pInfoDataB->Fail++;
			}
			else
			{
				pInfoDataB->Fail++;
			}

			// check the next biggest data block pool on the next iteration

			if ( ++iClass >= SOH_NDBCLASSES )
			{
				// free the message block
				if (pMsgB->bFreeMemory) {
					DigiFreeMem(pMsgB);
				}
				else
				{
					pMsgB->b_next = gInfoMsgB.pFree.MsgBs;
					gInfoMsgB.pFree.MsgBs = pMsgB;

					// update counts

					gInfoMsgB.Free++;
				}
				gInfoMsgB.Used--;

				return 0;
			}

			pInfoDataB = &gaInfoDataB[iClass];
		}
		else
		{
			// Remove a data block from the free list

			ASSERT( pInfoDataB->pFree.DataBs );

			pDataB = pInfoDataB->pFree.DataBs;
			pInfoDataB->pFree.DataBs = pDataB->db_freep;

			// update counts

			pInfoDataB->Used++;
			pInfoDataB->Free--;

			if ( ( pInfoDataB->Used ) > pInfoDataB->Hwm )
			{
				pInfoDataB->Hwm = pInfoDataB->Used;
			}

			break;
		}
#endif // #if !defined USE_ALLOCB_MEM_POOL
	}

	// initialize data and message blocks and connect together

	pDataB->db_freep	= 0;
	pDataB->db_type		= M_DATA;
	pDataB->db_ref		= 1;
	pDataB->db_diag		= 0xff;

	pMsgB->b_next	= pMsgB->b_prev = pMsgB->b_cont = 0;
	pMsgB->b_rptr	= pMsgB->b_wptr = pDataB->db_base;
	pMsgB->b_datap	= pDataB;

	// Successful return

	return ( pMsgB );
}								   // allocb

void
freeb( PMSGB pMsgB )
/*++

Description:

	This routine frees a message block and the data associated with it.

Parameters:

	pMsgB - a pointer to the message block to free.

Return Value:

	None

--*/
{
	PDATAB          pDataB;
	PCLASSINFO      pInfoDataB;
	PSOHBUFCALLARGS *ppBCA;
	PSOHBUFCALLARGS pBCANewTail;

	ASSERT( pMsgB ); // null pointer

	if ( !pMsgB )
	{
		return;
	}

	ASSERT( !pMsgB->b_cont ); // block still linked
	ASSERT( !pMsgB->b_next ); // block still linked
	// ASSERT( !pMsgB->b_prev ); // block still linked - flushq doesn't clear
	ASSERT( pMsgB->b_datap ); // null pointer
	ASSERT( pMsgB->b_datap->db_ref >= 0 ); // count is negative

	// unlink data block from message block
	pDataB = pMsgB->b_datap;
	pMsgB->b_datap = 0;

	// free message block or return to free list
	if ( ( pMsgB->bFreeMemory ) && !( gInfoMsgB.pRun.BufCallArgs ) )
	{
		DigiFreeMem( pMsgB );
		gInfoMsgB.Used--;
	}
	else
	{
		pMsgB->b_next = gInfoMsgB.pFree.MsgBs;
		gInfoMsgB.pFree.MsgBs = pMsgB;

		gInfoMsgB.Free++;
		gInfoMsgB.Used--;
	}

	// queue bufcall requests

	if ( gInfoMsgB.pRun.BufCallArgs &&
	  ( gInfoMsgB.Free > gInfoMsgB.Stop[gInfoMsgB.pRun.BufCallArgs->Priority] ) )
	{
		// remove the bufcallarg entry from it's list

		pBCANewTail = gInfoMsgB.pRun.BufCallArgs;
		gInfoMsgB.pRun.BufCallArgs = pBCANewTail->Next;
		pBCANewTail->Next = 0;

		// put on the bufcall run list

		ppBCA = &gInfoBufCall.pRun.BufCallArgs;

		while ( *ppBCA)
		{
			// ??? perhaps back pointers would be better than walking this list

			ppBCA = &(*ppBCA)->Next;
		}

		*ppBCA = pBCANewTail;
	}


	// Decrement reference count: if not last, keep block

	if ( --pDataB->db_ref )
	{
		return;
	}

	// free data block or return to free list

	pInfoDataB = &gaInfoDataB[pDataB->db_class];

	if ( ( pDataB->bFreeMemory ) && !( pInfoDataB->pRun.BufCallArgs ) )
	{
		DigiFreeMem( pDataB );
		pInfoDataB->Used--;
	}
	else
	{
		pDataB->db_diag = 0;
		pDataB->db_freep = pInfoDataB->pFree.DataBs;
		pInfoDataB->pFree.DataBs = pDataB;

		pInfoDataB->Free++;
		pInfoDataB->Used--;
	}

	// queue bufcall requests

	if ( pInfoDataB->pRun.BufCallArgs &&
		 ( pInfoDataB->Free > pInfoDataB->Stop[pInfoDataB->pRun.BufCallArgs->Priority] ) )
	{
		// remove the bufcallarg entry from it's list
		pBCANewTail = pInfoDataB->pRun.BufCallArgs;
		pInfoDataB->pRun.BufCallArgs = pBCANewTail->Next;
		pBCANewTail->Next = 0;

		// put on the end of bufcall run list

		ppBCA = &gInfoBufCall.pRun.BufCallArgs;

		while ( *ppBCA)
		{
			// ??? perhaps back pointers would be better than walking this list

			ppBCA = &(*ppBCA)->Next;
		}

		*ppBCA = pBCANewTail;
	}

	return;
}								   // freeb

PMSGB
allocm( int Priority )
/*++

Description:

	This routine allocates a message block with no data block attached.

Parameters:

	Priority - the priority for this allocation

Return Value:

	A valid pointer to the message block on success.
	Zero on failure.

--*/
{
	PMSGB           pMsgB;

	pMsgB = allocb( 0, Priority );

	return ( pMsgB );
}								   // allocm

void
freemsg( PMSGB pMsgB )
/*++

Description:

	This routine frees all message blocks in a message.

Parameters:

	pMsgB - pointer to message to free.

Return Value:

	None

--*/
{
	PMSGB           pNextMsgB;

	ASSERT( pMsgB );

	// Go down message chain freeing blocks

	while ( pMsgB )
	{

		ASSERT( !pMsgB->b_next );

		pNextMsgB = pMsgB->b_cont;
		pMsgB->b_cont = 0;
		freeb( pMsgB );
		pMsgB = pNextMsgB;
	}
}								   // freemsg

PQUEUE
allocq(  )
/*++

Description:

	This routine allocates a pair of queue structures.

Parameters:

	None

Return Value:

	Pointer to write side queue for success.
	Zero for failure.

--*/
{
	PQUEUE          pWQ;
	PQUEUE          pRQ;

	// use a free queue pair or alloc more if out

	if ( gInfoQPair.Free )
	{
		// Remove from free list

		ASSERT( gInfoQPair.pFree.QPairs );

		pWQ = gInfoQPair.pFree.QPairs;
		gInfoQPair.pFree.QPairs = pWQ->q_next;

		// clear structures

		RtlZeroMemory( pWQ, sizeof( QUEUE ) * 2 );

		// update counts

		gInfoQPair.Used++;
		gInfoQPair.Free--;

		if ( ( gInfoQPair.Used ) > gInfoQPair.Hwm )
		{
			gInfoQPair.Hwm = gInfoQPair.Used;
		}

	}
	else
	{
		// allocate a pair: by convention the write queue is first

		if ( gInfoQPair.Used < gInfoQPair.Max )
		{
			pWQ = DigiAllocMem( NonPagedPool, sizeof( QUEUE ) * 2 );

			if ( !pWQ )
			{
				gInfoQPair.Fail++;

				return ( 0 );
			}

			RtlZeroMemory( pWQ, sizeof( QUEUE ) * 2 );

			// This structure is not part of the minimum pool
			pWQ->bFreeMemory = TRUE;

			// update count and high water mark

			gInfoQPair.Used++;

			if ( ( gInfoQPair.Used ) > gInfoQPair.Hwm )
			{
				gInfoQPair.Hwm = gInfoQPair.Used;
			}
		}
		else
		{
			gInfoQPair.Fail++;

			return ( 0 );
		}
	}

	// get read queue pointer and initialize both queues

	pRQ = pWQ + 1;
	pWQ->q_other = pRQ;
	pRQ->q_other = pWQ;

	// mark queues as in use

	RD( pWQ )->q_flag = QUSE | QREADR;
	pWQ->q_flag = QUSE;

	// Sucessful return

	return ( pWQ );
}								   // alloq


void
freeq( PQUEUE pQ )
/*++

Description:

	This routine frees a pair of queue structures.

Parameters:

	pQ - pointer to queue to free.

Return Value:

	None

--*/
{
	// Must be write side

	if ( pQ->q_flag & QREADR )
		pQ = WR( pQ );

	// Flush any remaining data

	flushq( pQ, FLUSHALL );
	flushq( RD( pQ ), FLUSHALL );

	// Ensure q is not scheduled

	unrunq( pQ );
	unrunq( RD( pQ ) );

	// clear flags

	pQ->q_flag = 0;
	RD( pQ )->q_flag = 0;

	// free queue pair or return to free list

	if ( pQ->bFreeMemory )
	{
		DigiFreeMem( pQ );

		// update counts

		gInfoQPair.Used--;
	}
	else
	{
		pQ->q_next = gInfoQPair.pFree.QPairs;
		gInfoQPair.pFree.QPairs = pQ;

		// update counts

		gInfoQPair.Used--;
		gInfoQPair.Free++;

	}
}								   // freeq


PSTREAM
allocs(  )
/*++

Description:

	This routine allocates a Stream structure.

Parameters:

	None

Return Value:

	A valid pointer to the Stream structure on success.
	Zero on failure.

--*/
{
	PSTREAM         pStream;

	// use a free Streams struct or alloc more if out

	if ( gInfoStream.Free )
	{
		// Remove from free list

		ASSERT( gInfoStream.pFree.Streams );

		pStream = gInfoStream.pFree.Streams;
		gInfoStream.pFree.Streams = pStream->str_cnext;

		// update counts

		gInfoStream.Used++;
		gInfoStream.Free--;

		if ( ( gInfoStream.Used ) > gInfoStream.Hwm )
		{
			gInfoStream.Hwm = gInfoStream.Used;
		}

		// clear structure
		RtlZeroMemory( pStream, sizeof( STREAM ) );

	}
	else
	{
		// allocate a Stream struct

		if ( gInfoStream.Used < gInfoStream.Max )
		{
			pStream = DigiAllocMem( NonPagedPool, sizeof( STREAM ) );

			if ( !pStream )
			{
				gInfoStream.Fail++;

				return ( 0 );
			}

			RtlZeroMemory( pStream, sizeof( STREAM ) );

			// Mark as not part of free pool
			pStream->bFreeMemory = TRUE;

			// update count and high water mark

			gInfoStream.Used++;

			if ( ( gInfoStream.Used ) > gInfoStream.Hwm )
			{
				gInfoStream.Hwm = gInfoStream.Used;
			}
		}
		else
		{
			gInfoStream.Fail++;

			return ( 0 );
		}
	}

	// Sucessful return

	return ( pStream );
}								   // allocs

void
frees( PSTREAM pStream )
/*++

Description:

	This routine frees a Streams structure.

Parameters:

	None

Return Value:

	None

--*/
{
	// free Stream struct or return to free list

	if ( pStream->bFreeMemory )
	{
		// Not part of the Minimum leased buffers
		DigiFreeMem( pStream );
	}
	else
	{
		pStream->str_cnext = gInfoStream.pFree.Streams;
		gInfoStream.pFree.Streams = pStream;
		gInfoStream.Free++;

	}

	// update counts
	gInfoStream.Used--;

}								   // frees

int
runbcq(  )
/*++

Description:

	This routine calls functions queued by bufcall.

Parameters:

	None

Return Value:

	Zero if nothing queued.
	Non-zero if queued functions called.

--*/
{
	PSOHBUFCALLARGS pBCA;
	PSOHBUFCALLARGS pNextBCA;

	// check if anything queued

	if ( gInfoBufCall.pRun.BufCallArgs == 0 )
		return 0;

	// run the queued functions and put the BCAs back on the free list

	pBCA = gInfoBufCall.pRun.BufCallArgs;

	while ( pBCA )
	{
		// call function

		( *pBCA->pFunc ) ( pBCA->Arg );

		// free BCA struct or return to free list

		pNextBCA = pBCA->Next;

		if ( pBCA->bFreeMemory )
		{
			DigiFreeMem( pBCA );

			// update counts

			gInfoBufCall.Used--;
		}
		else
		{
			pBCA->pFunc = 0;
			pBCA->Next = gInfoBufCall.pFree.BufCallArgs;
			gInfoBufCall.pFree.BufCallArgs = pBCA;

			// update counts

			gInfoBufCall.Used--;
			gInfoBufCall.Free++;
		}

		pBCA = pNextBCA;
	}

	// reset the run queue head

	gInfoBufCall.pRun.BufCallArgs = pBCA;

	return 1;
}								   // runbcq



int
bufcall( int Size, int Priority, PFI pFunc, caddr_t Arg )
/*++

Description:

	This routine stores a function to be called when the specified buffer
	size becomes available.

Parameters:

	Size - the size of data buffer requested.
	Priority - the priority of this request.
	pFunc - a pointer to the function to call.
	Arg - the argument to the function.

Return Value:

	Non-zero if sucessful.
	Zero if an error was encountered.

--*/
{
	PSOHBUFCALLARGS pBCANew;
	PSOHBUFCALLARGS *ppBCA;

	ASSERT( Size );
	ASSERT( pFunc );

	// Check size

	if ( Size < 0 || Size > SOH_DBSIZE_MAX )
		return 0;

	// Get priority into range

	Priority &= ALLOC_PRI_MASK;

	if ( Priority < BPRI_LO )
	{
		Priority = BPRI_LO;
	}
	else if ( Priority > BPRI_HI )
	{
		Priority = BPRI_HI;
	}

	// use a free bufcall struct or alloc more if out

	if ( gInfoBufCall.Free )
	{
		// Remove from free list

		ASSERT( gInfoBufCall.pFree.BufCallArgs );

		pBCANew = gInfoBufCall.pFree.BufCallArgs;
		gInfoBufCall.pFree.BufCallArgs = pBCANew->Next;

		// update counts

		gInfoBufCall.Used++;
		gInfoBufCall.Free--;

		if ( ( gInfoBufCall.Used ) > gInfoBufCall.Hwm )
		{
			gInfoBufCall.Hwm = gInfoBufCall.Used;
		}
	}
	else
	{
		// allocate a bufcall struct

		if ( gInfoBufCall.Used < gInfoBufCall.Max )
		{
			pBCANew = DigiAllocMem( NonPagedPool, sizeof( SOHBUFCALLARGS ) );

			if ( !pBCANew )
			{
				gInfoBufCall.Fail++;
				return ( 0 );
			}

			RtlZeroMemory( pBCANew, sizeof( SOHBUFCALLARGS ) );

			// Mark the BufCall structure as not part of the minimum pool
			pBCANew->bFreeMemory = TRUE;

			// update count and high water mark

			gInfoBufCall.Used++;

			if ( ( gInfoBufCall.Used ) > gInfoBufCall.Hwm )
			{
				gInfoBufCall.Hwm = gInfoBufCall.Used;
			}
		}
		else
		{
			gInfoBufCall.Fail++;

			return ( 0 );
		}
	}


	// determine target list to use to store bufcall request

	if ( gInfoMsgB.Free <= gInfoMsgB.Stop[Priority] )
	{
		ppBCA = &gInfoMsgB.pRun.BufCallArgs;
		pBCANew->Size = 0;			   /* diagnostic */
	}
	else
	{
		ppBCA= &( gaInfoDataB[gaDBClass[Size - 1]] ).pRun.BufCallArgs;
		pBCANew->Size = Size;		   /* diagnostic */
	}

	// fill in remaining members

	pBCANew->Priority = Priority;
	pBCANew->pFunc = pFunc;
	pBCANew->Arg = Arg;

	// store in target list by priority

	while ( *ppBCA && ( ( *ppBCA )->Priority >= Priority ) )
	{
		ppBCA = &( *ppBCA )->Next;
	}

	pBCANew->Next = *ppBCA;
	*ppBCA = pBCANew;

	// Sucessful return

	return ( 1 );
}

int
timeout( PFI pFunc, caddr_t Arg, int Ticks )
/*++

Description:

	This routine schedules a function to be called in the future.

Parameters:

	pFunc - a pointer to the function to call.
	Arg - the argument to the function.
	Ticks - the number of ticks into the future for the function to be called

Return Value:

	A non-zero timeout identifier.
	Zero if an error was encountered.

--*/
{
	PSOHTIMEOUTARGS     pTOANew;
	PSOHTIMEOUTARGS     pTOA;
	PSOHTIMEOUTARGS    *ppTOA;

	ASSERT( gInfoTimeOut.pFree.TimeOutArgs );

	if ( !gInfoTimeOut.pFree.TimeOutArgs )
	{
		// this shouldn't happen

		ASSERT( 0 );

		return( 0 );
	}

	// get a free timeout from list

	pTOANew = gInfoTimeOut.pFree.TimeOutArgs;
	gInfoTimeOut.pFree.TimeOutArgs = pTOANew->Next;

	// fill in members

	pTOANew->pFunc = pFunc;
	pTOANew->Arg = Arg;
	pTOANew->Time = ( gCalling ? gCalltime : curtime(  ) ) + Ticks;
	pTOANew->TimeoutId = ++gTimeoutId ? gTimeoutId : ++gTimeoutId; // skip 0

	// insert this request on the run queue

	for ( ppTOA = &gInfoTimeOut.pRun.TimeOutArgs;
		  ( pTOA = *ppTOA ) && ( pTOANew->Time - pTOA->Time < TIMER_WRAP );
		  ppTOA = &pTOA->Next );

	pTOANew->Next = pTOA;
	*ppTOA = pTOANew;

	// update count and high water mark

	gInfoTimeOut.Used++;

	if ( ( gInfoTimeOut.Used ) > gInfoTimeOut.Hwm )
	{
		gInfoTimeOut.Hwm = gInfoTimeOut.Used;
	}

	// return the timeout identifier

	return pTOANew->TimeoutId;

}

void
untimeout( int TimeoutId )
/*++

Description:

	This routine unschedules a previously scheduled timeout function.

Parameters:

	TimeoutId - a timeout identifier.

Return Value:

	None.

--*/
{
	PSOHTIMEOUTARGS     pTOA;
	PSOHTIMEOUTARGS     *ppTOA;

	// remove the timeout structure from the run queue

	for ( ppTOA = &gInfoTimeOut.pRun.TimeOutArgs;
		  ( pTOA = *ppTOA ) && ( pTOA->TimeoutId != TimeoutId );
		  ppTOA = &pTOA->Next );

	if ( !pTOA )
	{
		// printf( "untimeout: ti_which %x not found\n", which );

		return;
	}

	*ppTOA = pTOA->Next;

	// return timeout structure to free list

	pTOA->Next = gInfoTimeOut.pFree.TimeOutArgs;
	pTOA->pFunc = 0;
	pTOA->Arg = 0;
	pTOA->Time = 0;
	pTOA->TimeoutId = 0;

	gInfoTimeOut.pFree.TimeOutArgs = pTOA;

	// update count

	gInfoTimeOut.Used--;

	return;

}

void
runtoq(  )
/*++

Description:

	This routine calls functions queued by timeout.

Parameters:

	None.

Return Value:

	None.

--*/
{
	PSOHTIMEOUTARGS     pTOA;

	// process timed out functions

	while ( ( pTOA = gInfoTimeOut.pRun.TimeOutArgs ) && ( LATER_THAN( pTOA->Time ) ) )
	{
		// remove from run queue

		gInfoTimeOut.pRun.TimeOutArgs = pTOA->Next ;

		// call function

		gCalltime = pTOA->Time;

		gCalling++;
		( *pTOA->pFunc ) ( pTOA->Arg );
		gCalling--;

		// Move to free list

		pTOA->Next = gInfoTimeOut.pFree.TimeOutArgs;
		pTOA->pFunc = 0;
		pTOA->Arg = 0;
		pTOA->Time = 0;
		pTOA->TimeoutId = 0;

		gInfoTimeOut.pFree.TimeOutArgs = pTOA;

		// update count

		gInfoTimeOut.Used--;
	}

	return;
}


unsigned long
curtime(  )
/*++

Description:

	This routine returns the current time in ticks.

Parameters:

	None.

Return Value:

	The current time in ticks as defined by the HZ constant.

--*/
{
	ulong Ticks;
	LARGE_INTEGER TicksNT;

	KeQueryTickCount(&TicksNT);

	Ticks= (ulong)((TicksNT.QuadPart * HZ)/gNTHZ);

	return (Ticks);
}



VOID
SohAllocFillStats(	IN PCLASSINFO		pClassInfo,
					IN PSOH_QUEUE_STATS pStats )
/*++

Description:

Fill in a stats object.

Arguments:

pClassInfo	- Pointer to the stats to get

pStats		- Pointer to the stats destination.

Return Value:

VOID.

Comments:


--*/

{

	DigiDump( DIGIFLOW, ( "Entering SohAllocFillStats:\n" ) );

	pStats->Min		= pClassInfo->Min;
	pStats->Max		= pClassInfo->Max;
	pStats->Used	= pClassInfo->Used;
	pStats->Hwm		= pClassInfo->Hwm;
	pStats->Free	= pClassInfo->Free;
	pStats->Fail	= pClassInfo->Fail;
	pStats->Size	= pClassInfo->AllocSize;

	pStats->Total = (pClassInfo->Used > pClassInfo->Min
		?	pStats->Size * pStats->Used
		:	pStats->Size * pStats->Min
	);

	DigiDump( DIGIFLOW, ( "Exiting SohAllocFillStats:\n" ) );

	return;

}

NTSTATUS
SohAllocGetStreams( IN PIRP pIrp
	)

/*++

Description:

This routine retrieves stats fro the Perfmon Streams Objects.

Arguments:

pIrp - Pointer to the IRP for this request.

Return Value:

Status.

Comments:


--*/

{
	PIO_STACK_LOCATION		pIrpStack;
	PSOH_GET_STREAMS		pStreams;
	NTSTATUS				Status = STATUS_SUCCESS;
	LONG					ulInformation = 0;
	int						i;

	DigiDump( DIGIFLOW, ( "Entering SohAllocGetStreams:\n" ) );

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );

	// verify output buffer
	if ( pIrpStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof( SOH_GET_STREAMS ) )
	{
		Status = STATUS_BUFFER_TOO_SMALL;
	}
	else
	{

		ulInformation	= pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
		pStreams		= ( PSOH_GET_STREAMS ) ( pIrp->AssociatedIrp.SystemBuffer );

		// Fill In the Data
		for (i=0;i < SOH_STREAMS_COUNT; i++) {

			switch (i + 1) 
			{
			case SOH_STREAMS_QPAIRS:
				SohAllocFillStats(&gInfoQPair,&pStreams->Stats[i]);
				break;
			case SOH_STREAMS_STREAMS:
				SohAllocFillStats(&gInfoStream,&pStreams->Stats[i]);
				break;
			case SOH_STREAMS_BUFCALL:
				SohAllocFillStats(&gInfoBufCall,&pStreams->Stats[i]);
				break;
			case SOH_STREAMS_TIMEOUT:
				SohAllocFillStats(&gInfoTimeOut,&pStreams->Stats[i]);
				break;
			case SOH_STREAMS_MSGBLKS:
				SohAllocFillStats(&gInfoMsgB,&pStreams->Stats[i]);
				break;
			default:
				ulInformation = 0;
				return STATUS_INVALID_PARAMETER;
				break;
			}

		}

	}

	// update status block and complete
	pIrp->IoStatus.Status		= Status;
	pIrp->IoStatus.Information	= ulInformation;

	IoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohAllocGetStreams\n" ) );

	return ( Status );

}							


NTSTATUS
SohAllocGetDataBlocks( IN PIRP pIrp
	)

/*++

Description:

This routine retrieves stats fro the Perfmon DataBlocks Objects.

Arguments:

pIrp - Pointer to the IRP for this request.

Return Value:

Status.

Comments:


--*/

{
	PIO_STACK_LOCATION		pIrpStack;
	PSOH_GET_DATABLOCKS		pDataBlocks;
	NTSTATUS				Status = STATUS_SUCCESS;
	LONG					ulInformation = 0;
	int						i;

	DigiDump( DIGIFLOW, ( "Entering SohAllocGetDataBlocks:\n" ) );

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );

	// verify output buffer
	if ( pIrpStack->Parameters.DeviceIoControl.OutputBufferLength != sizeof( SOH_GET_DATABLOCKS ) )
	{
		Status = STATUS_BUFFER_TOO_SMALL;
	}
	else
	{

		ulInformation	= pIrpStack->Parameters.DeviceIoControl.OutputBufferLength;
		pDataBlocks		= ( PSOH_GET_DATABLOCKS ) ( pIrp->AssociatedIrp.SystemBuffer );

		// Fill In the Data
		for (i=0;i < SOH_NDBCLASSES; i++) {

			SohAllocFillStats(&gaInfoDataB[i],&pDataBlocks->Stats[i]);

		}

	}

	// update status block and complete
	pIrp->IoStatus.Status		= Status;
	pIrp->IoStatus.Information	= ulInformation;

	IoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohAllocGetDataBlocks\n" ) );

	return ( Status );

}							

/////////////////////////////////////////////////////////////////////////////////
////////
////////  Debug Code
////////
/////////////////////////////////////////////////////////////////////////////////


#if DBG

int
DoNothing(caddr_t Dummy)
{
	return( 0 );
}

void
SohAllocTest( PSOHALLOCTABLE pAlloc,
			  PDRIVER_OBJECT pDirverObject
	)
{
	PMSGB pMsgB1;
	PMSGB pMsgB2;
	PMSGB pMsgB3;
	PMSGB pMsgB4;
	PMSGB pMsgB5;
	PQUEUE pQ1;
	PQUEUE pQ2;
	PQUEUE pQ3;
	PQUEUE pQ4;
	PSTREAM pS1;
	PSTREAM pS2;
	PSTREAM pS3;
	PSTREAM pS4;
	int Class;
	int Size;
	int Cnt;
	caddr_t Arg;
	int Ticks;
	int TimeOutId;

	///////////////////////////////////////////////////////////////////////////
	// set up the Allocation Table with small resource limits

	// QPairs
	pAlloc->QPairsMin		= 1;
	pAlloc->QPairsMax		= 3;

	// Streams
	pAlloc->StreamsMin		= 1;
	pAlloc->StreamsMax		= 3;

	// Msg Blocks
	pAlloc->MsgBsMin		= 1;
	pAlloc->MsgBsMax		= 4;

	// Priority Limits
	pAlloc->MedPriMsgLimit	= 25; 
	pAlloc->LoPriMsgLimit	= 50; 

	// Data Blocks
	for ( Class = 0, Size = SOH_DBSIZE_MIN;
		  Class < SOH_NDBCLASSES;
		  Class++, Size *= 2 )
	{

		pAlloc->aDBAlloc[Class].DataBsMin = 1;
		pAlloc->aDBAlloc[Class].DataBsMax = 3;
		pAlloc->aDBAlloc[Class].Size = Size;
	}
	pAlloc->aDBAlloc[SOH_NDBCLASSES-1].DataBsMin = 0; 
	pAlloc->aDBAlloc[SOH_NDBCLASSES-2].DataBsMin = 0;

	// BufCalls
	pAlloc->BufCallMin = 1;
	pAlloc->BufCallMax = 3;

	// Timeouts
	pAlloc->TimeoutMax = 3;

	// Log the Component resources	
	SohLogComponentUsage( pAlloc, pDirverObject );

	// Log the Data Block resources	
	SohLogDataBlockUsage( pAlloc, pDirverObject );

	///////////////////////////////////////////////////////////////////////////
	// just want to see what this is

	gNTHZ = (N100NS_PER_SEC/KeQueryTimeIncrement());

	///////////////////////////////////////////////////////////////////////////
	// allocate the streams resources

	SohAllocInit(pAlloc);

	///////////////////////////////////////////////////////////////////////////
	// test allocb and freeb

	// alloc too small

	Size = -1;
	pMsgB1 = allocb( Size, BPRI_HI ); // this should fail
	ASSERT(pMsgB1 == 0);

	// alloc too big

	Size = 9000;
	pMsgB1 = allocb( Size, BPRI_HI ); // this should fail
	ASSERT(pMsgB1 == 0);

	// alloc to limit and free all - check handling at limits 

	// Highest Message Size
	Size = 8000;

	pMsgB1 = allocb( Size, BPRI_HI );
	ASSERT(pMsgB1);
	bzero(pMsgB1->b_rptr,Size);

	pMsgB2 = allocb( Size, BPRI_HI );
	ASSERT(pMsgB2);
	bzero(pMsgB1->b_rptr,Size);

	pMsgB3 = allocb( Size, BPRI_HI );
	ASSERT(pMsgB3);
	bzero(pMsgB1->b_rptr,Size);

	pMsgB4 = allocb( Size, BPRI_HI ); // this should fail at data block
	ASSERT(pMsgB4 == 0);

	pMsgB4 = allocm( BPRI_HI ); 
	ASSERT(pMsgB4);
	pMsgB5 = allocm( BPRI_HI ); // this should fail at msg block
	ASSERT(pMsgB5 == 0);

	pMsgB4->b_datap = pMsgB3->b_datap; // link data block to test db_ref
	pMsgB4->b_datap->db_ref++;

	freeb( pMsgB1 );
	freeb( pMsgB2 );
	freeb( pMsgB3 );
	freeb( pMsgB4 );

	// alloc to min and free to test min limit
	Size = 2000;
	pMsgB1 = allocb( Size, BPRI_HI );
	ASSERT(pMsgB1);
	pMsgB2 = allocb( Size, BPRI_HI );
	ASSERT(pMsgB2);
	freeb( pMsgB2 );
	freeb( pMsgB1 );

	// alloc and free to test priority limits

	pMsgB1 = allocb( Size, BPRI_LO ); // this will alloc a buffer
	ASSERT(pMsgB1);
	pMsgB2 = allocb( Size, BPRI_MED ); // this will alloc a buffer
	ASSERT(pMsgB2);
	pMsgB3 = allocb( Size, BPRI_HI ); // this should grab a free buffer
	ASSERT(pMsgB3);

	freeb( pMsgB1 );
	freeb( pMsgB2 );
	freeb( pMsgB3 );

	// alloc and free to test using next class

	Size = 20;
	pMsgB1 = allocb( Size, BPRI_LO );
	ASSERT(pMsgB1);
	pMsgB2 = allocb( Size, BPRI_LO );
	ASSERT(pMsgB2);
	pMsgB3 = allocb( Size, BPRI_LO );
	ASSERT(pMsgB3);
	pMsgB4 = allocb( Size, BPRI_LO ); // this should use next class
	ASSERT(pMsgB4);

	freeb( pMsgB1 );
	freeb( pMsgB2 );
	freeb( pMsgB3 );
	freeb( pMsgB4 );

	///////////////////////////////////////////////////////////////////////////
	// test bufcall and runbcq

	// alloc to limit
	Size = 8000;

	pMsgB1 = allocb( Size, BPRI_HI ); // ensure a free msg block
	freeb( pMsgB1 );

	// call bufcall thrice to test priority ordering

	Arg = (caddr_t) 1;
	bufcall( Size, BPRI_HI, DoNothing, Arg ); // queues for data block
	Arg = (caddr_t) 2;
	bufcall( Size, BPRI_LO, DoNothing, Arg ); // queues for msg block
	Arg = (caddr_t) 3;
	bufcall( Size, BPRI_MED, DoNothing, Arg ); // queues for msg block

	//  alloc & free all

	pMsgB1 = allocb( Size, BPRI_HI );
	pMsgB2 = allocb( Size, BPRI_HI );
	pMsgB3 = allocb( Size, BPRI_HI );

	freeb( pMsgB1 );
	freeb( pMsgB2 );
	freeb( pMsgB3 );

	// call runbcq
	runbcq();

	// call bufcall again to test priority ordering and BCA free list operation

	Arg = (caddr_t) 1;
	bufcall( Size, BPRI_LO, DoNothing, Arg ); // queues for ??? block
	Arg = (caddr_t) 2;
	bufcall( Size, BPRI_HI, DoNothing, Arg ); // queues for ??? block
	Arg = (caddr_t) 3;
	bufcall( Size, BPRI_MED, DoNothing, Arg ); // queues for ??? block

	//  alloc & free all

	pMsgB1 = allocb( Size, BPRI_HI );
	pMsgB2 = allocb( Size, BPRI_HI );
	pMsgB3 = allocb( Size, BPRI_HI );

	freeb( pMsgB1 );
	freeb( pMsgB2 );
	freeb( pMsgB3 );

	// call runbcq
	runbcq();

	///////////////////////////////////////////////////////////////////////////
	// test allocq and freeq
	// alloc to limit and free all - check handling at limits 
	pQ1 = allocq();
	ASSERT(pQ1);
	pQ2 = allocq();
	ASSERT(pQ2);
	pQ3 = allocq();
	ASSERT(pQ3);
	pQ4 = allocq(); // this should fail
	ASSERT(pQ4 == 0);

	freeq( pQ1 );
	freeq( pQ2 );
	freeq( pQ3 );

	// alloc to min and free to test min limit
	pQ1 = allocq();
	ASSERT(pQ1);
	pQ2 = allocq();
	ASSERT(pQ2);

	freeq( pQ2 );
	freeq( pQ1 );


	///////////////////////////////////////////////////////////////////////////
	// test allocs and frees
	// alloc to limit and free all - check handling at limits 
	pS1 = allocs();
	ASSERT(pS1);
	pS2 = allocs();
	ASSERT(pS2);
	pS3 = allocs();
	ASSERT(pS3);
	pS4 = allocs(); // this should fail
	ASSERT(pS4 == 0);

	frees( pS1 );
	frees( pS2 );
	frees( pS3 );

	// alloc to min and free to test min limit
	pS1 = allocs();
	ASSERT(pS1);
	pS2 = allocs();
	ASSERT(pS2);

	frees( pS2 );
	frees( pS1 );


	///////////////////////////////////////////////////////////////////////////
	// test timeout, untimeout and runtoq

	// call timeout
	Arg = (caddr_t) 1;
	Ticks = 5;
	TimeOutId = timeout( DoNothing, Arg, Ticks );

	// call untimeout
	untimeout( TimeOutId );

	// call timeout thrice
	Arg = (caddr_t) 1;
	Ticks = 30;
	TimeOutId = timeout( DoNothing, Arg, Ticks );

	Arg = (caddr_t) 2;
	Ticks = 5;
	TimeOutId = timeout( DoNothing, Arg, Ticks );

	Arg = (caddr_t) 3;
	Ticks = 40;
	TimeOutId = timeout( DoNothing, Arg, Ticks );

	// call runtoq

	runtoq();


	///////////////////////////////////////////////////////////////////////////
	// Unload the Streams Resources

	SohAllocUnload();

}
#endif //DBG
