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

   dxbdevice.c

Abstract:

   This module contains required NT kernelmode driver dispatch routines, including
   Create, Close, IoControl and Cleanup.

Author:

   Michael Zarns

--*/
#include "digidxb.h"
#if NT5_BUILD
#include <initguid.h>
#include <devguid.h>
#endif

NTSTATUS
DxbDeviceCreate(
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
/*++

Routine Description:

   This routine is called to open the device associated with DeviceObject.
   We will always allow the driver to be opened.

Arguments:

   pDevice
		Device object being opened.

	pIrp
		The open request.

Return Value:

   STATUS_SUCCESS

--*/
{
	PDXB_DEVICE_EXTENSION const pExt = pDevice->DeviceExtension;
	NTSTATUS Status;
	LARGE_INTEGER Time = {0};
	UNREFERENCED_PARAMETER( pDevice );
#if DBG
	KeQuerySystemTime( &Time );
#endif

	// Fail create if spoofing!
	Status = pExt->fSpoof ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS;

#if NT5_BUILD
	// Fail create if adapter isn't started
	if ( NT_SUCCESS( Status ) )
		Status = pExt->PnpState == DXBPNP_STARTED ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL;
#endif

   DigiDump( DIGIDEVICE, 
             ( "DXB!DxbCreate: time 0x%08x%08x, device %S\n",
             Time.HighPart, Time.LowPart, 
             ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

	dxbIoCompleteRequest( pIrp, Status, 0 );
   return( Status );
}


NTSTATUS
DxbDeviceClose(
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
/*++

Routine Description:

   This routine is called to close the device.

Arguments:

	pDevice
		Device object being closed.

	pIrp
		The close request.

Return Value:

   STATUS_SUCCESS

--*/
{
	LARGE_INTEGER Time = {0};
	UNREFERENCED_PARAMETER( pDevice );

#if DBG
	KeQuerySystemTime( &Time );
#endif

   DigiDump( DIGIDEVICE,
             ( "DXB!DeviceClose: time 0x%08x%08x, device %S\n",
             Time.HighPart, Time.LowPart,
             ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

   dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, 0 );
   return( STATUS_SUCCESS );
}


NTSTATUS
DxbDeviceIoControl(
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
/*++

Description:

	The primary dispatch routine for IRP_MJ_DEVICE_CONTROL requests.

Parameters:

	Standard NT Dispatch routine parameters.

Return Value:

	STATUS_SUCCESS if request completed successfully.

--*/
{
	PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
	PIO_STACK_LOCATION pIos = IoGetCurrentIrpStackLocation( pIrp );
	ULONG Ioctl = pIos->Parameters.DeviceIoControl.IoControlCode;
	NTSTATUS Status;

	LARGE_INTEGER Time = {0};

#if DBG
	KeQuerySystemTime( &Time );
#endif

	DigiDump( DIGIDEVICE,
	          ( "DXB!DeviceIoControl: Enter, time 0x%08x%08x, device %S, pIrp 0x%08x, Ioctl %s\n", 
	          Time.HighPart, Time.LowPart,
	          pExt->Name.Buffer, pIrp,
	          Ioctl == IOCTL_DS_OPEN   ? "IOCTL_DS_OPEN" :
	          Ioctl == IOCTL_DS_CLOSE  ? "IOCTL_DS_CLOSE" :
	          Ioctl == IOCTL_DS_PUTMSG ? "IOCTL_DS_PUTMSG" :
	          Ioctl == IOCTL_DS_GETMSG ? "IOCTL_DS_GETMSG" :
	          Ioctl == IOCTL_DS_IOCTL  ? "IOCTL_DS_IOCTL" :
	          Ioctl == IOCTL_DS_LINK   ? "IOCTL_DS_LINK" :
	          Ioctl == IOCTL_DS_PURGE  ? "IOCTL_DS_PURGE" :
	          Ioctl == IOCTL_DS_READ   ? "IOCTL_DS_READ" :
	          Ioctl == IOCTL_DS_WRITE  ? "IOCTL_DS_WRITE" :
	          Ioctl == IOCTL_DS_POP    ? "IOCTL_DS_POP" :
	          Ioctl == IOCTL_DS_PUSH   ? "IOCTL_DS_PUSH" :
	          Ioctl == IOCTL_DS_UNLINK ? "IOCTL_DS_UNLINK" : 
	          Ioctl == IOCTL_KME_READ  ? "IOCTL_KME_READ" :
	          Ioctl == IOCTL_KME_WRITE ? "IOCTL_KME_WRITE" : "Unknown" ) );

	switch ( Ioctl )
	{
		case IOCTL_DS_OPEN:
			Status = dxbStreamOpen( pExt, pIrp, pIos );
			break;

		case IOCTL_DS_CLOSE:
			Status = dxbStreamClose( pExt, pIrp, pIos );
			break;

		case IOCTL_DS_PUTMSG:
			Status = dxbStreamPutMsg( pExt, pIrp, pIos );
			break;

		case IOCTL_DS_GETMSG:
			Status = dxbStreamGetMsg( pExt, pIrp, pIos );
			break;

		case IOCTL_DS_IOCTL:
			Status = dxbStreamIoctl( pExt, pIrp, pIos );
			break;

		case IOCTL_DS_LINK:
			Status = dxbStreamLink( pExt, pIrp, pIos );
			break;

		case IOCTL_DS_PURGE:
			Status = dxbStreamPurge( pExt, pIrp, pIos );
			break;

		case IOCTL_KME_READ:
			Status = DxbKmeRead( pExt, pIrp );
			break;

		case IOCTL_KME_WRITE:
			Status = DxbKmeWrite( pExt, pIrp );
			break;

		case IOCTL_KME_MAP:
			Status = DxbKmeMap( pExt, pIrp );
			break;

		case IOCTL_KME_UNMAP:
			Status = DxbKmeUnmap( pIrp );
			break;

		case IOCTL_DS_READ:
		case IOCTL_DS_WRITE:
		case IOCTL_DS_POP:
		case IOCTL_DS_PUSH:
		case IOCTL_DS_UNLINK:
		default:
			DigiDump( DIGIERROR,
			          ( "DXB!DispatchIoctl: Rejecting unsupported DSAPI request: %s!\n",
			          Ioctl == IOCTL_DS_READ   ? "IOCTL_DS_READ"   :
			          Ioctl == IOCTL_DS_WRITE  ? "IOCTL_DS_WRITE"  :
			          Ioctl == IOCTL_DS_POP    ? "IOCTL_DS_POP"    :
			          Ioctl == IOCTL_DS_POP    ? "IOCTL_DS_PUSH"   :
			          Ioctl == IOCTL_DS_UNLINK ? "IOCTL_DS_UNLINK" :
			                                     "Unknown" ) );

			Status = STATUS_UNSUCCESSFUL;
			dxbIoCompleteRequest( pIrp, Status, 0 );
			break;
	}

#if DBG
	KeQuerySystemTime( &Time );
#endif
	DigiDump( DIGIDEVICE,
	          ( "DXB!DeviceIoControl: Leave, time 0x%08x%08x, device %S, pIrp 0x%08x, Status 0x%x\n", 
	          Time.HighPart, Time.LowPart, pExt->Name.Buffer, pIrp, Status ) );

	return Status;
}


STATIC NTSTATUS
dxbCloseComplete(
	PDEVICE_OBJECT pDeviceObject,
	PIRP           pIrp,
	PVOID          Context )
/*++

Description:

	This is the completion routine for DxbClose.

Parameters:

	pDeviceObject

	pIrp

	Context
		Not used.

Return Value:

	STATUS_MORE_PROCESSING_REQUIRED

--*/
{
	UNREFERENCED_PARAMETER( pDeviceObject );
	UNREFERENCED_PARAMETER( Context );
	ASSERT( pIrp );

	ASSERT( pIrp->UserIosb );
	*pIrp->UserIosb = pIrp->IoStatus;

	ASSERT( pIrp->UserEvent );
	(void)KeSetEvent( pIrp->UserEvent, IO_NO_INCREMENT, FALSE );
	IoFreeIrp( pIrp );
	return( STATUS_MORE_PROCESSING_REQUIRED );
}


STATIC NTSTATUS
dxbClose(
	PDXB_STREAM_OBJECT pStream )
/*++

Description:

	This routine performs a synchronous IOCTL_DS_CLOSE request.  Used internally at
	IRP_MJ_CLEANUP time to close orphan streams.

Parameters:

	pStream
		Stream to be closed.

Return Value:

	STATUS_SUCCESS, or some error

--*/
{
	DS_CLOSE Close;
	PIRP pIrp;
	KEVENT Event;
	IO_STATUS_BLOCK IoStatus;
	NTSTATUS Status;
	PIO_STACK_LOCATION pIos;

	ASSERT( pStream && pStream->Type == DXB_TYPE_STREAM_OBJECT );
	RtlZeroMemory( &Close, sizeof( Close ) );
	Close.hStream = (HANDLE)pStream->Channel;

	ASSERT( KeGetCurrentIrql() == PASSIVE_LEVEL );

	KeInitializeEvent( &Event, NotificationEvent, FALSE );
	RtlZeroMemory( &IoStatus, sizeof( IoStatus ) );

	pIrp = IoAllocateIrp( pStream->pExt->pDeviceObject->StackSize, FALSE );
	if ( !pIrp )
		return( STATUS_INSUFFICIENT_RESOURCES );

	pIrp->RequestorMode = KernelMode;
	pIrp->AssociatedIrp.SystemBuffer = &Close;
	pIrp->UserIosb = &IoStatus;
	pIrp->UserEvent = &Event;

	pIos = IoGetNextIrpStackLocation( pIrp );
	pIos->MajorFunction = IRP_MJ_DEVICE_CONTROL;
	pIos->Parameters.DeviceIoControl.IoControlCode = (ULONG)IOCTL_DS_CLOSE;
	pIos->Parameters.DeviceIoControl.InputBufferLength = sizeof( Close );
	IoSetCompletionRoutine( pIrp, dxbCloseComplete, NULL, TRUE, TRUE, TRUE );

	Status = IoCallDriver( pStream->pExt->pDeviceObject, pIrp );
	if ( Status == STATUS_PENDING )
	{
		(void)KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
		Status = IoStatus.Status;
	}
	return Status;
}


NTSTATUS
DxbDeviceCleanup(
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
/*++

Routine Description:

	A File object (i.e. handle returned from CreateFile) is being closed.  Shutdown
	all streams opened on this File Object.

Arguments:

	pDevice
		Device Object

	pIrp
		The cleanup request

Return Value:


--*/
{
	PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
	PLIST_ENTRY pHead, pEntry;
	NTSTATUS Status;
	LARGE_INTEGER Time = {0};

#if DBG
	KeQuerySystemTime( &Time );
#endif

   DigiDump( DIGIDEVICE,
             ( "DXB!DeviceCleanup: time 0x%08x%08x, device %S\n",
             Time.HighPart, Time.LowPart,
             ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

	DxbAdapterLock( pExt );
	pHead = &pExt->StreamList;
	pEntry = pHead->Flink;
	while ( pEntry != pHead )
	{
		PDXB_STREAM_OBJECT pStream;
		pStream = CONTAINING_RECORD( pEntry, DXB_STREAM_OBJECT, Entries.StreamList );

		ASSERT( pStream->Type == DXB_TYPE_STREAM_OBJECT );
		if ( pStream->OriginalFileObject == pIrp->Tail.Overlay.OriginalFileObject )
		{
			// This stream was created by the owner of the FileObject being closed.
			// Close the stream since it's creator/owner is going away.
			DxbAdapterUnlock( pExt );
			DigiDump( DIGIERROR, ( "*** DxbCleanup: closing orphan Stream 0x%x ***\n", pStream ) );

			Status = dxbClose( pStream );	// Don't use DsClose!  Not a dsapi lib hStream.
			if ( NT_ERROR(Status) )
			{
			   DigiDump( DIGIERROR,
			             ( "***DxbCleanup: DsClose error, Status 0x%x ***\n", Status) );
				DxbAdapterLock( pExt );
				break;
			}

			// Must reset to head of list, because any number of Streams may have
			// been closed since between the last release and this acquire of spinlock.
			pEntry = pHead;
			DxbAdapterLock( pExt );
		}
		// Get next entry
		pEntry = pEntry->Flink;
	}

	DxbAdapterUnlock( pExt );
   dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, 0 );

   DigiDump( DIGIDEVICE, 
             ( "DXB!DeviceCleanup: Leave, device %S\n",
             ((PDXB_DEVICE_EXTENSION)(pDevice->DeviceExtension))->Name.Buffer ) );

   return STATUS_SUCCESS;
}


NTSTATUS
DxbDeviceShutdown(
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
{
	PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
	ASSERT( pExt->Type == DXB_TYPE_DEVICE_EXT );
	DigiDump( DIGIWARN, ( "DXB!DeviceShutdown: device %S\n", pExt->Name.Buffer ) );

	pExt->bShutdown = TRUE;

	{
		// Delay to make sure DPC has seen the shutdown
		LARGE_INTEGER Delay;
		Delay.QuadPart = 100 * ONE_MILLISECOND;
		ASSERT( KeGetCurrentIrql() < DISPATCH_LEVEL );
		KeDelayExecutionThread( KernelMode, FALSE, &Delay );
	}

	pExt->AssertReset( pExt );
	dxbIoCompleteRequest( pIrp, STATUS_SUCCESS, 0 );
	return STATUS_SUCCESS;
}


VOID
dxbDeleteDevice(
	PDXB_DEVICE_EXTENSION pExt )
/*++

Routine Description:

   Release any resources which we have allocated for the DeviceObject and delete the
	device object.

Arguments:

   pExt - the device extension of the device to delete.

Return Value:

   None.

--*/
{
   DigiDump( DIGIDEVICE, ( "DXB!dxbDeleteDevice, device %S\n", pExt->Name.Buffer ) );

   // Delete SymbolicLink
	{
		UNICODE_STRING SymbolicName;
		if ( NT_SUCCESS( DigiStrSymbolicName( &SymbolicName, pExt->Name.Buffer ) ) )
		{
			(void)IoDeleteSymbolicLink( &SymbolicName );
			DigiStrFreeUnicodeString( &SymbolicName );
		}
	}

#if NT5_BUILD
	if ( pExt->pNextDeviceObject )
		IoDetachDevice( pExt->pNextDeviceObject );
#endif

	IoDeleteDevice( pExt->pDeviceObject );
}


#if NT5_BUILD
STATIC PWCHAR
GetInstanceString( 
	PWSTR pString,
	ULONG Length )
/*++

Description: 

	Parse a WCHAR string, returning a pointer to the device instance.  Expected format
	is "<SomeGuid>\<InstanceNumber>".

Parameters:

	pString
		String to parse

	Length
		Length in WCHARs of the string.

Return Value:

	NULL if not found, otherwise pointer to InstanceNumber string.

--*/
{
	ULONG i = Length;
	if ( pString == NULL || i == 0 )
		return NULL;

	while ( i > 0 && pString[--i] != '\\' )
	{
		;
	}

	if ( i != 0 && i < Length-1 )
		return &pString[i+1];
	else
		return NULL;
}


STATIC NTSTATUS
GetDeviceInstance( 
	IN PDEVICE_OBJECT pPdo,
	IN OUT PWCHAR     buf,
	IN ULONG          cb )
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	WCHAR MyBuf[64] = {0};
	ULONG MyCb;
	Status = IoGetDeviceProperty( pPdo, DevicePropertyDriverKeyName, sizeof MyBuf, MyBuf, &MyCb );
	if ( NT_SUCCESS( Status ) )
	{
		PWSTR p = GetInstanceString( MyBuf, MyCb/sizeof(WCHAR) );
		if ( p && wcslen(p)*sizeof(WCHAR) < cb )
		{
			wcscpy( buf, p );
			Status = STATUS_SUCCESS;
		}
	}
	return Status;
}


STATIC NTSTATUS
GetDeviceClass( 
	IN PDEVICE_OBJECT pPdo,
	IN OUT PWCHAR     buf,
	IN ULONG          cb )


{
	// Use the GUID of the adapter device class to determine whether this adapter
	// was installed as a MultiPortSerial adapter or Net adapter.
	// 
	// If MultiPortSerial, the string "Mps" will be used when defining the name
	// of the device object - this will guarantee no name conflicts between
	// Digi adapters of each class installed in the same system.
#define MULTIPORTSERIAL L"Mps"
   
	NTSTATUS Status;   
	ULONG		MyCb;

	Status = IoGetDeviceProperty( pPdo, DevicePropertyClassGuid, 0, NULL, &MyCb );

	if ( Status == STATUS_BUFFER_TOO_SMALL && MyCb != 0) 
	{
		PWCHAR	pGuidStr;

		pGuidStr = DigiAllocMem( NonPagedPool, MyCb );				    
		if ( NULL != pGuidStr ) 
		{
			UNICODE_STRING	GuidUString;
			GUID				Guid;

			Status = IoGetDeviceProperty( pPdo, DevicePropertyClassGuid, MyCb, pGuidStr, &MyCb );
			if (NT_SUCCESS( Status ))
				Status = DigiStrAllocUnicodeString( &GuidUString, pGuidStr, NULL, NULL );
			if (NT_SUCCESS( Status ))
				Status = RtlGUIDFromString( &GuidUString, &Guid );            
			if (NT_SUCCESS( Status )) 
			{ 
				if ( IsEqualGUID( &Guid, (PVOID)&GUID_DEVCLASS_MULTIPORTSERIAL ))
				{
					if ( cb < sizeof MULTIPORTSERIAL )
						Status = STATUS_BUFFER_TOO_SMALL;
					else
						wcscpy( buf, MULTIPORTSERIAL );                     
				}               
			}               
			DigiFreeMem( pGuidStr );
			if ( GuidUString.Buffer )
				DigiStrFreeUnicodeString( &GuidUString );
		}
	}                     
   return Status;   
}      
#endif


#if NT5_BUILD
NTSTATUS
DxbAddDevice( 
	IN PDRIVER_OBJECT pDriverObject,
	IN PDEVICE_OBJECT pPhysicalDeviceObject )
{
	PDXB_DEVICE_EXTENSION pExt;
	PDEVICE_OBJECT pFunctionalDeviceObject = NULL;
	NTSTATUS Status;
	UNICODE_STRING Instance = {0};
	UNICODE_STRING Name = {0};
	
	{
		WCHAR buf[16] = {0};
      WCHAR ClsBuf[16] = {0};
		Status = GetDeviceInstance( pPhysicalDeviceObject, buf, sizeof buf );
      if ( NT_SUCCESS( Status ) )
      	Status = GetDeviceClass( pPhysicalDeviceObject, ClsBuf, sizeof ClsBuf );
		if ( NT_SUCCESS( Status ) )
			Status = DigiStrAllocUnicodeString( &Instance, buf, NULL, NULL );
		if ( NT_SUCCESS( Status ) )
			Status = DigiStrAllocUnicodeString( &Name, L"DigiDxb", ClsBuf , buf );
	}

	if ( NT_SUCCESS( Status ) )
	{
		UNICODE_STRING DeviceName = {0};
		Status = DigiStrDeviceName( &DeviceName, Name.Buffer );
		if ( NT_SUCCESS( Status ) )
		{
			Status = IoCreateDevice( pDriverObject, sizeof *pExt, &DeviceName, FILE_DEVICE_DS, 0, FALSE, &pFunctionalDeviceObject );
			DigiStrFreeUnicodeString( &DeviceName );
		}
		if ( NT_ERROR( Status ) )
			goto Exit;
	}

	pExt = pFunctionalDeviceObject->DeviceExtension;
	pExt->Type = DXB_TYPE_DEVICE_EXT;
	
	pExt->pPdo = pPhysicalDeviceObject;
	pExt->fHoldNewRequests = FALSE;
	InitializeListHead( &pExt->RequestList );
	InitializeListHead( &pExt->ChildListHead );

	DigiInitializeSpinLock( &pExt->DxbLock );
	pExt->pDeviceObject = pFunctionalDeviceObject;

	InitializeListHead( &pExt->StreamList );
	DigiInitializeSpinLock( &pExt->WindowLock );

	dxbQueueInitializeEventDpcTimer( pExt );
	dxbQueueInitializeCmdWorkQueue( pExt );
	dxbQueueInitializeCreditWorkQueue( pExt );

	{
		ULONG i;
		for ( i = 0; i < sizeof pExt->Channels / sizeof pExt->Channels[0]; i++ )
			DigiInitializeSpinLock( &pExt->Channels[i].DxbLock );
	}

	{
        POWER_STATE State = {0};

        pExt->PowerState   = PWRSTATE_ON;  // power assumed to be on
        pExt->SysPower     = PowerSystemWorking;
        pExt->DevPower     = PowerDeviceD0;
        pExt->WakeSysPower = PowerSystemWorking;

        State.DeviceState = PowerDeviceD0;
		(void)PoSetPowerState( pFunctionalDeviceObject, DevicePowerState, State );
	}

	pFunctionalDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

	// Set both PDO and FDO PAGABLE flags.  This forces SET_POWER apis to be called
	// at PASSIVE_LEVEL rather than DISPATCH_LEVEL.
	pFunctionalDeviceObject->Flags |= DO_POWER_PAGABLE;
	pPhysicalDeviceObject->Flags   |= DO_POWER_PAGABLE;

	pExt->pNextDeviceObject = IoAttachDeviceToDeviceStack( pFunctionalDeviceObject, pPhysicalDeviceObject );

	// Create a symbolic link name for this device
	pExt->Name.Buffer = pExt->NameBuffer;
	pExt->Name.MaximumLength = sizeof pExt->NameBuffer;
	pExt->Name.Length = 0;
	RtlAppendUnicodeStringToString( &pExt->Name, &Name );

	pExt->Instance.Buffer = pExt->InstanceBuffer;
	pExt->Instance.MaximumLength = sizeof pExt->InstanceBuffer;
	pExt->Instance.Length = 0;
	RtlAppendUnicodeStringToString( &pExt->Instance, &Instance );

	DigiDump( DIGIDEVICE|DIGIPNP, ( "Dxb!AddDevice: DriverObject 0x%x, PDO  0x%x\n"
	                                "               FDO 0x%x, pExt 0x%x\n"
	                                "               NextLowerDevice 0x%x\n"
	                                "               Name %S\n"
	                                "               Instance %S\n"
	                                "               Status 0x%x\n",
	                                pDriverObject, pPhysicalDeviceObject, 
	                                pFunctionalDeviceObject, pExt, 
	                                pExt->pNextDeviceObject, 
	                                pExt->Name.Buffer, 
	                                pExt->Instance.Buffer, 
	                                Status ) );
Exit:

	if ( Instance.Buffer )
		DigiStrFreeUnicodeString( &Instance );

	if ( Name.Buffer )
		DigiStrFreeUnicodeString( &Name );

	return Status;
}


NTSTATUS
DxbDevicePnp(
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp )
/*++

Description:

	The primary dispatch routine for IRP_MJ_PNP requests.

Parameters:

	Standard NT Dispatch routine parameters.

Return Value:

	STATUS_SUCCESS if request completed successfully.

--*/
{
	DXB_OBJECT_TYPE Type = *(PULONG)pDevice->DeviceExtension;
	PIO_STACK_LOCATION pIos = IoGetCurrentIrpStackLocation( pIrp );
	ULONG Minor = pIos->MinorFunction;
	NTSTATUS Status;

	DigiDump( DIGIDEVICE, ( "DXB!DevicePnp: Enter, %s, pExt 0x%08x, pIrp 0x%08x\n", 
	          DxbGetPnpIrpName(Minor),
	          pDevice->DeviceExtension, pIrp ) );

	// We handle the irp differently depending on the target device.  If it's our dxb
	// adapter device (FDO), use DxbPnpDispatch.  If it's a child device (PDO), use 
	// DxbChildDispatch.

	switch ( Type )
	{
		case DXB_TYPE_DEVICE_EXT:
			Status = DxbPnpDispatch( pDevice->DeviceExtension, pIrp, pIos );
			break;

		case DXB_TYPE_CHILD_DEVICE:
			Status = DxbChildDispatch( pDevice->DeviceExtension, pIrp, pIos );
			break;

		default:
			DigiDump( DIGIERROR, ( "*** ERROR: PNP Irp received with bad device ext type, pExt 0x%x, %s (0x%x)! ***\n", pDevice->DeviceExtension, DxbGetPnpIrpName(Minor), Minor ) );
			Status = STATUS_UNSUCCESSFUL;
			dxbIoCompleteRequest( pIrp, Status, 0 );
			break;
	}

	DigiDump( DIGIDEVICE, ( "DXB!DevicePnp: Leave %s, pExt 0x%08x, pIrp 0x%08x, Status 0x%x\n", DxbGetPnpIrpName(Minor), pDevice->DeviceExtension, pIrp, Status ) );
	return Status;
}


/*---------------------------------------------------------------------------*/
/*  Name: DxbDevicePower                                                     */
/* --------------------------------------------------------------------------*/ 
/*    In: - pFuncDeviceObject, functional device object created by device    */
/*        - Irp, I/O request                                                 */   
/*---------------------------------------------------------------------------*/
/* InOut:                                                                    */
/*---------------------------------------------------------------------------*/
/*   Out:                                                                    */
/*---------------------------------------------------------------------------*/
/* Description: Handle IRP_MJ_POWER request                                  */
/*   uses the IRP's minor function code to dispatch a handler function (like */
/*   HandleSetPower for IRP_MN_SET_POWER)                                    */
/*---------------------------------------------------------------------------*/

NTSTATUS DxbDevicePower( IN PDEVICE_OBJECT pFuncDeviceObject, IN PIRP Irp )
{
	NTSTATUS Status;
	DXB_OBJECT_TYPE Type = *(PULONG)pFuncDeviceObject->DeviceExtension;
    
	switch (Type)
	{
		case DXB_TYPE_DEVICE_EXT:
			Status = DispatchPowerFdo( pFuncDeviceObject, Irp );
			break;

		case DXB_TYPE_CHILD_DEVICE:      
			Status = DispatchPowerPdo( pFuncDeviceObject, Irp );
			break;

		default:
			Status = DispatchPowerFdo( pFuncDeviceObject, Irp );
			break;
	}
	
    return Status;
}


NTSTATUS DxbDeviceSystemControl( IN PDEVICE_OBJECT pDevice, IN PIRP Irp )
/*++

Description:

	The primary dispatch routine for IRP_MJ_SYSTEM_CONTROL requests.

Parameters:

	Standard NT Dispatch routine parameters.

Return Value:

	STATUS_SUCCESS if request completed successfully.

--*/
{
    PDXB_DEVICE_EXTENSION pExt = pDevice->DeviceExtension;
	DXB_OBJECT_TYPE Type = *(PULONG)pDevice->DeviceExtension;
    NTSTATUS Status;
   
	DigiDump( DIGIDEVICE, ( "DXB!DeviceSystemControl: Enter, pExt 0x%08x, pIrp 0x%08x\n", 
	          pDevice->DeviceExtension, Irp ) );

    switch (Type)
    {
    case DXB_TYPE_DEVICE_EXT:
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(pExt->pNextDeviceObject, Irp);
        break;

    case DXB_TYPE_CHILD_DEVICE:
        Status = Irp->IoStatus.Status;
        IoCompleteRequest( Irp, IO_NO_INCREMENT );
        break;

    default:
        IoSkipCurrentIrpStackLocation(Irp);
        Status = IoCallDriver(pExt->pNextDeviceObject, Irp);
        break;
    }

	DigiDump( DIGIDEVICE, ( "DXB!DeviceSystemControl: Leave, pExt 0x%08x, pIrp 0x%08x, Status 0x%x\n", pDevice->DeviceExtension, Irp, Status ) );
    return Status;   
}


#endif  // NT5_BUILD
