/*++
*******************************************************************************
* Copyright (c) 1990-99 Digi International Inc., All Rights Reserved				*
* 																										*
* This software contains proprietary and confidential information of Digi		*
* International Inc.  By accepting transfer of this copy, Recipient agrees		*
* to retain this software in confidence, to prevent disclosure to others,		*
* and to make no use of this software other than that for which it was			*
* delivered.  This is an unpublished copyrighted work of Digi International	*
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly	*
* prohibited.																						*
* 																										*
* Restricted Rights Legend																		*
*																										*
* Use, duplication, or disclosure by the Government is subject to					*
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in			*
* Technical Data and Computer Software clause at DFARS 252.227-7031 or			*
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -				*
* Restricted Rights at 48 CFR 52.227-19, as applicable.								*
*																										*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343				*
*																										*
*******************************************************************************

Module Name:

   dxbpnp.c

Abstract:

   NT 5.0 PNP Irp handler routines.

Author:

   Michael Zarns

--*/
#include "digidxb.h"

#if NT5_BUILD
typedef struct ADAPTER_OPTION
{
	PCWSTR             Name;			// Adapter Option name string
	PDXB_ADAPTER_START StartRoutine;	// Adapter Startup routine

} ADAPTER_OPTION, *PADAPTER_OPTION;


// Table of adapter startup routines
static ADAPTER_OPTION Options[] = 
{
	{ DXB_REGVAL_OPTION_ENUM_SYNCPORT, dxbEpcStartAdapterSyncport },
	{ DXB_REGVAL_OPTION_ENUM_PRIME,    dxbEpcStartAdapterPrime    },
	{ DXB_REGVAL_OPTION_ENUM_CLYDE,    dxbClydeStartNonCsuAdapter },
	{ DXB_REGVAL_OPTION_ENUM_HOUSTON,  dxbHlcStartAdapterDense    },
	{ DXB_REGVAL_OPTION_ENUM_LONDON,   dxbHlcStartAdapterDense    },
	{ DXB_REGVAL_OPTION_ENUM_CHASKA,   dxbHlcStartAdapterDense    },
	{ DXB_REGVAL_OPTION_ENUM_ACCELEPORT_2000,   dxbRapStartAdapterTigger    }
};

#define OptionCount		( sizeof Options / sizeof Options[0] )


struct PNP_IRP_NAME
{
	ULONG Minor;
	PCSTR Name;
};

STATIC const struct DXB_MINOR_TABLE PnpIrps[] = 
{
	IRP_MN_START_DEVICE, 					"IRP_MN_START_DEVICE",
	IRP_MN_QUERY_STOP_DEVICE, 				"IRP_MN_QUERY_STOP_DEVICE",
	IRP_MN_STOP_DEVICE, 						"IRP_MN_STOP_DEVICE",
	IRP_MN_CANCEL_STOP_DEVICE, 			"IRP_MN_CANCEL_STOP_DEVICE",
	IRP_MN_QUERY_REMOVE_DEVICE, 			"IRP_MN_QUERY_REMOVE_DEVICE",
	IRP_MN_REMOVE_DEVICE, 					"IRP_MN_REMOVE_DEVICE",
	IRP_MN_CANCEL_REMOVE_DEVICE, 			"IRP_MN_CANCEL_REMOVE_DEVICE",
	IRP_MN_SURPRISE_REMOVAL, 				"IRP_MN_SURPRISE_REMOVAL",
	IRP_MN_QUERY_CAPABILITIES, 			"IRP_MN_QUERY_CAPABILITIES",
	IRP_MN_QUERY_PNP_DEVICE_STATE, 		"IRP_MN_QUERY_PNP_DEVICE_STATE",
	IRP_MN_FILTER_RESOURCE_REQUIREMENTS,"IRP_MN_FILTER_RESOURCE_REQUIREMENTS",
	IRP_MN_DEVICE_USAGE_NOTIFICATION, 	"IRP_MN_DEVICE_USAGE_NOTIFICATION",
	IRP_MN_QUERY_DEVICE_RELATIONS, 		"IRP_MN_QUERY_DEVICE_RELATIONS",
	IRP_MN_QUERY_RESOURCES, 				"IRP_MN_QUERY_RESOURCES",
	IRP_MN_QUERY_RESOURCE_REQUIREMENTS, "IRP_MN_QUERY_RESOURCE_REQUIREMENTS",
	IRP_MN_QUERY_ID, 							"IRP_MN_QUERY_ID",
	IRP_MN_QUERY_DEVICE_TEXT, 				"IRP_MN_QUERY_DEVICE_TEXT",
	IRP_MN_QUERY_BUS_INFORMATION, 		"IRP_MN_QUERY_BUS_INFORMATION",
	IRP_MN_QUERY_INTERFACE, 				"IRP_MN_QUERY_INTERFACE",
	IRP_MN_READ_CONFIG, 						"IRP_MN_READ_CONFIG",
	IRP_MN_WRITE_CONFIG, 					"IRP_MN_WRITE_CONFIG",
	IRP_MN_EJECT, 								"IRP_MN_EJECT",
	IRP_MN_SET_LOCK, 							"IRP_MN_SET_LOCK",
	IRP_MN_QUERY_LEGACY_BUS_INFORMATION,"IRP_MN_QUERY_LEGACY_BUS_INFORMATION"
};

PCSTR 
DxbGetPnpIrpName( ULONG Minor )
{
	return DxbGetIrpName( Minor, PnpIrps, sizeof PnpIrps / sizeof PnpIrps[0] );
}


STATIC NTSTATUS
DxbPnpIrpComplete( 
	IN PDEVICE_OBJECT pDevice,
	IN PIRP           pIrp,
	IN PKEVENT        pEvent )
{
	KeSetEvent( pEvent, IO_NO_INCREMENT, FALSE );
	return STATUS_MORE_PROCESSING_REQUIRED;
}


STATIC NTSTATUS
DxbPnpForwardIrp( 
	IN PDEVICE_OBJECT pNextLowerDriver,
	IN PIRP           pIrp )
{
	ASSERT( pNextLowerDriver );
	ASSERT( pIrp );
	IoSkipCurrentIrpStackLocation( pIrp );
	return IoCallDriver( pNextLowerDriver, pIrp );
}



STATIC NTSTATUS
DxbPnpForwardIrpWait( 
	IN PDEVICE_OBJECT pNextLowerDriver,
	IN PIRP           pIrp )
{
	KEVENT Event;
	NTSTATUS Status;

	KeInitializeEvent( &Event, SynchronizationEvent, FALSE );

	IoCopyCurrentIrpStackLocationToNext( pIrp );
	IoSetCompletionRoutine( pIrp, DxbPnpIrpComplete, &Event, TRUE, TRUE, TRUE );

	Status = IoCallDriver( pNextLowerDriver, pIrp );
	if ( Status == STATUS_PENDING )
	{
		DigiDump( DIGIPNPLOUD, ( "Dxb!PnpForwardIrp: waiting for Irp 0x%x completion...", pIrp ) );
		KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
		Status = pIrp->IoStatus.Status;
		DigiDump( DIGIPNPLOUD, ( "Dxb!PnpForwardIrp: Irp completed   0x%x, status 0x%x", pIrp, Status ) );
	}
	return Status;
}


STATIC VOID
DumpResourceList( 
	IN PCM_RESOURCE_LIST pResourceList )
{
	if ( pResourceList )
	{
		PCM_FULL_RESOURCE_DESCRIPTOR pFull = pResourceList->List;
		USHORT frd;

		DigiDump( DIGIPNPLOUD, ( "ResourceList:\n"
		                         "   Count %d\n", pResourceList->Count ) );

		if ( pFull )
		{
			for ( frd = 0; frd < pResourceList->Count; frd++ )
			{
				PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartial;
				ULONG prd;

				DigiDump( DIGIPNPLOUD, ( "   List[%d]\n"
				                         "      InterfaceType 0x%x\n"
				                         "      BusNumber 0x%x\n"
				                         "      PartialResourceList\n"
				                         "         Version 0x%x\n"
				                         "         Revision 0x%x\n"
				                         "         Count 0x%x\n",
				                         frd, pFull->InterfaceType, pFull->BusNumber,
				                         pFull->PartialResourceList.Version,
				                         pFull->PartialResourceList.Revision,
				                         pFull->PartialResourceList.Count ) );
				
				// Walk through all partial descriptors
				pPartial = pFull->PartialResourceList.PartialDescriptors;
				for ( prd = 0; prd < pFull->PartialResourceList.Count; prd++ )
				{
					DigiDump( DIGIPNPLOUD, ( "         PartialDescriptor[%d]\n"
					                         "            Type 0x%x\n"
					                         "            ShareDisposition 0x%x\n"
					                         "            Flags 0x%x\n",
					                         prd, pPartial->Type,
					                         pPartial->ShareDisposition,
					                         pPartial->Flags ) );
					switch ( pPartial->Type )
					{
						case CmResourceTypePort:
							DigiDump( DIGIPNPLOUD, ( "            Port\n"
							                         "               Start  0x%x%x\n"
							                         "               Length 0x%x\n",
							                         pPartial->u.Port.Start.HighPart,
							                         pPartial->u.Port.Start.LowPart,
							                         pPartial->u.Port.Length ) );
							break;

						case CmResourceTypeMemory:
							DigiDump( DIGIPNPLOUD, ( "            Memory\n"
							                         "               Start  0x%x%x\n"
							                         "               Length 0x%x\n",
							                         pPartial->u.Memory.Start.HighPart,
							                         pPartial->u.Memory.Start.LowPart,
							                         pPartial->u.Memory.Length ) );
							break;

						case CmResourceTypeInterrupt:
							DigiDump( DIGIPNPLOUD, ( "            Interrupt\n"
							                          "               Level    0x%x\n"
							                          "               Vector   0x%x\n"
							                          "               Affinity 0x%x\n",
							                          pPartial->u.Interrupt.Level,
							                          pPartial->u.Interrupt.Vector,
							                          pPartial->u.Interrupt.Affinity ) );
							break;

						case CmResourceTypeDevicePrivate:
							DigiDump( DIGIPNPLOUD, ( "            DevicePrivate\n"
							                          "               Data[0] 0x%x\n"
							                          "               Data[1] 0x%x\n"
							                          "               Data[2] 0x%x\n",
							                          pPartial->u.DevicePrivate.Data[0],
							                          pPartial->u.DevicePrivate.Data[1],
							                          pPartial->u.DevicePrivate.Data[2] ) );
							break;

						default:
							DigiDump( DIGIPNPLOUD, ( "            Unknown type!\n" ) );
							break;
					}
					pPartial++;
				}
				// Next FULL_RESOURCE_DESCRIPTOR starts immediately after.
				pFull = (PVOID) pPartial;
			}
		}
	}
}


NTSTATUS
DxbPnpStartDevice(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_START_DEVICE Irp.  The PnP Manager sends this IRP after it has 
	assigned resources, if any, to the device. The device may have been recently 
	enumerated and is being started for the first time, or the device may be 
	restarting after being stopped for resource reconfiguration. 

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.  May be NULL, if called from within IRP_MN_SET_POWER to come out of
		hibernation.

	pIos
		Parameters passed along with Irp. May be NULL, if called from within 
		IRP_MN_SET_POWER to come out of hibernation.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	HANDLE hkDriver = NULL;
	HANDLE hkParameters = NULL;
	DIGI_RESOURCE_LIST Resources;
	UNICODE_STRING Option = {0};

	DigiDump( DIGIPNP, ( "Dxb!PnpStartDevice: pExt 0x%x\n", pExt ) );
	
	if ( pExt->PnpState != DXBPNP_ADDED )
	{
		DigiDump( DIGIWARN, ( "Dxb!StartDevice: *** Bad PnpState 0x%x! ***\n", pExt->PnpState ) );
		goto Exit;
	}

    Status = DxbPnpForwardIrpWait( pExt->pNextDeviceObject, pIrp );
        
	if ( NT_ERROR( Status ) )
		goto Exit;

	DigiDump( DIGIPNPLOUD, ( "AllocatedResources\n" ) );
	DumpResourceList( pIos->Parameters.StartDevice.AllocatedResources );
	DigiDump( DIGIPNPLOUD, ( "AllocatedResourcesTranslated\n" ) );
	DumpResourceList( pIos->Parameters.StartDevice.AllocatedResourcesTranslated );

	Status = DigiAssignPnpResources( pIos->Parameters.StartDevice.AllocatedResources,
		                             pIos->Parameters.StartDevice.AllocatedResourcesTranslated,
		                             &Resources );
	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIWARN, ( "Dxb!PnpStartDevice: Error parsing resources lists, status 0x%x!\n", Status ) );
		goto Exit;
	}

	pExt->Hardware.BusType = Resources.BusType;
	pExt->PowerResources = Resources;	// set backup copy of resources
	pExt->bShutdown = FALSE;

	// Open adapter registry key, i.e. CurrentControlSet\Class\{...}\0001.
	Status = IoOpenDeviceRegistryKey( pExt->pPdo, PLUGPLAY_REGKEY_DRIVER, MAXIMUM_ALLOWED, &hkDriver );
	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIWARN, ( "Dxb!PnpStartDevice: Error opening registry key, status 0x%x!\n", Status ) );
		goto Exit;
	}

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegOpenSubkey( &hkParameters, hkDriver, DXB_REGKEY_PARAMETERS );

	if ( NT_SUCCESS( Status ) )
	{
		// First time a card comes up, the INF sets Spoof to TRUE, telling us to
		// spoof startup.  Skip configuration and clear the spoof flag so next time
		// we come up completely.
		ULONG Spoof;
		DigiRegQueryDword( hkParameters, DXB_REGVAL_SPOOF, &Spoof, DigiRegFlagOptional, FALSE );
		DigiRegWriteDword( hkParameters, DXB_REGVAL_SPOOF, 0 );
		pExt->fSpoof = Spoof != FALSE;
		if ( Spoof )
			goto EnumerateChildren;
	}		

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQuerySz( hkParameters, DXB_REGVAL_OPTION, &Option, DigiRegFlagRequired, NULL );

	if ( NT_SUCCESS( Status ) )
	{
		// Find and call the adapter startup routine based on the Option found.
		ULONG i;
		Status = STATUS_UNSUCCESSFUL;

		for ( i = 0; i < OptionCount; i++ )
		{
			UNICODE_STRING OptionName;
			RtlInitUnicodeString( &OptionName, Options[i].Name );
			if ( RtlEqualUnicodeString( &Option, &OptionName, TRUE ) )
			{
				Status = Options[i].StartRoutine( pExt, &OptionName, hkParameters, &Resources );
				break;
			}
		}
		if ( i == OptionCount )
		{
			DigiDump( DIGIERROR, ( "Dxb!StartDevice: Option %S not found!\n", Option.Buffer ) );
		}
	}

	// Create Symbolic link
	if ( NT_SUCCESS( Status ) )
	{
		UNICODE_STRING DeviceName   = {0};
		UNICODE_STRING SymbolicName = {0};

		Status = DigiStrDeviceName( &DeviceName, pExt->Name.Buffer );
		if ( NT_SUCCESS( Status ) )
		{
			Status = DigiStrSymbolicName( &SymbolicName, pExt->Name.Buffer );
			if ( NT_SUCCESS( Status ) )
			{
				(void)IoCreateSymbolicLink( &SymbolicName, &DeviceName );
				DigiStrFreeUnicodeString( &SymbolicName );
			}
			DigiStrFreeUnicodeString( &DeviceName );
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		// This adapter is Good To Go!
		InsertTailList( &gDeviceExtList, &pExt->ExtListEntry );
		pExt->PnpState = DXBPNP_STARTED;
		IoRegisterShutdownNotification( pExt->pDeviceObject );
	}

EnumerateChildren:
    if ( NT_SUCCESS( Status ) )
		DxbChildCreateChildList( pExt, hkParameters );

Exit:
	if ( Option.Buffer )
		DigiRegFreeString( &Option );

	if ( hkParameters )
		DigiRegCloseKey( hkParameters );

	if ( hkDriver )
		ZwClose( hkDriver );

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


NTSTATUS
DxbPnpRestartDevice(
	PDXB_DEVICE_EXTENSION pExt )
/*++

Description:

	Handle the restart after the system returns from standby.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.  May be NULL, if called from within IRP_MN_SET_POWER to come out of
		hibernation.

	pIos
		Parameters passed along with Irp. May be NULL, if called from within 
		IRP_MN_SET_POWER to come out of hibernation.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status;
	HANDLE hkDriver = NULL;
	HANDLE hkParameters = NULL;
	DIGI_RESOURCE_LIST Resources;
	UNICODE_STRING Option = {0};

	DigiDump( DIGIPNP, ( "Dxb!DxbPnpRestartDevice: pExt 0x%x\n", pExt ) );
	
	if ( pExt->PnpState != DXBPNP_ADDED )
	{
		DigiDump( DIGIWARN, ( "Dxb!DxbPnpRestartDevice: *** Bad PnpState 0x%x! ***\n", pExt->PnpState ) );
		goto Exit;
	}

	Resources = pExt->PowerResources;	// use backup copy of resources

	pExt->bShutdown = FALSE;

	// Open adapter registry key, i.e. CurrentControlSet\Class\{...}\0001.
	Status = IoOpenDeviceRegistryKey( pExt->pPdo, PLUGPLAY_REGKEY_DRIVER, MAXIMUM_ALLOWED, &hkDriver );
	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIWARN, ( "Dxb!DxbPnpRestartDevice: Error opening registry key, status 0x%x!\n", Status ) );
		goto Exit;
	}

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegOpenSubkey( &hkParameters, hkDriver, DXB_REGKEY_PARAMETERS );

	if ( NT_SUCCESS( Status ) )
		Status = DigiRegQuerySz( hkParameters, DXB_REGVAL_OPTION, &Option, DigiRegFlagRequired, NULL );

	if ( NT_SUCCESS( Status ) )
	{
		// Find and call the adapter startup routine based on the Option found.
		ULONG i;
		Status = STATUS_UNSUCCESSFUL;

		for ( i = 0; i < OptionCount; i++ )
		{
			UNICODE_STRING OptionName;
			RtlInitUnicodeString( &OptionName, Options[i].Name );
			if ( RtlEqualUnicodeString( &Option, &OptionName, TRUE ) )
			{
				Status = Options[i].StartRoutine( pExt, &OptionName, hkParameters, &Resources );
				break;
			}
		}
		if ( i == OptionCount )
		{
			DigiDump( DIGIERROR, ( "Dxb!DxbPnpRestartDevice: Option %S not found!\n", Option.Buffer ) );
		}
	}

	if ( NT_SUCCESS( Status ) )
	{
		// This adapter is Good To Go!
		pExt->PnpState = DXBPNP_STARTED;
		IoRegisterShutdownNotification( pExt->pDeviceObject );
	}

Exit:
	if ( Option.Buffer )
		DigiRegFreeString( &Option );

	if ( hkParameters )
		DigiRegCloseKey( hkParameters );

	if ( hkDriver )
		ZwClose( hkDriver );

	return Status;
}


NTSTATUS
DxbPnpQueryStopDevice(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_QUERY_STOP_DEVICE Irp.  The PnP Manager sends this IRP to query 
	whether a device can be stopped for resource reconfiguration.

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status = STATUS_UNSUCCESSFUL;
	DigiDump( DIGIPNP, ( "Dxb!PnpQueryStopDevice: pExt 0x%x\n", pExt ) );

	// TODO: Check if we can stop the device...
#if 0
	DigiAcquireSpinLock( &pExt->DxbLock );
	if ( IsListEmpty( &pExt->StreamList ) )
	{
#endif
		Status = STATUS_SUCCESS;
		pExt->fHoldNewRequests = TRUE;
#if 0
	}
	DigiReleaseSpinLock( &pExt->DxbLock );
#endif
	if ( NT_SUCCESS( Status ) )
	{
		// We can stop it, continue
		pIrp->IoStatus.Status = Status;
		Status = DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
	}
	else
		// We can't stop it, reject the request now
		dxbIoCompleteRequest( pIrp, Status, 0 );

	return Status;
}


NTSTATUS
DxbPnpStopDevice(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_STOP_DEVICE Irp.  The PnP Manager sends this IRP to stop a device
	so it can reconfigure its hardware resources. The PnP Manager only sends this IRP 
	if a prior IRP_MN_QUERY_STOP_DEVICE completed successfully.

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.  May be NULL, if called from within IRP_MN_SET_POWER to hibernate.

	pIos
		Parameters passed along with Irp.  May be NULL, if called from within 
		IRP_MN_SET_POWER to hibernate.

Return Value:

	This routine must return STATUS_SUCCESS.

--*/
{
    BOOLEAN fCalledInternally = pIrp == NULL ? TRUE : FALSE;
    NTSTATUS Status = STATUS_SUCCESS;

	DigiDump( DIGIPNP, ( "Dxb!PnpStopDevice: pExt 0x%x\n", pExt ) );

	if (pExt->PnpState == DXBPNP_STARTED)
	{
		pExt->PnpState = DXBPNP_ADDED;

        if (!fCalledInternally)
        {
            // Child PDO's are created on a Start Device - we have to remove them on a Stop Device   
            if (ChildrenRemoved(pExt))
            {
                // If any child devices were found - notify pnp system of the removal.
                IoInvalidateDeviceRelations (pExt->pPdo, BusRelations);
                DigiDump( DIGIINIT, ("Dxb!StopDevice: InvalidateDeviceRelations\n"));
            }  
        }  
		pExt->StopAdapter( pExt );
		IoUnregisterShutdownNotification( pExt->pDeviceObject );
	}
	else
	{
		DigiDump( DIGIWARN, ( "Dxb!StopDevice: *** Bad PnpState 0x%x! ***\n", pExt->PnpState ) );
	}

	if (!fCalledInternally)
	{
        Status = DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
	}

    return Status;
}


NTSTATUS
DxbPnpCancelStopDevice(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_CANCEL_STOP_DEVICE Irp.  The PnP Manager uses this IRP to inform 
	the drivers for a device that the device will not be stopped for resource 
	reconfiguration.
	
	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status = STATUS_SUCCESS;
	DigiDump( DIGIPNP, ( "Dxb!PnpCancelStopDevice: pExt 0x%x\n", pExt ) );

	pIrp->IoStatus.Status = STATUS_SUCCESS;
	Status = DxbPnpForwardIrpWait( pExt->pNextDeviceObject, pIrp );
	if ( NT_SUCCESS( Status ) )
	{
		// TODO: return to running status
		pExt->fHoldNewRequests = FALSE;
	}

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


NTSTATUS
DxbPnpQueryRemoveDevice(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_QUERY_REMOVE_DEVICE Irp.  The PnP Manager sends this IRP to 
	inform drivers that a device is about to be removed from the system and to query 
	whether the device can be removed without disrupting the system.

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status;
	DigiDump( DIGIPNP, ( "Dxb!PnpQueryRemoveDevice: pExt 0x%x\n", pExt ) );

	// TODO: Check if we can remove the device...
	Status = STATUS_SUCCESS;

	if ( NT_SUCCESS( Status ) )
	{
		// We can Remove it, continue
		pIrp->IoStatus.Status = Status;
		pExt->fHoldNewRequests = TRUE;
		Status = DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
	}
	else
		// We can't Remove it, reject the request now
		dxbIoCompleteRequest( pIrp, Status, 0 );

	return Status;
}


NTSTATUS
DxbPnpRemoveDevice(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_REMOVE_DEVICE Irp.  The PnP Manager uses this IRP to direct 
	drivers to remove a device from their device list. 

	For an orderly device removal, the PnP Manager sends an IRP_MN_QUERY_REMOVE_DEVICE
	prior to the remove IRP. In this case, the device is in the remove-pending state 
	when the remove IRP arrives. For a surprise-style device removal, the physical 
	device has already been removed so the PnP Manager sends the remove IRP without a 
	prior query-remove. A device can be in any state when it receives a remove IRP as 
	a result of a surprise-style removal.

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	This routine must return STATUS_SUCCESS.

--*/
{
	PDEVICE_OBJECT const pNext = pExt->pNextDeviceObject;
	DigiDump( DIGIPNP, ( "Dxb!PnpRemoveDevice: pExt 0x%x\n", pExt ) );

	pIrp->IoStatus.Status = STATUS_SUCCESS;

	if ( pExt->PnpState == DXBPNP_REMOVED )
	{
		DigiDump( DIGIWARN, ( "*** WARNING: Dxb!PnpRemoveDevice: Got a second Remove on this device, 0x%x!\n", pExt->pDeviceObject  ) );
	}

	if ( pExt->PnpState == DXBPNP_STARTED )
	{
		pExt->PnpState = DXBPNP_REMOVED;
		DxbChildDeleteChildList( pExt );
		RemoveEntryList( &pExt->ExtListEntry );
		pExt->StopAdapter( pExt );
		IoUnregisterShutdownNotification( pExt->pDeviceObject );
	}

	dxbDeleteDevice( pExt );

	return DxbPnpForwardIrp( pNext, pIrp );
}


NTSTATUS
DxbPnpCancelRemoveDevice(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_CANCEL_REMOVE_DEVICE Irp.  The PnP Manager uses this IRP to 
	inform the drivers for a device that the device will not be removed.

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status = STATUS_SUCCESS;
	DigiDump( DIGIPNP, ( "Dxb!PnpCancelRemoveDevice: pExt 0x%x\n", pExt ) );

	pIrp->IoStatus.Status = STATUS_SUCCESS;
	Status = DxbPnpForwardIrpWait( pExt->pNextDeviceObject, pIrp );
	if ( NT_SUCCESS( Status ) )
	{
		// TODO: return to running status
		pExt->fHoldNewRequests = FALSE;
	}

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


NTSTATUS
DxbPnpSurpriseRemoval(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_SURPRISE_REMOVAL Irp.  The PnP Manager sends this IRP to notify 
	the drivers for a device that the device has been unexpectedly removed from the 
	machine and is no longer available for I/O. 

	The PnP Manager sends this IRP before notifying user-mode components. After this 
	IRP completes, the PnP Manager sends a GUID_TARGET_DEVICE_REMOVE_COMPLETE 
	notification to components that registered for such notification.

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in the context of a system 
	thread.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	This routine must return STATUS_SUCCESS.

--*/
{
	PDEVICE_OBJECT const pNext = pExt->pNextDeviceObject;
	DigiDump( DIGIPNP, ( "Dxb!PnpSurpriseRemoval: pExt 0x%x\n", pExt ) );

	// TODO: Remove device and release resources
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	pExt->fHoldNewRequests = TRUE;

	if ( pExt->PnpState == DXBPNP_REMOVED )
	{
		DigiDump( DIGIWARN, ( "*** WARNING: Dxb!PnpSurpriseRemoval: Already removed this device, 0x%x!\n", pExt->pDeviceObject  ) );
	}

	if ( pExt->PnpState == DXBPNP_STARTED )
	{
		pExt->PnpState = DXBPNP_REMOVED;
		DxbChildDeleteChildList( pExt );
		RemoveEntryList( &pExt->ExtListEntry );
		pExt->StopAdapter( pExt );
		IoUnregisterShutdownNotification( pExt->pDeviceObject );
	}

	dxbDeleteDevice( pExt );

	return DxbPnpForwardIrp( pNext, pIrp );
}


NTSTATUS
DxbPnpQueryDeviceRelations(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Handle an IRP_MN_QUERY_DEVICE_RELATIONS Irp.  The PnP Manager sends this IRP to 
	gather information about devices with a relationship to the specified device.

	The PnP Manager queries a device's BusRelations (child devices) when the device is
	enumerated and at other times while the device is active, such as when a driver 
	calls IoInvalidateDeviceRelations to indicate that a child device has arrived or 
	departed. 

	The PnP Manager queries a device's RemovalRelations before it removes or ejects 
	the device and it queries for EjectionRelations before it ejects a device.

	The PnP Manager queries a device's PowerRelations when a component registers for 
	power notification on the specified device or one of its descendants.

	The PnP Manager queries a device's TargetDeviceRelation when a driver or user-mode
	application registers for PnP notification of an EventCategoryTargetDeviceChange 
	on the device. The PnP Manager queries for the device that is associated with a 
	particular file object. This is the only PnP IRP that has a valid file object 
	parameter.

	The PnP Manager sends this IRP at IRQL PASSIVE_LEVEL in an arbitrary thread 
	context.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status = pIrp->IoStatus.Status;
	ULONG_PTR Information = pIrp->IoStatus.Information;
	DigiDump( DIGIPNP, ( "Dxb!PnpQueryDeviceRelations: pExt 0x%x, QueryType %s\n", pExt,
	          pIos->Parameters.QueryDeviceRelations.Type == BusRelations ? "BusRelations" :
	          pIos->Parameters.QueryDeviceRelations.Type == EjectionRelations ? "EjectionRelations" :
	          pIos->Parameters.QueryDeviceRelations.Type == PowerRelations ? "PowerRelations" :
	          pIos->Parameters.QueryDeviceRelations.Type == RemovalRelations ? "RemovalRelations" :
	          pIos->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation ? "TargetDeviceRelation" : "Unknown" ) );

	switch ( pIos->Parameters.QueryDeviceRelations.Type )
	{
		case BusRelations:
		{
			ULONG nChildren = 0;

			// Count children to determine size of struct to allocate.  If not running,
			// use default nChildren=0.
			if ( pExt->PnpState == DXBPNP_STARTED )
			{
				PLIST_ENTRY pEntry = pExt->ChildListHead.Flink;
				while ( pEntry != &pExt->ChildListHead )
				{
					nChildren++;
					pEntry = pEntry->Flink;
				}
			}

			// Create response.  Alloc from paged pool, PNP manager will later free it!
			{
				PDEVICE_RELATIONS pRelations = ExAllocatePoolWithTag( PagedPool, sizeof *pRelations + nChildren * sizeof(PVOID), DefaultPoolTag );
				if ( !pRelations )
				{
					// The OS is low on memory, so fail this IRP and do not forward it to 
					// the lower layer.

					// NOTE: We don't need to call dxbIoCompleteRequest() as this IRP does not 
					// have any associated buffers which need to be unlocked

					NTSTATUS NtStatus = STATUS_INSUFFICIENT_RESOURCES;
					
					pIrp->IoStatus.Status      = NtStatus;
					pIrp->IoStatus.Information = 0;									
					IoCompleteRequest( pIrp, IO_NO_INCREMENT );

					return( NtStatus );				
				}

				pRelations->Count = nChildren;
				DigiDump( DIGIPNP, ( "Dxb: returning %d %s:\n", nChildren, nChildren == 1 ? "child" : "children") );

				// Fill in array of child device object pointers
				{
					ULONG i;
					PLIST_ENTRY pEntry;
					for ( i = 0, pEntry = pExt->ChildListHead.Flink; 
					      i < nChildren && pEntry != &pExt->ChildListHead; 
					      i++, pEntry = pEntry->Flink )
					{
						PDXB_CHILD_DEVICE pChild = CONTAINING_RECORD( pEntry, DXB_CHILD_DEVICE, ListEntry );
						ASSERT( pChild->Type == DXB_TYPE_CHILD_DEVICE );
						DigiDump( DIGIPNP, ( "      PDO 0x%08x, Ext 0x%08x\n", pChild->pPhysicalDeviceObject, pChild ) );
						pRelations->Objects[i] = pChild->pPhysicalDeviceObject;
						ObReferenceObject( pChild->pPhysicalDeviceObject );	// PNP removes the reference
					}
				}
				if ( pIrp->IoStatus.Information != 0 )
				{
					DigiDump( DIGIWARN, ( "Dxb!PnpQueryDeviceRelations: *** Freeing previous DEVICE_RELATIONS struct ***\n" ) );
					ExFreePool( (PVOID)pIrp->IoStatus.Information );
				}

				pIrp->IoStatus.Information = (ULONG_PTR)pRelations;
				pIrp->IoStatus.Status = STATUS_SUCCESS;
			}
			break;
		}

		case PowerRelations:
		case RemovalRelations:
		case EjectionRelations:
		case TargetDeviceRelation:
		default:
			break;
	}
		
	return DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
}


NTSTATUS
DxbPnpDispatch(
	PDXB_DEVICE_EXTENSION pExt, 
	PIRP                  pIrp, 
	PIO_STACK_LOCATION    pIos )
/*++

Description:

	Dispatch routine for all adapter FDO related PNP requests.

Parameters:

	pExt
		Device extension for our FDO which describes the adapter.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	STATUS_SUCCESS, or some error value.

--*/
{
	ULONG const Minor = pIos->MinorFunction;
	NTSTATUS Status;

	DigiDump( DIGIPNP, ( "Dxb!PnpDispatch: %s, pExt 0x%08x, pIrp 0x%08x\n", DxbGetPnpIrpName(Minor), pExt, pIrp ) );

	switch ( Minor )
	{
		case IRP_MN_START_DEVICE:						Status = DxbPnpStartDevice( pExt, pIrp, pIos );	break;
		case IRP_MN_QUERY_REMOVE_DEVICE:				Status = DxbPnpQueryRemoveDevice( pExt, pIrp, pIos );	break;
		case IRP_MN_REMOVE_DEVICE:						Status = DxbPnpRemoveDevice( pExt, pIrp, pIos );	break;
		case IRP_MN_CANCEL_REMOVE_DEVICE:			Status = DxbPnpCancelRemoveDevice( pExt, pIrp, pIos );	break;
		case IRP_MN_QUERY_STOP_DEVICE:				Status = DxbPnpQueryStopDevice( pExt, pIrp, pIos );	break;
		case IRP_MN_STOP_DEVICE:						Status = DxbPnpStopDevice( pExt, pIrp, pIos );	break;
		case IRP_MN_CANCEL_STOP_DEVICE:				Status = DxbPnpCancelStopDevice( pExt, pIrp, pIos );	break;
		case IRP_MN_QUERY_DEVICE_RELATIONS:			Status = DxbPnpQueryDeviceRelations( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_INTERFACE:					Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_CAPABILITIES:				Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_RESOURCES:					Status = DxbPnpQueryResources( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:	Status = DxbPnpQueryResourceRequirements( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_DEVICE_TEXT:				Status = DxbPnpQueryDeviceText( pExt, pIrp, pIos );	break;
//		case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:	Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );	break;
//		case IRP_MN_READ_CONFIG:						Status = DxbPnpReadConfig( pExt, pIrp, pIos );	break;
//		case IRP_MN_WRITE_CONFIG:						Status = DxbPnpWriteConfig( pExt, pIrp, pIos );	break;
//		case IRP_MN_EJECT:								Status = DxbPnpEject( pExt, pIrp, pIos );	break;
//		case IRP_MN_SET_LOCK:							Status = DxbPnpSetLock( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_ID:							Status = DxbPnpQueryId( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_PNP_DEVICE_STATE:			Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_BUS_INFORMATION:			Status = DxbPnpSkipAndForwardIrp( pExt, pIrp, pIos );	break;
//		case IRP_MN_DEVICE_USAGE_NOTIFICATION:		Status = DxbPnpDeviceUsageNotification( pExt, pIrp, pIos );	break;
		case IRP_MN_SURPRISE_REMOVAL:					Status = DxbPnpSurpriseRemoval( pExt, pIrp, pIos );	break;
//		case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:	break;

		default:
			DigiDump( DIGIPNPLOUD|DIGIDEVICE, ( "Dxb!PnpDispatch: Passing unhandled PNP Irp %s on to PDO!\n", DxbGetPnpIrpName( Minor ) ) );
			Status = DxbPnpForwardIrp( pExt->pNextDeviceObject, pIrp );
			break;
	}

	if ( NT_ERROR( Status ) )
	{
		DigiDump( DIGIPNPLOUD, ( "Dxb!PnpDispatch: %s, returning failure status 0x%x\n", DxbGetPnpIrpName(Minor), Status ) );
	}
	return Status;
}


#endif // NT5_BUILD
