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

   digipnp.c

Abstract:

   This module contains Windows 2000 PnP/Power Utilities.

Author:

   Mike Zarns

--*/

#include "digilib.h"

#if NT5_BUILD

///////////////////////////////////////////////////////////////////////////
//
//	W2K Utilities
//
///////////////////////////////////////////////////////////////////////////

NTSTATUS
DigiGetDeviceInstance( 
	IN PDEVICE_OBJECT pPdo,
	IN OUT PWCHAR     buf,
	IN ULONG          cb
    )

/*++

Routine Description:

    Ask the PDO for the Driver Key Name and Get the Instance Number for
	the Adapter (i.e. 0001)

Arguments:

    PDEVICE_OBJECT	- The Lower Level Device POD.
	buf				- Storage for the Instance String
	cd				- bytes of storage available for the Instance String


Return Value:

    A valid NTSTATUS code.

--*/

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

PWCHAR
DigiGetInstanceString( 
	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;
}

///////////////////////////////////////////////////////////////////////////
//
//	PnP Debug Utilities
//
///////////////////////////////////////////////////////////////////////////

#if DBG

STATIC const struct DIGIPNP_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"
};


STATIC PCSTR
DigiGetIrpName( ULONG Minor,
			    DIGIPNP_MINOR_TABLE const Table[],
				ULONG nEntries
	)
{
	static const CHAR Unknown[] = "Unknown";
	PCSTR Name = Unknown;
	ULONG i;
	for ( i = 0; i < nEntries; i++ )
	{
		if ( Minor == Table[i].Minor )
		{
			Name = Table[i].Name;
			break;
		}
	}
	return Name;
}


PCSTR 
DigiGetPnpIrpName(
	IN ULONG Minor
	)
{
	return DigiGetIrpName( Minor, PnpIrps, ARRAY_SIZE(PnpIrps) );
}



VOID
DigiGetDeviceProperties( 
	IN PDEVICE_OBJECT pPdo
	)
{


	// IoGetDeviceProperties
	struct Property
	{
		ULONG Id;
		PWSTR Name;

	} PropertyList[] = 

	{
		{DevicePropertyDeviceDescription, 			L"DevicePropertyDeviceDescription"},
		{DevicePropertyHardwareID,					L"DevicePropertyHardwareID"},
		{DevicePropertyClassName, 					L"DevicePropertyClassName"},
		{DevicePropertyClassGuid, 					L"DevicePropertyClassGuid"},
		{DevicePropertyDriverKeyName, 				L"DevicePropertyDriverKeyName"},
		{DevicePropertyManufacturer, 				L"DevicePropertyManufacturer"},
		{DevicePropertyFriendlyName, 				L"DevicePropertyFriendlyName"},
		{DevicePropertyLocationInformation, 		L"DevicePropertyLocationInformation"},
		{DevicePropertyPhysicalDeviceObjectName,	L"DevicePropertyPhysicalDeviceObjectName"},
		{DevicePropertyEnumeratorName, 				L"DevicePropertyEnumeratorName"},
	};

	ULONG i;
	for ( i = 0; i < ARRAY_SIZE(PropertyList); i++ )
	{
		NTSTATUS s;
		WCHAR Buffer[256] = {0};
		ULONG cb;
		s = IoGetDeviceProperty( pPdo, PropertyList[i].Id, sizeof Buffer, (PVOID)Buffer, &cb );
		if ( NT_SUCCESS( s ) )
		{
			DigiDump( DEBUG_DIGIPNP, ( "Digilib!DeviceProperty: %S: %S\n", PropertyList[i].Name, Buffer ) );
		}
		else
		{
			DigiDump( DEBUG_DIGIPNP, ( "Digilib!PnpStartDevice: Error reading %S!\n", PropertyList[i].Name ) );
		}
	}
}

#endif

///////////////////////////////////////////////////////////////////////////
//
//	PnP Irp Processing
//
///////////////////////////////////////////////////////////////////////////

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


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

	KeInitializeEvent( &Event, SynchronizationEvent, FALSE );

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

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


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


///////////////////////////////////////////////////////////////////////////
//
//	Power Debug Utilities
//
///////////////////////////////////////////////////////////////////////////

#if DBG


PCSTR DigiGetPowerIrpName( ULONG Minor )
{
	
	const struct DIGIPNP_MINOR_TABLE PowerIrps[] = 
	{
		IRP_MN_SET_POWER,			"IRP_MN_SET_POWER",
		IRP_MN_QUERY_POWER,			"IRP_MN_QUERY_POWER",
		IRP_MN_WAIT_WAKE,			"IRP_MN_WAIT_WAKE",
		IRP_MN_POWER_SEQUENCE,		"IRP_MN_POWER_SEQUENCE"
	};

	return DigiGetIrpName( Minor, PowerIrps, ARRAY_SIZE( PowerIrps ) );
}



PCSTR 
DigiGetPowerSystemState(
	IN ULONG Type
	)
{

	const struct DIGIPNP_MINOR_TABLE PowerSystemStates[] =
	{
		PowerSystemUnspecified,		"PowerSystemUnspecified",
		PowerSystemWorking,			"PowerSystemWorking",
		PowerSystemSleeping1,		"PowerSystemSleeping1",
		PowerSystemSleeping2,		"PowerSystemSleeping2",
		PowerSystemSleeping3,		"PowerSystemSleeping3",
		PowerSystemHibernate,		"PowerSystemHibernate",
		PowerSystemShutdown,		"PowerSystemShutdown"
	};

	return DigiGetIrpName( (ULONG)(Type), PowerSystemStates, ARRAY_SIZE( PowerSystemStates ) );
}

PCSTR 
DigiGetPowerDeviceState(
	IN ULONG Type
	)
{
	
	const struct DIGIPNP_MINOR_TABLE PowerDeviceStates[] =
	{
		PowerDeviceUnspecified,		"PowerDeviceUnspecified",
		PowerDeviceD0,				"PowerDeviceD0",
		PowerDeviceD1,				"PowerDeviceD1",
		PowerDeviceD2,				"PowerDeviceD2",
		PowerDeviceD3,				"PowerDeviceD3"
	};

	return DigiGetIrpName( (ULONG)(Type), PowerDeviceStates, ARRAY_SIZE( PowerDeviceStates ) );
}

#endif

///////////////////////////////////////////////////////////////////////////
//
//	Power Irp Processing
//
///////////////////////////////////////////////////////////////////////////


NTSTATUS
DigiPowerForwardIrp( 
	IN PDEVICE_OBJECT pDevice, 
	IN OUT PIRP       pIrp
	)
{
	ASSERT( pDevice );
	ASSERT( pIrp );
	PoStartNextPowerIrp( pIrp );
	IoSkipCurrentIrpStackLocation( pIrp );
	return PoCallDriver( pDevice, pIrp );
}


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


NTSTATUS
DigiPowerForwardIrpWait( 
	IN PDEVICE_OBJECT pDevice, 
	IN OUT PIRP       pIrp
	)
{
	KEVENT Event;
	NTSTATUS Status;
	ASSERT( pDevice );
	ASSERT( pIrp );

	KeInitializeEvent( &Event, SynchronizationEvent, FALSE );

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

	Status = PoCallDriver( pDevice, pIrp );
	if ( Status == STATUS_PENDING )
	{
		KeWaitForSingleObject( &Event, Executive, KernelMode, FALSE, NULL );
		Status = pIrp->IoStatus.Status;
	}
	return Status;
}


VOID
DigiPowerComplete( 
	IN PDEVICE_OBJECT   pDevice,
	IN UCHAR            MinorFunction,
	IN POWER_STATE      PowerState,
	IN PVOID            Context,
	IN PIO_STATUS_BLOCK IoStatus
	)
{
	const PIRP pSystemPowerIrp = Context;
	DigiPowerForwardIrp( pDevice, pSystemPowerIrp );
}

#endif