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

   sohioctl.c

Abstract:

   This module contains functions to support DSTREAMS and KME APIs. Does any
   necessary processing of the IOCTL's and returns control to the I/O subsystem.

Author:

   Ron Gredvig

Revision History:

   $History: sohioctl.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 "sohmsg.h"
#include "digisoh.h"
#include "dsioctl.h"
#include "digikme.h"
#include "sohioctl.h"
#include "sohdstrm.h"
#include "sohcmplt.h"

#include "digilib.h"

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

///////////////////////////////////////////////////////////////////////////////
//
// Internal Support Routine Prototypes
//
NTSTATUS
SohValidateRequest( IN PSOH_DEV_EXT pDevExt,
					IN ULONG_PTR StrmObjId,
					IN ULONG InputLength,
					IN ULONG ValidInputLength,
					IN ULONG OutputLength,
					IN ULONG ValidOutputLength,
					IN BOOLEAN OpenIoctl,
					IN BOOLEAN CloseIoctl );

NTSTATUS
SohValidateBuffers( IN PUCHAR pBuffer1,
					IN ULONG LengthBuffer1,
					IN PUCHAR pBuffer2,
					IN ULONG LengthBuffer2 );
///////////////////////////////////////////////////////////////////////////////
//
// Exported Routines
//

NTSTATUS
SohDsOpen( IN PDEVICE_OBJECT pDev,
		   IN PIRP pIrp )
/*++

Description:

This routine opens a Stream.

Arguments:

pDev - Pointer to the device object.

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING or an error status.

Comments:

Create a Stream Object, find and claim unused handle (index into array!).

Initialize Stream Object: pOpenIrp = pIrp, State = OPENING, hStream = handle

Add Stream object to Device Object’s open list.

Mark IRP pending and return STATUS PENDING.

Relies on SohIrpCompleter thread to complete IRP.

--*/
{
	PIO_STACK_LOCATION	pIrpStack;
	PSOH_DEV_EXT		pDevExt;
	PSOH_STREAM_OBJECT	pStreamObj;
	PDS_OPEN			pDsOpen;
	NTSTATUS			Status;
	KIRQL				DevKirql;
	ULONG				StrmObjId;


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

	// initialize variables
	pStreamObj = 0;
	pDevExt = pDev->DeviceExtension;
	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters
	Status = SohValidateRequest( pDevExt,
								 0,
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_OPEN ),
					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
								 sizeof( DS_OPEN ),
								 TRUE,
								 FALSE
								 );

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	pDsOpen = ( PDS_OPEN ) ( pIrp->AssociatedIrp.SystemBuffer );

	if ( *pDsOpen->DeviceName == 0 )
	{
		//
		// If the input string is NULL fail the open request.
		//
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	if ( *( pDsOpen->DeviceName + DS_DEVICENAME_SIZE - 1 ) != 0 )
	{
		//
		// If the input string is not NULL terminated fail the open request.
		//
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	// create a Stream Object

	pStreamObj = DigiAllocMem( NonPagedPool, sizeof( SOH_STREAM_OBJECT ) );

	if ( !pStreamObj )
	{
		goto ReturnError;
	}

	RtlZeroMemory( pStreamObj, sizeof( SOH_STREAM_OBJECT ) );

	// claim an unused handle for this Stream

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &DevKirql );

	for ( StrmObjId = 1; StrmObjId < gulMaxStreams; StrmObjId++ )
	{
		if ( gapStreamObj[StrmObjId] == ( PSOH_STREAM_OBJECT ) NULL )
		{

			break;
		}
	}

	if ( ( StrmObjId == gulMaxStreams ) || gulMaxStreams <= 0 || !pStreamObj )
	{
		// error processing - ran out of streams
		//
		// We can't allocate needed resources!
		//
		KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

		DigiDump( DIGIERRORS, ( "SOH: Could not allocate Stream Object!\n" ) );

		Status = STATUS_INSUFFICIENT_RESOURCES;

		goto ReturnError;
	}

	gapStreamObj[StrmObjId] = pStreamObj;
	gSohRuntimeEnv.StreamObjectCount++;

	// initialize Stream Object

	pStreamObj->Type = SOH_OBJECT_TYPE_STREAM;
	pStreamObj->pDevExt = pDev->DeviceExtension;

	KeInitializeSpinLock( &pStreamObj->SpinLock );

	KeInitializeEvent( &pStreamObj->evCleanup, SynchronizationEvent, FALSE );
	pStreamObj->ProcessState	= SOH_PROCESS_STATE_IDLE;
	pStreamObj->CancelState		= SOH_PROCESS_STATE_IDLE;

	pStreamObj->hObjectId = ( HANDLE ) StrmObjId;
	pStreamObj->pOriginalFileObject = pIrp->Tail.Overlay.OriginalFileObject;
	pStreamObj->pOpenIrp = pIrp;  
	pStreamObj->State = SOH_STREAM_STATE_OPENING;

	InitializeListHead( &pStreamObj->PendingPutmsgListHead );
	InitializeListHead( &pStreamObj->CompletedPutmsgListHead );
	InitializeListHead( &pStreamObj->PendingGetmsgListHead );
	InitializeListHead( &pStreamObj->CompletedGetmsgListHead );
	InitializeListHead( &pStreamObj->PurgeListHead );

	// mark as pending

	IoMarkIrpPending( pIrp );


	// add handle to device object's active stream list

	InsertTailList( &gSohRuntimeEnv.StreamObjectListHead,
					&pStreamObj->ListEntry
				  );

	
	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

	// set event for Stream thread
	KeSetEvent( &gSohRuntimeEnv.evStreams,
				0,
				FALSE );

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

	return ( STATUS_PENDING );

ReturnError:

	if ( pStreamObj )
	{
		DigiFreeMem( pStreamObj );
	}

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

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsOpen with error:\n" ) );

	return ( Status );
}								   // end SohDsOpen

NTSTATUS
SohDsClose( IN PDEVICE_OBJECT pDev,
			IN PIRP pIrp )
/*++

Description:

	This routine closes a Stream.

Arguments:

	pDev - Pointer to the device object

	pIrp - Pointer to the IRP for this request.

Return Value:

	STATUS_PENDING.

Comments:

	Use hStream to obtain Stream Object (index into array!).

	Update Stream Object: pCloseIrp = pIrp, State = CLOSING

	SohStreams or SohIrpCompleter may be processing a Stream when it's state
	changes. That is OK. The next time the Streams gets processed it will
	be processed for close.

	Mark IRP pending and return STATUS_PENDING.

	Relies on SohIrpCompleter thread to complete IRP.

	Any pending requests are cancelled!!!

	Outstanding IRPs completed by SohIrpCompleter.

--*/
{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT     pDevExt;
	PSOH_STREAM_OBJECT pStreamObj;
	ULONG_PTR          iStream;
	PDS_CLOSE       pDsClose;
	KIRQL           DevKirql;
	NTSTATUS        Status;

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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );

	// validate input parameters
	Status = SohValidateRequest( pDevExt,
							 *( PULONG ) ( pIrp->AssociatedIrp.SystemBuffer ),
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_CLOSE ),
								 0,
								 0,
								 FALSE,
								 TRUE);

	if ( !NT_SUCCESS( Status ) )
	{
		// This ioctl does not use an output buffer so ignore this error
		if ( Status != STATUS_BUFFER_TOO_SMALL )
		{
			goto ReturnError;
		}
	}

	pDsClose = (PDS_CLOSE)( pIrp->AssociatedIrp.SystemBuffer );
	iStream = (ULONG_PTR)pDsClose->hStream;
	pStreamObj = gapStreamObj[iStream];

	// mark as pending

	IoMarkIrpPending( pIrp );

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &DevKirql );

	// mark Stream as closing
	// Dstream thread and completer thread will handle any pending Irps

	pStreamObj->pCloseIrp = pIrp;
	pStreamObj->StateBeforeTheClose = pStreamObj->State;
	pStreamObj->State = SOH_STREAM_STATE_CLOSING;

	// do anything for a Stream that was linked ???

	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

	// set event for Stream thread
	KeSetEvent( &gSohRuntimeEnv.evStreams,
				0,
				FALSE );

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

	return ( STATUS_PENDING );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsClose with error:\n" ) );

	return ( Status );
}								   // end SohDsClose



NTSTATUS
SohDsIoctl( IN PDEVICE_OBJECT pDev,
			IN PIRP pIrp )
/*++

Description:

This routine presents IOCTL requests to the DSTREAM subsystem.

Arguments:

pDev - Pointer to the device object.

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING.

Comments:

Determine if request is from kernel or user mode. ???

Use hStream to obtain Stream Object (index into array!).

Update Stream Object: pIoctlIrp = pIrp

Mark IRP pending and return STATUS_PENDING.

Relies on SohIrpCompleter thread to complete IRP.

--*/
{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PSOH_STREAM_OBJECT pStreamObj;
	PDS_IOCTL       pDsIoctl;
	KIRQL           DevKirql;
	ULONG_PTR       iStream;
	NTSTATUS        Status;


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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters
	Status = SohValidateRequest( pDevExt,
							 *( PULONG ) ( pIrp->AssociatedIrp.SystemBuffer ),
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_IOCTL ),
					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
								 sizeof( DS_IOCTL ),
								 FALSE,
								 FALSE);

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	pDsIoctl = ( PDS_IOCTL ) ( pIrp->AssociatedIrp.SystemBuffer );

	// validate buffer parameters
	Status = SohValidateBuffers( pDsIoctl->pDataIn,
								 pDsIoctl->cbDataIn,
								 0,
								 0 );

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	iStream = (ULONG_PTR)pDsIoctl->hStream;
	pStreamObj = gSohRuntimeEnv.apStreamObj[iStream];


	// check for a current Ioctl IRP ???

	//
	// Lock data from user mode requests
	//
	if ( pIrp->RequestorMode == UserMode )
	{
		if( pDsIoctl->pDataIn )
		{
			Status = DigiLockBuffer( pIrp,
					                 pDsIoctl->pDataIn,
								     pDsIoctl->cbDataIn,
								     &pDsIoctl->pProviderDataIn);

			if ( Status != STATUS_SUCCESS )
				goto ReturnError;
		}
	}
	else
	{
		pDsIoctl->pProviderDataIn = pDsIoctl->pDataIn;
	}

	// mark as pending

	IoMarkIrpPending( pIrp );

	// mark ioctl as queued

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &DevKirql );

	pStreamObj->pIoctlIrp = pIrp;
	pStreamObj->IoctlState = IoctlQueuedState;

	// ??? cancel routine ???

	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

	// set event for Stream thread
	KeSetEvent( &gSohRuntimeEnv.evStreams,
				0,
				FALSE );

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

	return ( STATUS_PENDING );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsIoctl with error:\n" ) );

	return ( Status );
}								   // end SohDsIoctl


NTSTATUS
SohDsPush( IN PDEVICE_OBJECT pDev,
		   IN PIRP pIrp )
/*++

Description:

This routine presents push requests to the DSTREAM subsystem.

Arguments:

pDev - Pointer to the device object.

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING.

Comments:

Determine if request is from kernel or user mode. ???

Use hStream to obtain Stream Object (index into array!).

Update Stream Object: pPushIrp = pIrp

Mark IRP pending and return STATUS_PENDING.

Relies on SohIrpCompleter thread to complete IRP.

--*/
{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PSOH_STREAM_OBJECT pStreamObj;
	PDS_PUSH        pDsPush;
	KIRQL           DevKirql;
	NTSTATUS        Status;
	ULONG_PTR       iStream;


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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters
	Status = SohValidateRequest( pDevExt,
							 *( PULONG ) ( pIrp->AssociatedIrp.SystemBuffer ),
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_PUSH ),
					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
								 sizeof( DS_PUSH ),
								 FALSE,
								 FALSE);

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	pDsPush = ( PDS_PUSH ) ( pIrp->AssociatedIrp.SystemBuffer );
	iStream = (ULONG_PTR)pDsPush->hStream;
	pStreamObj = gSohRuntimeEnv.apStreamObj[iStream];


	if ( *( ( ( PCHAR ) pDsPush->ModuleName ) + DS_DEVICENAME_SIZE ) != 0 )
	{
		//
		// If the input string is not NULL terminated fail the open request.
		//
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	// mark as pending

	IoMarkIrpPending( pIrp );


	// check for a current push IRP ???


	// log IRP in Stream object

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &DevKirql );

	pStreamObj->PushState = PushQueuedState;
	pStreamObj->pPushIrp = pIrp;

	// ??? cancel routine ???

	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

	// set event for Stream thread
	KeSetEvent( &gSohRuntimeEnv.evStreams,
				0,
				FALSE );

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

	return ( STATUS_PENDING );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsPush with error:\n" ) );

	return ( Status );
}								   // end SohDsPush


NTSTATUS
SohDsLink( IN PDEVICE_OBJECT pDev,
		   IN PIRP pIrp )
/*++

Description:

This routine presents link requests to the DSTREAM subsystem.

Arguments:

pDev - Pointer to the device object.

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING.

Comments:

Determine if request is from kernel or user mode. ???

Use hStream to obtain Stream Object (index into array!).

Update Stream Object: pLinkIrp = pIrp

Mark IRP pending and return STATUS_PENDING.

Relies on SohIrpCompleter thread to complete IRP.

--*/
{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PSOH_STREAM_OBJECT pUpperStreamObj;
	PDS_LINK        pDsLink;
	ULONG_PTR       iUpperStream;
	ULONG_PTR       iLowerStream;
	KIRQL           DevKirql;
	NTSTATUS        Status;


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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters
	Status = SohValidateRequest( pDevExt,
							 *( PULONG ) ( pIrp->AssociatedIrp.SystemBuffer ),
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_LINK ),
					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
								 sizeof( DS_LINK ),
								 FALSE,
								 FALSE);

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	pDsLink = ( PDS_LINK ) ( pIrp->AssociatedIrp.SystemBuffer );
	iUpperStream = (ULONG_PTR)pDsLink->hStreamUpper;
	iLowerStream = (ULONG_PTR)pDsLink->hStreamLower;
	pUpperStreamObj = gSohRuntimeEnv.apStreamObj[iUpperStream];

	Status = SohValidateRequest( pDevExt,
								 iLowerStream,
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_LINK ),
					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
								 sizeof( DS_LINK ),
								 FALSE,
								 FALSE);

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}


	// check for a current link IRP ???


	// mark as pending

	IoMarkIrpPending( pIrp );


	// indicate IRP is queued

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &DevKirql );

	pUpperStreamObj->LinkState = LinkQueuedState;
	pUpperStreamObj->pLinkIrp = pIrp;

	// ??? cancel Irp ???

	// do anything for a Stream that was linked ???

	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

	// set event for Stream thread
	KeSetEvent( &gSohRuntimeEnv.evStreams,
				0,
				FALSE );

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

	return ( STATUS_PENDING );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsLink with error:\n" ) );

	return ( Status );
}								   // end SohDsLink



NTSTATUS
SohDsPutmsg( IN PDEVICE_OBJECT pDev,
			 IN PIRP pIrp )
/*++

Description:

This routine presents STREAMS PUTMSG requests to STREAMS.

Arguments:

pDev - Pointer to the device object

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING.

Comments:

User mode ???

Use hStream to obtain Stream Object (index into array!).

Update Stream Object: add pIrp to pending putmsg list

Mark IRP pending and return STATUS_PENDING.

Relies on SohIrpCompleter thread to complete IRP.

--*/
{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PSOH_STREAM_OBJECT pStreamObj;
	PDS_PUTMSG      pDsPutmsg;
	ULONG_PTR       iStream;
	NTSTATUS        Status;


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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters
	Status = SohValidateRequest( pDevExt,
								 *( PULONG ) ( pIrp->AssociatedIrp.SystemBuffer ),
								 pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_PUTMSG ),
								 0,
								 0,
								 FALSE,
								 FALSE);

	if ( !NT_SUCCESS( Status ) )
	{
		if ( Status != STATUS_BUFFER_TOO_SMALL )
		{
			// This ioctl does not use an output buffer so ignore this error
			goto ReturnError;
		}
	}

	pDsPutmsg = ( PDS_PUTMSG ) ( pIrp->AssociatedIrp.SystemBuffer );

	// validate buffer parameters
	if ( !( pDsPutmsg->pControl || pDsPutmsg->pData ) )
	{
		// error if both are zero
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	Status = SohValidateBuffers( pDsPutmsg->pControl,
								 pDsPutmsg->cbControl,
								 pDsPutmsg->pData,
								 pDsPutmsg->cbData);

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	iStream = (ULONG_PTR)pDsPutmsg->hStream;
	pStreamObj = gSohRuntimeEnv.apStreamObj[iStream];

	//
	// Lock data from user mode requests
	//
	if ( pIrp->RequestorMode == UserMode )
	{
		if( pDsPutmsg->pControl )
		{
			Status = DigiLockBuffer( pIrp,
				                     pDsPutmsg->pControl,
								     pDsPutmsg->cbControl,
								     &pDsPutmsg->pProviderControl);

			if ( Status != STATUS_SUCCESS )
				goto ReturnError;
		}

		if( pDsPutmsg->pData )
		{
			Status = DigiLockBuffer( pIrp,
									 pDsPutmsg->pData,
								     pDsPutmsg->cbData,
								     &pDsPutmsg->pProviderData );

			if ( Status != STATUS_SUCCESS )
				goto ReturnError;
		}
	}
	else
	{
		pDsPutmsg->pProviderControl = pDsPutmsg->pControl;
		pDsPutmsg->pProviderData = pDsPutmsg->pData;
	}

	// mark as pending

	IoMarkIrpPending( pIrp );

	// set cancel routine and put on list

	Status = SohQueueMsg( pIrp,
						  &gSohRuntimeEnv.SpinLock,
						  &pStreamObj->PendingPutmsgListHead,
						  SohCancelPutGetMsg );

	if ( Status != STATUS_SUCCESS )
	{
		goto ReturnError;
	}

	// set event for Stream thread
	KeSetEvent( &gSohRuntimeEnv.evStreams,
				0,
				FALSE );

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

	return ( STATUS_PENDING );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsPutmsg with error:\n" ) );

	return ( Status );
}								   // end SohDsPutmsg



NTSTATUS
SohDsGetmsg( IN PDEVICE_OBJECT pDev,
			 IN PIRP pIrp )
/*++

Description:

This routine retrieves upbound messages from STREAMS.

Arguments:

pDev - Pointer to hte device object

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING.

Comments:

User mode ???

Use hStream to obtain Stream Object (index into array!).

Update Stream Object: add pIrp to pending getmsg list

Mark IRP pending and return STATUS_PENDING.

Relies on SohIrpCompleter thread to complete IRP.

--*/

{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PSOH_STREAM_OBJECT pStreamObj;
	PDS_GETMSG      pDsGetmsg;
	ULONG_PTR       iStream;
	NTSTATUS        Status;


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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters
	Status = SohValidateRequest( pDevExt,
							 *( PULONG ) ( pIrp->AssociatedIrp.SystemBuffer ),
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_GETMSG ),
					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
								 sizeof( DS_GETMSG ),
								 FALSE,
								 FALSE);

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	pDsGetmsg = ( PDS_GETMSG ) ( pIrp->AssociatedIrp.SystemBuffer );

	// validate buffer parameters
	if ( ( pDsGetmsg->pControl == NULL) && (pDsGetmsg->pData == NULL ) )
	{
		// error if both are zero
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	Status = SohValidateBuffers( pDsGetmsg->pControl,
								 pDsGetmsg->cbMaxControl,
								 pDsGetmsg->pData,
								 pDsGetmsg->cbMaxData );

	if ( !NT_SUCCESS( Status ) )
	{
		goto ReturnError;
	}

	iStream = (ULONG_PTR)pDsGetmsg->hStream;
	pStreamObj = gSohRuntimeEnv.apStreamObj[iStream];

	//
	// Lock data from user mode requests
	//
	if ( pIrp->RequestorMode == UserMode )
	{

		if (pDsGetmsg->pControl)
		{
			Status = DigiLockBuffer( pIrp,
									 pDsGetmsg->pControl,
									 pDsGetmsg->cbMaxControl,
									 &pDsGetmsg->pProviderControl);

			if ( Status != STATUS_SUCCESS )
			{
				goto ReturnError;
			}
		}

		if (pDsGetmsg->pData)
		{
			Status = DigiLockBuffer( pIrp,
									 pDsGetmsg->pData,
									 pDsGetmsg->cbMaxData,
									 &pDsGetmsg->pProviderData);

			if ( Status != STATUS_SUCCESS )
			{
				goto ReturnError;
			}
		}
	}
	else
	{
		pDsGetmsg->pProviderControl = pDsGetmsg->pControl;
		pDsGetmsg->pProviderData = pDsGetmsg->pData;
	}

	// mark as pending
	IoMarkIrpPending( pIrp );

	// set cancel routine and put on list

	Status = SohQueueMsg( pIrp,
						  &gSohRuntimeEnv.SpinLock,
						  &pStreamObj->PendingGetmsgListHead,
						  SohCancelPutGetMsg );

	if ( Status != STATUS_SUCCESS )
	{
		goto ReturnError;
	}

	// set event for Stream thread

	KeSetEvent( &gSohRuntimeEnv.evStreams,
				0,
				FALSE );

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

	return ( STATUS_PENDING );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsGetmsg with error:\n" ) );

	return ( Status );
}								   // end SohDsGetmsg



NTSTATUS
SohDsPurge( IN PDEVICE_OBJECT pDev,
			IN PIRP pIrp )
/*++

Description:

	This routine purges pending STREAMS messages on a DSTREAM subsystem for a
	given Stream. Messages are completed in order and then this Irp is
	completed.

Arguments:

	pDev - Pointer to the device object.

	pIrp - Pointer to the IRP for this request.

Return Value:

	STATUS_PENDING.

Comments:

	This routine assumes purge completes before close is called, therefore
	it does not protect against the Stream closing. This could be a bad thing.

--*/

{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PSOH_STREAM_OBJECT pStreamObj;
	PDS_PURGE       pDsPurge;
	ULONG_PTR       iStream;
	KIRQL           DevKirql;
	KIRQL           CancelKirql;
	PIRP            pTmpIrp;
	PLIST_ENTRY     pListEntry;
	NTSTATUS        Status;
	PDRIVER_CANCEL  pOldCancelRoutine;

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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters
	Status = SohValidateRequest( pDevExt,
							 *( PULONG ) ( pIrp->AssociatedIrp.SystemBuffer ),
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
								 sizeof( DS_PURGE ),
								 0,
								 0,
								 FALSE,
								 FALSE);

	if ( !NT_SUCCESS( Status ) )
	{
		// This ioctl does not use an output buffer so ignore this error
		if ( Status != STATUS_BUFFER_TOO_SMALL )
		{
			goto ReturnError;
		}
	}

	pDsPurge = ( PDS_PURGE ) ( pIrp->AssociatedIrp.SystemBuffer );
	iStream = (ULONG_PTR)pDsPurge->hStream;
	pStreamObj = gSohRuntimeEnv.apStreamObj[iStream];


	// this request cannot be canceled


	// purge lists

	// We need to make sure Irp's cannot be cancelled while we are purging
	// them
	IoAcquireCancelSpinLock( &CancelKirql );

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &DevKirql );

	if ( pDsPurge->Flags & DS_PURGE_PUTMSG )
	{
		// completed putmsgs are removed
		while ( !( IsListEmpty( &pStreamObj->CompletedPutmsgListHead ) ) )
		{
			pListEntry = RemoveHeadList( &pStreamObj->CompletedPutmsgListHead );

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

			// Set the Cancel Routine
			IoSetCancelRoutine( pTmpIrp, NULL );

			// we have the Irp and it is no longer cancelable
			// put it on the purge list

			pTmpIrp->IoStatus.Status = STATUS_CANCELLED;
			pTmpIrp->IoStatus.Information = 0;

			InsertTailList( &pStreamObj->PurgeListHead, pListEntry );
		}

		// pending putmsg's are removed
		while ( !( IsListEmpty( &pStreamObj->PendingPutmsgListHead ) ) )
		{
			pListEntry = RemoveHeadList( &pStreamObj->PendingPutmsgListHead );

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

			// Set the Cancel Routine
			IoSetCancelRoutine( pTmpIrp, NULL );

			// we have the Irp and it is no longer cancelable
			// put it on the purge list

			pTmpIrp->IoStatus.Status = STATUS_CANCELLED;
			pTmpIrp->IoStatus.Information = 0;

			InsertTailList( &pStreamObj->PurgeListHead, pListEntry );
		}
	}

	if ( pDsPurge->Flags & DS_PURGE_GETMSG )
	{
		// completed getmsgs are removed
		while ( !( IsListEmpty( &pStreamObj->CompletedGetmsgListHead ) ) )
		{

			pListEntry = RemoveHeadList( &pStreamObj->CompletedGetmsgListHead );

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

			// Reset the Cancel Routine
			IoSetCancelRoutine( pTmpIrp, NULL );

			// otherwise, we have the Irp and it is no longer cancelable
			pTmpIrp->IoStatus.Status = STATUS_CANCELLED;
			pTmpIrp->IoStatus.Information = 0;

			InsertTailList( &pStreamObj->PurgeListHead, pListEntry );
		}

		// pending getmsg's are removed
		while ( !( IsListEmpty( &pStreamObj->PendingGetmsgListHead ) ) )
		{

			pListEntry = RemoveHeadList( &pStreamObj->PendingGetmsgListHead );

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

			// Reset the Cancel Routine
			IoSetCancelRoutine( pTmpIrp, NULL );

			// otherwise, we have the Irp and it is no longer cancelable
			pTmpIrp->IoStatus.Status = STATUS_CANCELLED;
			pTmpIrp->IoStatus.Information = 0;

			InsertTailList( &pStreamObj->PurgeListHead, pListEntry );
		}
	}

	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

	// The cancel routine has been set to NULL
	IoReleaseCancelSpinLock( CancelKirql );

	// complete purged irps
	while ( !( IsListEmpty( &pStreamObj->PurgeListHead ) ) ) // Irps to purge
	{
		// Remove IRP from list

		pListEntry = RemoveHeadList( &pStreamObj->PurgeListHead );

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

		SohIoCompleteRequest( pTmpIrp, IO_NO_INCREMENT );
	}


	// complete this irp

	Status = STATUS_SUCCESS;

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

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

	return ( Status );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohDsPurge with error:\n" ) );

	return ( Status );
}								   // end SohDsPurge



NTSTATUS
SohKmeRead( IN PDEVICE_OBJECT pDev,
			IN PIRP pIrp )
/*++

Description:

This routine reads KME data.

Arguments:

pDev - Pointer to the device object.

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING.

Comments:

User mode ???

Use hStream to obtain Stream Object (index into array!).


Mark IRP pending and return STATUS_PENDING.

Relies on SohIrpCompleter thread to complete IRP.

--*/

{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PKME_READ       pKmeRead;
	PVOID           pvSource;
	PVOID           pvDestination;
	ULONG           Length;
	NTSTATUS        Status;


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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters

	DigiDump( DIGIVERYLOUD, ( "   InputBufferLength = %d, sizeof(DS_GETMSG) = %s\n,"
					   "   OutputBufferLength = %d, sizeof(DS_GETMSG) = %d\n",
					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
						  sizeof( DS_GETMSG ),
					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
						  sizeof( DS_GETMSG ) ) );



	if ( pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof( KME_READ ) )
	{
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	pKmeRead = ( PKME_READ ) ( pIrp->AssociatedIrp.SystemBuffer );

	if ( pKmeRead->Flags || !pKmeRead->Length )
	{
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	if ( pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < pKmeRead->Length )
	{
		Status = STATUS_BUFFER_TOO_SMALL;

		goto ReturnError;
	}


	//
	// Lock data from user mode requests ???
	//
	if ( pIrp->RequestorMode == UserMode )
	{
		// call DigiLockBuffer
	}
	else
	{
		// don't
	}


	pvSource = ( PVOID ) pKmeRead->Address;
	pvDestination = ( PVOID ) pKmeRead;
	Length = pKmeRead->Length;

	RtlCopyMemory( pvDestination, pvSource, Length );

	Status = STATUS_SUCCESS;

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = Length;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

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

	return ( Status );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohkmeRead with error:\n" ) );

	return ( Status );
}								   // end SohKmeRead



NTSTATUS
SohKmeWrite( IN PDEVICE_OBJECT pDev,
			 IN PIRP pIrp )
/*++

Description:

This routine writes KME data.

Arguments:

pDev - Pointer to the device object.

pIrp - Pointer to the IRP for this request.

Return Value:

STATUS_PENDING.

Comments:

User mode ???



--*/

{
	PIO_STACK_LOCATION pIrpStack;
	PSOH_DEV_EXT    pDevExt;
	PKME_WRITE      pKmeWrite;
	PVOID           pvSource;
	PVOID           pvDestination;
	ULONG           Length;
	NTSTATUS        Status;


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

	pDevExt = pDev->DeviceExtension;

	pIrpStack = IoGetCurrentIrpStackLocation( pIrp );


	// validate input parameters

//	DigiDump( DIGIVERYLOUD, ( "   InputBufferLength = %d, sizeof(SXB_KME_WRITE) = %s\n,"
//				   "   OutputBufferLength = %d, sizeof(SXB_KME_WRITE) = %d\n",
//					  pIrpStack->Parameters.DeviceIoControl.InputBufferLength,
//						   sizeof( SXB_KME_WRITE ),
//					 pIrpStack->Parameters.DeviceIoControl.OutputBufferLength,
//						   sizeof( SXB_KME_WRITE ) ) );



	if ( pIrpStack->Parameters.DeviceIoControl.InputBufferLength < sizeof( KME_WRITE ) )
	{
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	pKmeWrite = ( PKME_WRITE ) ( pIrp->AssociatedIrp.SystemBuffer );

	if ( pKmeWrite->Flags || !pKmeWrite->Length )
	{
		Status = STATUS_INVALID_PARAMETER;

		goto ReturnError;
	}

	if ( pIrpStack->Parameters.DeviceIoControl.OutputBufferLength < ( sizeof( KME_WRITE ) + pKmeWrite->Length ) )

	{
		Status = STATUS_BUFFER_TOO_SMALL;

		goto ReturnError;
	}


	//
	// Lock data from user mode requests ???
	//
	if ( pIrp->RequestorMode == UserMode )
	{
		// call DigiLockBuffer
	}
	else
	{
		// don't
	}


	pvSource = ( PVOID ) ( pKmeWrite + 1 );	// by convention
	pvDestination = ( PVOID ) pKmeWrite->Address;
	Length = pKmeWrite->Length;

	RtlCopyMemory( pvDestination, pvSource, Length );

	Status = STATUS_SUCCESS;

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = Length;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

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

	return ( Status );

ReturnError:

	// update status block and complete

	pIrp->IoStatus.Status = Status;
	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	DigiDump( DIGIFLOW, ( "Exiting SohkmeWrite with error:\n" ) );

	return ( Status );
}								   // end SohKmeWrite

NTSTATUS
SohQueueMsg( IN PIRP pIrp,
			 IN PKSPIN_LOCK pSpinLock,
			 IN PLIST_ENTRY pListHead,
			 IN PDRIVER_CANCEL pCancelRoutine )
/*++

Description:

	This routine queues an IRP to the tail of a list for later processing.

Parameters:

	pIrp - a pointer to the Irp to requeue.
	pSpinLock - a pointer to the spin lock to hold while accessing lists
	pListHead- a pointer to the list to put the Irp on
	pCancelRoutine - a pointer to the cancel routine for the Irp

Return Value:

	STATUS_SUCCESS if the Irp was queued.
	STATUS_CANCELLED if the Irp should be cancelled.

--*/
{
	KIRQL           Kirql;
	KIRQL           CancelKirql;
	NTSTATUS        Status;
	PDRIVER_CANCEL  pOldCancelRoutine;

	Status = STATUS_SUCCESS;

	IoAcquireCancelSpinLock( &CancelKirql );	// hold lock until done

	// If the Irp has already being cancelled then skip it.
	if (!pIrp->Cancel)
	{

		// Set the Cancel routine
		IoSetCancelRoutine( pIrp, pCancelRoutine );

		KeAcquireSpinLock( pSpinLock, &Kirql );

		InsertTailList( pListHead, &pIrp->Tail.Overlay.ListEntry );

		KeReleaseSpinLock( pSpinLock, Kirql );

	}

	// The Irp is now queued, with a cancel routine set
	IoReleaseCancelSpinLock( CancelKirql );

	return ( Status );
}								   // end SohQueueMsg


PIRP
SohDequeueMsg( IN PKSPIN_LOCK pSpinLock,
			   IN PLIST_ENTRY pListHead )
/*++

Description:

	This routine dequeues an Irp from the head of a list. Irps with cancel
	pending are moved to the cancel list.
	
Parameters:

	pSpinLock - a pointer to the spin lock to hold while accessing lists
	pListHead- a pointer to the list to remove the Irp from

Return Value:

	a pointer to the IRP if one is available.
	NULL if no Irps are queued or Irp is being cancelled.

--*/
{
	PIRP            pIrp;
	KIRQL           CancelKirql;
	KIRQL           Kirql;
	PLIST_ENTRY     pListEntry;
	PDRIVER_CANCEL  pOldCancelRoutine;

	pIrp = NULL;

	// Need to test if the irp has already been cancelled
	IoAcquireCancelSpinLock( &CancelKirql );

	if (pSpinLock)
	{
		KeAcquireSpinLock( pSpinLock, &Kirql );
	}

	while ( !( IsListEmpty( pListHead ) ) )
	{
		pListEntry = RemoveHeadList( pListHead );

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

		// If the Irp is being cancelled then skip it.
		if (!pIrp->Cancel)
		{

			// this Irp can no longer be cancelled we will complete it
			IoSetCancelRoutine( pIrp, NULL );

			break;
		}
	}

	if (pSpinLock)
	{
		KeReleaseSpinLock( pSpinLock, Kirql );
	}

	// No Longer Needed
	IoReleaseCancelSpinLock( CancelKirql );

	return ( pIrp );
}								   // end SohDequeueMsg

VOID
SohRequeueMsg( IN PIRP pIrp,
			   IN PKSPIN_LOCK pSpinLock,
			   IN PLIST_ENTRY pListHead,
			   IN PDRIVER_CANCEL pCancelRoutine )
/*++

Description:

	This routine requeues an IRP to the head of a list for later processing.

Parameters:

	pIrp - a pointer to the Irp to requeue.
	pSpinLock - a pointer to the spin lock to hold while accessing lists
	pListHeadPending - a pointer to the list to put the Irp back on
	pCancelRoutine - a pointer to the cancel routine for the Irp

Return Value:

	STATUS_SUCCESS if the Irp was queued.
	STATUS_CANCELLED if the Irp should be cancelled.

--*/
{
	KIRQL           CancelKirql;
	KIRQL           Kirql;
	PDRIVER_CANCEL  pOldCancelRoutine;

	// Need until the cancel routine is set and the IRP is Queued
	IoAcquireCancelSpinLock( &CancelKirql );

	if (pSpinLock)
	{
		KeAcquireSpinLock( pSpinLock, &Kirql );
	}

	// Set the cancel routine
	IoSetCancelRoutine( pIrp, pCancelRoutine );

	InsertHeadList( pListHead, &pIrp->Tail.Overlay.ListEntry );

	if (pSpinLock)
	{
		KeReleaseSpinLock( pSpinLock, Kirql );
	}

	// The Irp is now queued with the cancel routine set
	IoReleaseCancelSpinLock( CancelKirql );

	return;
}								   // end SohRequeueMsg


VOID
SohCancelPutGetMsg( IN PDEVICE_OBJECT pDevObj,
					IN PIRP pIrp )
/*++

Description:

	This routine cancels an Irp on the putmsg or getmsg queue.

Parameters:

	pDevObj - a pointer to the device object the Irp is queued on.
	pIrp - a pointer to the Irp to cancel.

Return Value:

	None

--*/
{
	KIRQL           Kirql;
	PSOH_DEV_EXT    pDevExt;

	pDevExt = ( PSOH_DEV_EXT ) pDevObj->DeviceExtension;

	// get the lock for this list, get the entry and release the lock

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &Kirql );

	RemoveEntryList( &pIrp->Tail.Overlay.ListEntry );

	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, Kirql );

	// release the cancel spin lock

	IoReleaseCancelSpinLock( pIrp->CancelIrql );

	// complete the Irp

	pIrp->IoStatus.Status = STATUS_CANCELLED;

	pIrp->IoStatus.Information = 0;

	SohIoCompleteRequest( pIrp, IO_NO_INCREMENT );

	return;
}								   // end SohCancelPutGetMsg

///////////////////////////////////////////////////////////////////////////////
//
// Internal Support Routines
//

NTSTATUS
SohValidateRequest( IN PSOH_DEV_EXT pDevExt,
					IN ULONG_PTR StrmObjId,
					IN ULONG InputLength,
					IN ULONG ValidInputLength,
					IN ULONG OutputLength,
					IN ULONG ValidOutputLength,
					IN BOOLEAN OpenIoctl,
					IN BOOLEAN CloseIoctl
					)

{
	PSOH_STREAM_OBJECT pStreamObj;
	KIRQL           DevKirql;
	NTSTATUS		Status;

	// validate input parameters

	DigiDump( DIGIVERYLOUD, ( "   InputBufferLength = %d, Valid Length = %d\n"
						    "   OutputBufferLength = %d, Valid Length = %d\n",
						    InputLength,
						    ValidInputLength,
						    OutputLength,
						    ValidOutputLength ) );

	// verify input buffer

	if ( InputLength != ValidInputLength )
	{

		// log the error ???

		return ( STATUS_INVALID_PARAMETER );
	}

	// verify output buffer

	if ( OutputLength != ValidOutputLength )
	{
		return ( STATUS_BUFFER_TOO_SMALL );
	}

	// return now for an open request

	if ( OpenIoctl )
	{
		return ( STATUS_SUCCESS );
	}

	// verify Stream object ID is within range

	if ( ( StrmObjId < SOH_STREAMS_MIN ) || ( StrmObjId > gulMaxStreams ) )
	{
		return ( STATUS_INVALID_PARAMETER );
	}

	// verify Stream is open and active while holding lock

	Status = STATUS_SUCCESS;

	KeAcquireSpinLock( &gSohRuntimeEnv.SpinLock, &DevKirql );

	pStreamObj = gapStreamObj[StrmObjId];

	if ( pStreamObj == 0 )
	{
		Status = STATUS_INVALID_PARAMETER ;
	}
	else if ( pStreamObj->State != SOH_STREAM_STATE_ACTIVE )
	{
			if ( !CloseIoctl )
			{
				Status = STATUS_INVALID_PARAMETER ;	
			}
	}

	KeReleaseSpinLock( &gSohRuntimeEnv.SpinLock, DevKirql );

	return ( Status );
}


NTSTATUS
SohValidateBuffers( IN PUCHAR pBuffer1,
					IN ULONG LengthBuffer1,
					IN PUCHAR pBuffer2,
					IN ULONG LengthBuffer2 )
{
	//
	// check for bBuffer && !Length
	// check for !bBuffer && Length
	//

	if ( pBuffer1 && !LengthBuffer1 )
	{
		//
		// If the buffer contains no data fail the request.
		//

		return ( STATUS_INVALID_PARAMETER );
	}

	if ( !pBuffer1 && LengthBuffer1 )
	{
		//
		// If the buffer is NULL but has a length fail the request.
		//

		return ( STATUS_INVALID_PARAMETER );
	}

	if ( pBuffer2 && !LengthBuffer2 )
	{
		//
		// If the buffer contains no data fail the request.
		//

		return ( STATUS_INVALID_PARAMETER );
	}

	if ( !pBuffer2 && LengthBuffer2 )
	{
		//
		// If the buffer is NULL but has a length fail the request.
		//

		return ( STATUS_INVALID_PARAMETER );
	}

	return ( STATUS_SUCCESS );
}
