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

   pdo.c

Abstract:

   NT 2000 DSTREAMS Provider PNP Irp handler routines for child PDOs.

Author:

   Jeff Romatoski

--*/

#include "Digilib.h"    // defines NT5_BUILD

#if NT5_BUILD

#include "pnp.h"
#include "stdio.h"


STATIC NTSTATUS
PnpPdoStartDevice(
	PPNP_PDO_EXTENSION ppdoExtension, 
	PIRP               pIrp, 
	PIO_STACK_LOCATION pIos
	);


NTSTATUS
PnpPdoRemoveDevice(
	PPNP_PDO_EXTENSION ppdoExtension,
	PIRP               pIrp, 
	PIO_STACK_LOCATION pIos
	);


///////////////////////////////////////////////////////////////////////////////

NTSTATUS
PnpPdoCreateDevice( 
	IN PPNP_FDO_EXTENSION	fdoExtension,
	IN HANDLE				hkChild,
	IN PUNICODE_STRING		pParametersKey, 				// "\{GUID}\0008\Parameters"
	IN PUNICODE_STRING		pServiceKey, 					// "ISDN\0"
	IN PUNICODE_STRING		pServiceSubkey OPTIONAL,		// "0"
	IN ULONG				iService, 
	IN ULONG				iLine,
	IN PUNICODE_STRING		pHardwareId						// "DIGIBRI"
	)					
/*++

Description:

	Alloc and init struct that describes a dxb child device.  This descriptor stays
	for the life of an adapter (between StartDevice and StopDevice), while the 
	device_object it owns may be deleted/added again and again.

Parameters:

	fdoExtension
		Adapter FDO device extension

	hkChild
		Handle to previously opened regkey containing the config params for the child 
		device.  It is under dxb\Parameters and is where the HardwareId was found.  We
		write the NT devicename the client should open to talk to this adapter.

	pParametersKey
		Partial registry path from NetClass GUID down to our adapter Parameters key.
		This key, combined with pServiceKey define the full path (again from netclass
		down) to the child device.  We write this to the child device so it can find its
		way back here.  e.g. {4D36E972-E325-11CE-BFC1-08002BE10318}\0008\Parameters.
		
	pServiceKey
		String defining the subkey (under Parameters) defining this child device.  It
		is the key in which HardwareId was found, and is where all config params for
		the client driver are stored.  e.g. ISDN\0.
		
	pServiceSubkey
		String defining the instance number, if this device is a multiple-child-per-line
		device.  Otherwise, NULL.

	iService
		Zero-based entry in the LineServices value for this line (identified by iLine).
		Used along with iLine to create unique identifiers for each child device.

	iLine
		Zero-based line number on this adapter.  Appended to the ServiceName to define 
		the service instance key.  Some services will have child devices here (Wan 
		drivers - ISDN, X25, etc),	others define children in the keys one level lower 
		(Asyn and Lan drivers). 

	pHardwareId
		String defining hardware Id of the child device.  HardwareId's define what 
		client driver is loaded to handle the device.

Return Value:

	STATUS_SUCCESS or error.

--*/
{
	NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES;
	PDEVICE_OBJECT pDevice = NULL;
	PPNP_PDO_EXTENSION	ppdoExtension = NULL;
	ULONG iKeyName;

	// Create device object.  Note: this automatically zeros out the device extension.
	Status = IoCreateDevice( gDriverData.DriverObject, sizeof( PNP_PDO_EXTENSION ), NULL, FILE_DEVICE_UNKNOWN, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &pDevice );
	if ( NT_ERROR(Status) )
		goto Exit;

	ppdoExtension					= pDevice->DeviceExtension;
	ppdoExtension->DeviceType		= PNP_DEVICE_TYPE_PDO;
	ppdoExtension->pDeviceObject	= pDevice;
	ppdoExtension->pFdo				= fdoExtension;

	// Signal that this device is done with initialization.
	pDevice->Flags &= ~DO_DEVICE_INITIALIZING;

	// Indicate that we require POWER IRPs to come in at PASSIVE_LEVEL rather than
	// at DISPATCH_LEVEL.  This must be consistent with all device object in the 
	// stack -- meaning that our adapter FDO, it's PDO, and all child PDOs must be
	// the same.
	pDevice->Flags |= DO_POWER_PAGABLE;

	// HardwareId identifies the child device type, e.g. DIGIBRI or DIGIASYN.
	Status = DigiStrAllocUnicodeString( &ppdoExtension->HardwareId, pHardwareId->Buffer, NULL, NULL );

	// RegPath identifies the location of config params for this child.  We later write
	// it to the client drivers key.
	if ( NT_SUCCESS(Status) )
		Status = DigiStrAllocUnicodeString( &ppdoExtension->RegPath, pParametersKey->Buffer, L"\\", pServiceKey->Buffer );

	if ( NT_SUCCESS(Status) )
	{
		// ServiceSubkey, e.g. last key in {NetGUID}\0001\Parameters\ASYN\0\0, is a 
		// one or two digit number.  Convert it to an int, later we'll sprintf it back 
		// into a two digit string in the InstanceId.
		if ( pServiceSubkey )
			Status = RtlUnicodeStringToInteger( pServiceSubkey, 10, &iKeyName );
		else
		{
			Status = STATUS_SUCCESS;
			iKeyName = 0;
		}
	}

	if ( NT_SUCCESS(Status) )
	{
		// Create this child device instance id.  The Id must be unique for this 
		// adapter, so use the three identifying numbers:
		// 	a) the zero-based service index (based on order of LineServices regval)
		// 	b) The zero-based line number
		// 	c) the arbitrary regkey name for this child's config key
		// For single child-per-line clients (e.g. ISDN) b and c are redundant.  For 
		// multiple child-per-line clients (e.g. ASYN) b and c uniquely identify the 
		// serial port.
		// For example, if LineServices="ISDN ASYN", child identifiers might be:
		//
		//        Key              InstanceId
		// "Parameters/ISDN/0"     "000000"
		// "Parameters/ISDN/1"     "000101"
		// "Parameters/ASYN/0/0"   "010000"
		// "Parameters/ASYN/1/2"   "010102"

		WCHAR buf[10] = {0};
		swprintf( buf, L"%02i%02i%02i", iService, iLine, iKeyName );
		Status = DigiStrAllocUnicodeString( &ppdoExtension->InstanceId, buf, NULL, NULL );
	}

	// Write devicename used by client driver to open this adapter
	Status = DigiRegWriteSz( hkChild, DXB_REGVAL_DEVICE, fdoExtension->DeviceName.Buffer );

	if ( NT_SUCCESS( Status ) )
	{
		DigiAcquireSpinLock(&fdoExtension->ChildListLock);
		InsertTailList( &fdoExtension->ChildListHead, &ppdoExtension->ChildListEntry );
		DigiReleaseSpinLock(&fdoExtension->ChildListLock);

		DigiDump( DEBUG_DIGIPNP, ( "DsPnp!PnpPdoCreateDevice   PDO 0x%x, pExt 0x%x, %S, %S\n",
								ppdoExtension, ppdoExtension->HardwareId.Buffer, ppdoExtension->InstanceId.Buffer ) );
	}

Exit:
	if ( NT_ERROR(Status) )
	{
		DigiDump( DEBUG_DIGIPNP, ( "DsPnp!PnpPdoCreateDevice: Error creating PDO, status 0x%x\n", Status ) );
		if ( ppdoExtension )
		{
			if ( ppdoExtension->HardwareId.Buffer )
				DigiStrFreeUnicodeString( &ppdoExtension->HardwareId );

			if ( ppdoExtension->RegPath.Buffer )
				DigiStrFreeUnicodeString( &ppdoExtension->RegPath );

			if ( ppdoExtension->InstanceId.Buffer )
				DigiStrFreeUnicodeString( &ppdoExtension->InstanceId );
		}
	}
	return Status;
}



STATIC NTSTATUS
PnpPdoStartDevice(
	PPNP_PDO_EXTENSION ppdoExtension, 
	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:

	ppdoExtension
		Device extension for this child PDO.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	STATUS_SUCCESS, or appropriate error status.

--*/
{
	NTSTATUS Status = STATUS_SUCCESS;
	DigiDump( DEBUG_DIGIPNP, ( "DsPnp!PnpPdoChildStartDevice: ppdoExtension 0x%x\n", ppdoExtension ) );

#if DBG
	DigiGetDeviceProperties(ppdoExtension->pDeviceObject);
#endif

	{
		HANDLE hkDriver;
		WCHAR buf[64] = {0};
		ULONG cb;
		UNICODE_STRING AdapterInstance = {0};	// Instance is last 4 digits of driver keyname + NULL

		Status = IoGetDeviceProperty( ppdoExtension->pDeviceObject, DevicePropertyDriverKeyName, sizeof buf, (PVOID)buf, &cb );
		if ( NT_SUCCESS( Status ) )
		{
			if ( cb > PDO_INSTANCE_DIGITS * sizeof (WCHAR) )
			{
				RtlInitUnicodeString( &AdapterInstance, &buf[cb/sizeof(WCHAR) - PDO_INSTANCE_DIGITS] );
			}
			else
				Status = STATUS_UNSUCCESSFUL;
		}

		if ( NT_SUCCESS( Status ) )
			Status = IoOpenDeviceRegistryKey( ppdoExtension->pDeviceObject, PLUGPLAY_REGKEY_DRIVER, MAXIMUM_ALLOWED, &hkDriver );

		if ( NT_SUCCESS( Status ) )
		{
			HANDLE hkParameters;
			Status = DigiRegOpenSubkey( &hkParameters, hkDriver, DXB_REGKEY_PARAMETERS );
			if ( NT_SUCCESS( Status ) )
			{
				// Write the RegPath (== registry path) defining this object.  Provides a
				// link back to the subkey under the dxb adapter instance.  Used by coinstaller
				// and client drivers to get/set config params, since all params are stored 
				// in the Dxb key, *not* in the client driver instance keys.
				Status = DigiRegWriteSz( hkParameters, DXB_REGVAL_REGPATH, ppdoExtension->RegPath.Buffer );

				// Write the Adapter Instance ("VAdapterNum") value.  Used by Isdn driver
				// to build GetAddressCaps strings.
				if ( NT_SUCCESS( Status ) )
					Status = DigiRegWriteSz( hkParameters, DXB_REGVAL_NDIS_ADAPTER_INSTANCE, AdapterInstance.Buffer );

				(void)DigiRegCloseKey( hkParameters );
			}
			ZwClose( hkDriver );
		}

		if ( NT_ERROR( Status ) )
		{
			DigiDump( DEBUG_DIGIPNP, ( "DsPnp!PnpPdoChildStartDevice: Error writing registry values, status 0x%x!\n", Status ) );
		}
	}

	return Status;
}

VOID
PnpPdoDeleteDevice( 
	PPNP_PDO_EXTENSION ppdoExtension
	)
/*++

Description:

	Delete a single child device.

Parameters:

	pChild
		Device extension to delete

--*/
{
	ASSERT( ppdoExtension->pDeviceObject );
	DigiDump( DEBUG_DIGIPNP, ( "DsPnp!PnpPdoDeleteDevice: deleting child PDO 0x%08x, ext 0x%08x\n", ppdoExtension->pDeviceObject, ppdoExtension ) );

	if ( ppdoExtension->RegPath.Buffer )
		DigiStrFreeUnicodeString( &ppdoExtension->RegPath );
	if ( ppdoExtension->InstanceId.Buffer )
		DigiStrFreeUnicodeString( &ppdoExtension->InstanceId );
	if ( ppdoExtension->HardwareId.Buffer )
		DigiStrFreeUnicodeString( &ppdoExtension->HardwareId );

	IoDeleteDevice( ppdoExtension->pDeviceObject );
}



NTSTATUS
PnpPdoRemoveDevice(
	PPNP_PDO_EXTENSION ppdoExtension,
	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.

Return Value:

	This routine must return STATUS_SUCCESS.

--*/
{
	NTSTATUS Status = STATUS_SUCCESS;
	DigiDump( DEBUG_DIGIPNP, ( "DsPnp!PnpPdoRemoveDevice: ppdoExtension 0x%x\n", ppdoExtension ) );
	return Status;
}


NTSTATUS
PnpPdoDispatch(
	PDEVICE_OBJECT	   pDeviceObject, 
	PIRP               pIrp
	)
/*++

Description:

	Dispatch routine for all child PDO related PNP requests.

Parameters:

	ppdoExtension
		Device extension for the PDO.
	
	pIrp
		Request.

	pIos
		Parameters passed along with Irp.

Return Value:

	STATUS_SUCCESS, or some error value.

--*/
{
	NTSTATUS			Status = pIrp->IoStatus.Status;
	PIO_STACK_LOCATION	pIos	= IoGetCurrentIrpStackLocation( pIrp );
	ULONG const			Minor = pIos->MinorFunction;
	ULONG_PTR			Information = pIrp->IoStatus.Information;

	PPNP_PDO_EXTENSION ppdoExtension =  (PPNP_PDO_EXTENSION) pDeviceObject->DeviceExtension;
	ASSERT( ppdoExtension->DeviceType = PNP_DEVICE_TYPE_PDO );

	// Some PNP Irps require that we complete the Irp w/o modifying the status value!!

	DigiDump( DEBUG_DIGIPNP, ( "DsPnp!PnpPdoDispatch: %s, ppdoExtension 0x%08x, pIrp 0x%08x\n",
										DigiGetPnpIrpName(Minor),
										ppdoExtension,
										pIrp ) );

	switch ( Minor )
	{
		case IRP_MN_QUERY_STOP_DEVICE:
		case IRP_MN_STOP_DEVICE:
		case IRP_MN_CANCEL_STOP_DEVICE:
		case IRP_MN_QUERY_REMOVE_DEVICE:
		case IRP_MN_CANCEL_REMOVE_DEVICE:
		case IRP_MN_SURPRISE_REMOVAL:
			Status = STATUS_SUCCESS;
			Information = 0;
			break;

		case IRP_MN_START_DEVICE:
			Status = PnpPdoStartDevice( ppdoExtension, pIrp, pIos );
			Information = 0;
			break;

		case IRP_MN_REMOVE_DEVICE:	
			Status = PnpPdoRemoveDevice( ppdoExtension, pIrp, pIos );
			Information = 0;
			break;

		case IRP_MN_QUERY_PNP_DEVICE_STATE:			
			Status = STATUS_SUCCESS;
			Information = PNP_DEVICE_DONT_DISPLAY_IN_UI;
			break;

		case IRP_MN_QUERY_CAPABILITIES:
		{
			PDEVICE_CAPABILITIES pCaps = pIos->Parameters.DeviceCapabilities.Capabilities;
			ASSERT( pCaps->Size == sizeof *pCaps );
			pCaps->LockSupported = FALSE;
			pCaps->EjectSupported = FALSE;
			pCaps->Removable = FALSE;
			pCaps->DockDevice = FALSE;
			pCaps->UniqueID = FALSE;
			pCaps->SilentInstall = TRUE;
			pCaps->RawDeviceOK = FALSE;
			pCaps->SurpriseRemovalOK = TRUE;
//			pCaps->Address = ;	// leave at default of 0xFFFFFFFF
//			pCaps->UINumber = ;	// leave at default of 0xFFFFFFFF
			pCaps->DeviceState[PowerSystemWorking]   = PowerDeviceD0;
			pCaps->DeviceState[PowerSystemSleeping1] = PowerDeviceD3;
			pCaps->DeviceState[PowerSystemSleeping2] = PowerDeviceD3;
			pCaps->DeviceState[PowerSystemSleeping3] = PowerDeviceD3;
			pCaps->DeviceState[PowerSystemHibernate] = PowerDeviceD3;
			pCaps->DeviceState[PowerSystemShutdown]  = PowerDeviceD3;
			pCaps->SystemWake = PowerSystemUnspecified;
			pCaps->DeviceWake = PowerSystemUnspecified;
			#define ONE_SECOND 10000
			pCaps->D1Latency = 0;
			pCaps->D2Latency = 0;
			pCaps->D3Latency = 20 * ONE_SECOND;

			Status = STATUS_SUCCESS;
			Information = 0;
			break;
		}

		case IRP_MN_QUERY_DEVICE_RELATIONS:
		{
			DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildQueryDeviceRelations: ppdoExtension 0x%x, QueryType %s\n", ppdoExtension,
			          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 TargetDeviceRelation:
				{
					// Must be ExAllocated from PagedPool!  Freed later by PNP manager
					PDEVICE_RELATIONS pRelations = ExAllocatePool( PagedPool, sizeof *pRelations );
					if ( !pRelations )
					{
						Status = STATUS_INSUFFICIENT_RESOURCES;
						Information = 0;
						break;
					}
					pRelations->Count = 1;
					pRelations->Objects[0] = ppdoExtension->pDeviceObject;

					// Reference it because we're returning a pointer to it.  PNP Manager will 
					// decrement reference when it's done.
					ObReferenceObject( ppdoExtension->pDeviceObject );
					Status = STATUS_SUCCESS;
					Information = (ULONG_PTR)pRelations;
					break;
				}

				case EjectionRelations:
				case PowerRelations:
				case RemovalRelations:
				case BusRelations:
				default:
					Status = pIrp->IoStatus.Status;
					break;
			}
			break;
		}

		case IRP_MN_QUERY_ID:
		{
			UNICODE_STRING BusName;
			RtlInitUnicodeString( &BusName, gDriverData.InitData.BusName );

			switch ( pIos->Parameters.QueryId.IdType )
			{
				case BusQueryDeviceID:
				{
					// DeviceId is <Bus>\<HardwareId>, i.e. "DXB\DIGIPRI"
					// DeviceId is not multi_sz so terminate with single NULL.
					USHORT Length = BusName.Length + ppdoExtension->HardwareId.Length + sizeof(WCHAR);
					PWCHAR pBuf = ExAllocatePool( NonPagedPool, Length );
					if ( !pBuf )
					{
						Status = STATUS_INSUFFICIENT_RESOURCES;
						Information = 0;
					}
					else
					{
						UNICODE_STRING Id;
						Id.Buffer = pBuf;
						Id.Length = 0;
						Id.MaximumLength = Length;
						RtlCopyUnicodeString( &Id, &BusName );
						RtlAppendUnicodeStringToString( &Id, &ppdoExtension->HardwareId );
						DigiDump( DEBUG_DIGIPNP, ( "DsPnP!ChildDispatch: BusQueryDeviceId: %S\n", Id.Buffer ) );

						Status = STATUS_SUCCESS;
						Information = (ULONG_PTR)Id.Buffer;
					}
					break;
				}

				case BusQueryInstanceID:
				{
					// InstanceId is <AdapterInstance>, i.e. "0001"
					// InstanceId is not multi_sz so terminate with single NULL.
					USHORT Length = ppdoExtension->InstanceId.Length + sizeof(WCHAR);
					PWSTR buf = ExAllocatePool( NonPagedPool, Length );
					if ( !buf )
					{
						Status = STATUS_INSUFFICIENT_RESOURCES;
						Information = 0;
					}
					else
					{
						UNICODE_STRING Id;
						Id.Buffer = buf;
						Id.Length = 0;
						Id.MaximumLength = Length;
						RtlCopyUnicodeString( &Id, &ppdoExtension->InstanceId );
						DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildDispatch: BusQueryInstanceId: %S\n", Id.Buffer ) );
						Status = STATUS_SUCCESS;
						Information = (ULONG_PTR)Id.Buffer;
					}
					break;
				}

				case BusQueryHardwareIDs:
				{
					// HardwareId is <Bus>\<HardwareId>, i.e. "DXB\DIGIPRI"
					// ** HardwareIDs is multi_sz so terminate with double NULL **
					USHORT Length = BusName.Length + ppdoExtension->HardwareId.Length + 2*sizeof(WCHAR);
					PWCHAR pBuf = ExAllocatePool( NonPagedPool, Length );
					if ( !pBuf )
					{
						Status = STATUS_INSUFFICIENT_RESOURCES;
						Information = 0;
					}
					else
					{
						UNICODE_STRING Id;
						Id.Buffer = pBuf;
						Id.Length = 0;
						Id.MaximumLength = Length;
						RtlZeroMemory( pBuf, Length );	// ensure double-null termination
						RtlCopyUnicodeString( &Id, &BusName );
						RtlAppendUnicodeStringToString( &Id, &ppdoExtension->HardwareId );
						DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildDispatch: BusQueryHardwareIDs: %S\n", Id.Buffer ) );
						Status = STATUS_SUCCESS;
						Information = (ULONG_PTR)Id.Buffer;
					}
					break;
				}
					
				case BusQueryCompatibleIDs:
					// ** CompatibleIDs is multi_sz so terminate with double NULL **
					DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildDispatch: BusQueryCompatibleIds, none.\n") );
					Status = STATUS_SUCCESS;
					Information = 0;
					break;
					
				default:
					DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildDispatch: *** Unexpected QUERY_ID type, 0x%x! ***\n", pIos->Parameters.QueryId.IdType ) );
					Status = STATUS_NOT_SUPPORTED;
					Information = 0;
					break;
			}
			break;
		}
			
		case IRP_MN_QUERY_DEVICE_TEXT:
		{
			switch ( pIos->Parameters.QueryDeviceText.DeviceTextType )
			{
				case DeviceTextDescription:
				{
					WCHAR Description[] = L"Digi PNP Device";
					PWCHAR pDescription = ExAllocatePool( NonPagedPool, (wcslen(Description)+ 1)*sizeof(WCHAR) );
					if ( !pDescription )
					{
						Status = STATUS_INSUFFICIENT_RESOURCES;
						Information = 0;
						break;
					}
					wcscpy( pDescription, Description );
					DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildDispatch: DeviceTextDescription: %S\n", pDescription ) );
					Status = STATUS_SUCCESS;
					Information = (ULONG_PTR)pDescription;
					break;
				}

				case DeviceTextLocationInformation:
				{
					WCHAR Location[] = L"Digi PNP adapter";
					PWCHAR pLocation = ExAllocatePool( NonPagedPool, (wcslen(Location)+ 1)*sizeof(WCHAR) );
					if ( !pLocation )
					{
						Status = STATUS_INSUFFICIENT_RESOURCES;
						Information = 0;
						break;
					}
					wcscpy( pLocation, Location );
					DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildDispatch: DeviceTextLocationInformation, %S\n", pLocation ) );
					Status = STATUS_SUCCESS;
					Information = (ULONG_PTR)pLocation;
					break;
					
				}

				default:
					DigiDump( DEBUG_DIGIPNP, ( "DsPnp!ChildDispatch: *** Unexpected QUERY_DEVICE_TEXT type, 0x%x! ***\n", pIos->Parameters.QueryDeviceText.DeviceTextType ) );
					Status = STATUS_NOT_SUPPORTED;
					Information = 0;
					break;
			}
			break;
		}

//		case IRP_MN_QUERY_BUS_INFORMATION:			break;
//		case IRP_MN_QUERY_PNP_DEVICE_STATE:			break;
//		case IRP_MN_FILTER_RESOURCE_REQUIREMENTS:	break;
//		case IRP_MN_DEVICE_USAGE_NOTIFICATION:		break;
//		case IRP_MN_QUERY_RESOURCES:				break;
//		case IRP_MN_QUERY_RESOURCE_REQUIREMENTS:	break;
//		case IRP_MN_QUERY_INTERFACE:				break;
//		case IRP_MN_READ_CONFIG:					break;
//		case IRP_MN_WRITE_CONFIG:					break;
//		case IRP_MN_EJECT:							break;
//		case IRP_MN_SET_LOCK:						break;
//		case IRP_MN_QUERY_LEGACY_BUS_INFORMATION:	break;

		default:
			DigiDump( DEBUG_DIGIPNP, ( "DsPnP!PnpPdoDispatch: PNP Irp not supported, %s, (0x%x)!\n", DigiGetPnpIrpName(Minor), Minor ) );
			Status = STATUS_NOT_SUPPORTED;
			Information = 0;
			break;
	}
	DsPnpIoCompleteRequest( pIrp, Status, Information );

	if ( NT_ERROR( Status ) )
	{
		DigiDump( DEBUG_DIGIPNP, ( "DsPnP!PnpPdoDispatch: %s, returning failure status 0x%x\n", DigiGetPnpIrpName(Minor), Status ) );
	}
	return Status;
}

#endif // NT5_BUILD