You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
924 lines
22 KiB
924 lines
22 KiB
/*++
|
|
|
|
Copyright (c) 1997-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
utils.c
|
|
|
|
Abstract:
|
|
|
|
This module provides general utility functions.
|
|
|
|
Author:
|
|
|
|
Andy Thornton (andrewth) 20-Oct-97
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "mfp.h"
|
|
|
|
VOID
|
|
MfInitCommonExtension(
|
|
PMF_COMMON_EXTENSION Common,
|
|
MF_OBJECT_TYPE Type
|
|
);
|
|
|
|
NTSTATUS
|
|
MfGetSubkeyByIndex(
|
|
IN HANDLE ParentHandle,
|
|
IN ULONG Index,
|
|
IN ACCESS_MASK Access,
|
|
OUT PHANDLE ChildHandle,
|
|
OUT PUNICODE_STRING Name
|
|
);
|
|
|
|
NTSTATUS
|
|
MfGetRegistryValue(
|
|
IN HANDLE Handle,
|
|
IN PWSTR Name,
|
|
IN ULONG Type,
|
|
IN ULONG Flags,
|
|
IN OUT PULONG DataLength,
|
|
IN OUT PVOID *Data
|
|
);
|
|
|
|
NTSTATUS
|
|
MfSendPnpIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_STACK_LOCATION Location,
|
|
OUT PULONG_PTR Information OPTIONAL
|
|
);
|
|
|
|
DEVICE_POWER_STATE
|
|
MfFindLowestChildPowerState(
|
|
IN PMF_PARENT_EXTENSION Parent
|
|
);
|
|
|
|
NTSTATUS
|
|
MfPowerRequestCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
);
|
|
|
|
NTSTATUS
|
|
MfSendSetPowerIrp(
|
|
IN PDEVICE_OBJECT Target,
|
|
IN POWER_STATE State
|
|
);
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
|
|
// NOTE: Should see if the rest of the funcs can be moved out of this
|
|
// file.
|
|
|
|
#pragma alloc_text(PAGE, MfInitCommonExtension)
|
|
#pragma alloc_text(PAGE, MfGetSubkeyByIndex)
|
|
#pragma alloc_text(PAGE, MfGetRegistryValue)
|
|
#pragma alloc_text(PAGE, MfSendPnpIrp)
|
|
|
|
#endif
|
|
|
|
|
|
VOID
|
|
MfInitCommonExtension(
|
|
PMF_COMMON_EXTENSION Common,
|
|
MF_OBJECT_TYPE Type
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This initializes the fields in the common header of the device extension
|
|
|
|
Arguments:
|
|
|
|
Common - The common header to initialize
|
|
|
|
Type - The type of the object being initialized (ie PDO or FDO)
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
Common->Type = Type;
|
|
}
|
|
|
|
VOID
|
|
MfFreeDeviceInfo(
|
|
PMF_DEVICE_INFO Info
|
|
)
|
|
{
|
|
if (Info->Name.Buffer) {
|
|
ExFreePool(Info->Name.Buffer);
|
|
Info->Name.Buffer = NULL;
|
|
}
|
|
|
|
if (Info->HardwareID.Buffer) {
|
|
ExFreePool(Info->HardwareID.Buffer);
|
|
Info->HardwareID.Buffer = NULL;
|
|
}
|
|
|
|
if (Info->CompatibleID.Buffer) {
|
|
ExFreePool(Info->CompatibleID.Buffer);
|
|
Info->CompatibleID.Buffer = NULL;
|
|
}
|
|
|
|
if (Info->ResourceMap) {
|
|
ExFreePool(Info->ResourceMap);
|
|
Info->ResourceMap = NULL;
|
|
}
|
|
|
|
if (Info->VaryingResourceMap) {
|
|
ExFreePool(Info->VaryingResourceMap);
|
|
Info->VaryingResourceMap = NULL;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
MfGetSubkeyByIndex(
|
|
IN HANDLE ParentHandle,
|
|
IN ULONG Index,
|
|
IN ACCESS_MASK Access,
|
|
OUT PHANDLE ChildHandle,
|
|
OUT PUNICODE_STRING Name
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This returns the name and a handle to a subkey given that keys index
|
|
|
|
Arguments:
|
|
|
|
ParentHandle - The handle of the key the subkeys are located under
|
|
|
|
Index - The index of the subkey required
|
|
|
|
Access - The type of access required to the subkey
|
|
|
|
ChildHandle - On success contains a handle to the subkey
|
|
|
|
Name - On success contains the name of the subkey
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#define INFO_BUFFER_SIZE sizeof(KEY_BASIC_INFORMATION) + 255*sizeof(WCHAR)
|
|
|
|
NTSTATUS status;
|
|
UCHAR buffer[INFO_BUFFER_SIZE];
|
|
PKEY_BASIC_INFORMATION info = (PKEY_BASIC_INFORMATION) buffer;
|
|
ULONG resultSize;
|
|
HANDLE childHandle;
|
|
UNICODE_STRING string = {0};
|
|
OBJECT_ATTRIBUTES attributes;
|
|
|
|
status = ZwEnumerateKey(ParentHandle,
|
|
Index,
|
|
KeyBasicInformation,
|
|
info,
|
|
INFO_BUFFER_SIZE,
|
|
&resultSize
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the name
|
|
//
|
|
|
|
ASSERT(info->NameLength <= MAXUSHORT);
|
|
|
|
string.Length = (USHORT) info->NameLength;
|
|
string.MaximumLength = (USHORT) info->NameLength;
|
|
string.Buffer = ExAllocatePoolWithTag(PagedPool,
|
|
info->NameLength,
|
|
MF_POOL_TAG
|
|
);
|
|
|
|
if (!string.Buffer) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(string.Buffer, info->Name, info->NameLength);
|
|
|
|
//
|
|
// Open the name to get a handle
|
|
//
|
|
|
|
InitializeObjectAttributes(&attributes,
|
|
&string,
|
|
0, //Attributes
|
|
ParentHandle,
|
|
NULL //SecurityDescriptoy
|
|
);
|
|
|
|
status = ZwOpenKey(&childHandle,
|
|
Access,
|
|
&attributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
DEBUG_MSG(1, ("\tSubkey %wZ\n", &string));
|
|
|
|
//
|
|
// Hand the name back to the caller
|
|
//
|
|
|
|
Name->Buffer = string.Buffer;
|
|
Name->Length = string.Length;
|
|
Name->MaximumLength = string.MaximumLength;
|
|
|
|
*ChildHandle = childHandle;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (string.Buffer) {
|
|
ExFreePool(string.Buffer);
|
|
}
|
|
//
|
|
// We should never be able to overflow as our buffer is the max size of
|
|
// a registry key name
|
|
//
|
|
ASSERT(status != STATUS_BUFFER_OVERFLOW);
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
MfGetRegistryValue(
|
|
IN HANDLE Handle,
|
|
IN PWSTR Name,
|
|
IN ULONG Type,
|
|
IN ULONG Flags,
|
|
IN OUT PULONG DataLength,
|
|
IN OUT PVOID *Data
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This retrieves a value key from the registry performing type and sanity
|
|
checking
|
|
|
|
Arguments:
|
|
|
|
Handle - The key the values are located under
|
|
|
|
Name - The name of the value
|
|
|
|
Type - The type (REG_*) of the value
|
|
|
|
DataLength - Points to the length of the data buffer, on success contains
|
|
the size of the data
|
|
|
|
Data - Pointer to pointer to the buffer to return the data in, if points to
|
|
NULL a buffer of the right size should be allocated (in this case
|
|
DataLength should be 0)
|
|
|
|
Return Value:
|
|
|
|
Status code that indicates whether or not the function was successful. If
|
|
the type of the object is not Type then we fail with STATUS_OBJECT_TYPE_MISMATCH
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#define VALUE_BUFFER_SIZE PAGE_SIZE
|
|
|
|
NTSTATUS status;
|
|
PKEY_VALUE_PARTIAL_INFORMATION info = NULL;
|
|
ULONG size = VALUE_BUFFER_SIZE, length;
|
|
UNICODE_STRING string;
|
|
BOOLEAN convertSzToMultiSz;
|
|
PWCHAR stringEnd, remainingBufferPtr;
|
|
SIZE_T stringLength, sizeRemaining;
|
|
ULONG remainingBufferLength;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Check parameters.
|
|
// For all types but REG_DWORD, which has special rules, if the caller
|
|
// provided a buffer, make sure he also provided the size of the buffer.
|
|
//
|
|
|
|
if ((Type != REG_DWORD) &&
|
|
(*Data && (!DataLength || !(*DataLength)))) {
|
|
|
|
ASSERT(FALSE);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(&string, Name);
|
|
|
|
info = ExAllocatePoolWithTag(PagedPool,
|
|
VALUE_BUFFER_SIZE,
|
|
MF_POOL_TAG
|
|
);
|
|
if (!info) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
while ((status = ZwQueryValueKey(Handle,
|
|
&string,
|
|
KeyValuePartialInformation,
|
|
info,
|
|
size,
|
|
&size
|
|
)) == STATUS_BUFFER_OVERFLOW) {
|
|
ExFreePool(info);
|
|
|
|
info = ExAllocatePoolWithTag(PagedPool,
|
|
size,
|
|
MF_POOL_TAG
|
|
);
|
|
|
|
if (!info) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
|
|
convertSzToMultiSz = (Type == REG_MULTI_SZ
|
|
&& info->Type == REG_SZ
|
|
&& Flags & MF_GETREG_SZ_TO_MULTI_SZ
|
|
);
|
|
|
|
//
|
|
// Make sure the type we got back is what we expected
|
|
//
|
|
|
|
if (info->Type != Type && !convertSzToMultiSz) {
|
|
|
|
status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Apply various common sense checks based on the type.
|
|
// At the same time, compute the size of the buffer needed
|
|
// to store the data.
|
|
//
|
|
|
|
if (info->Type == REG_DWORD) {
|
|
|
|
//
|
|
// If the data is a REG_DWORD then Data is a pointer to a ULONG to store
|
|
// the data. This is different behavior than all other data types, so
|
|
// exit out after doing this processing.
|
|
//
|
|
|
|
ASSERT(info->DataLength == sizeof(ULONG));
|
|
if (info->DataLength < sizeof(ULONG)) {
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(Data, info->Data, sizeof(ULONG));
|
|
|
|
status = STATUS_SUCCESS;
|
|
goto cleanup;
|
|
|
|
} else if (info->Type == REG_SZ) {
|
|
|
|
if (FAILED(StringCbLength((PWCHAR)info->Data, info->DataLength, &stringLength))) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
ASSERT(stringLength <= MAXULONG);
|
|
|
|
//
|
|
// Account for the necessary NULLs in the
|
|
// required buffer size calculation.
|
|
//
|
|
if (convertSzToMultiSz) {
|
|
length = (ULONG)stringLength + 2*sizeof(UNICODE_NULL);
|
|
} else {
|
|
length = (ULONG)stringLength + sizeof(UNICODE_NULL);
|
|
}
|
|
|
|
} else if (info->Type == REG_MULTI_SZ) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
//
|
|
// Iterate over the buffer looking for strings.
|
|
// Initialize the "effective" size of the buffer to 1 character
|
|
// smaller than the buffer actually is, because we always need
|
|
// to look one character past the end of the string for the
|
|
// second NULL terminator
|
|
//
|
|
remainingBufferPtr = (PWCHAR)info->Data;
|
|
remainingBufferLength = (info->DataLength / sizeof(WCHAR)) - 1;
|
|
length = 0;
|
|
|
|
while (remainingBufferLength) {
|
|
|
|
//
|
|
// First see if we can find a NULL-terminated string
|
|
// in the remaining buffer. If not, this is not a MULTI_SZ.
|
|
//
|
|
if (FAILED(StringCchLength(remainingBufferPtr,
|
|
remainingBufferLength,
|
|
&stringLength
|
|
))) {
|
|
|
|
goto cleanup;
|
|
}
|
|
|
|
length += ((ULONG)stringLength+1)*sizeof(WCHAR);
|
|
|
|
//
|
|
// Look for a second NULL terminator after the end of
|
|
// the string. If it exists, this is a MULTI_SZ.
|
|
//
|
|
if (remainingBufferPtr[(ULONG)stringLength+1] == UNICODE_NULL) {
|
|
|
|
length += sizeof(UNICODE_NULL);
|
|
|
|
status = STATUS_SUCCESS;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If not, advance past the single-null-terminated string
|
|
// we did find and try again.
|
|
//
|
|
remainingBufferLength -= ((ULONG)stringLength+1);
|
|
remainingBufferPtr += ((ULONG)stringLength+1);
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
} else {
|
|
|
|
length = info->DataLength;
|
|
}
|
|
|
|
//
|
|
// Now allocate a buffer if necessary, and copy the data over.
|
|
//
|
|
if (*Data) {
|
|
|
|
//
|
|
// If the user supplied a buffer then make sure its big enough and use it
|
|
// otherwise allocate one.
|
|
//
|
|
|
|
if (*DataLength < length) {
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
goto cleanup;
|
|
}
|
|
} else {
|
|
*Data = ExAllocatePoolWithTag(PagedPool,
|
|
length,
|
|
MF_POOL_TAG
|
|
);
|
|
if (!*Data) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
}
|
|
|
|
if (convertSzToMultiSz) {
|
|
|
|
//
|
|
// Copy the string into the destination buffer
|
|
// and add in the second NULL
|
|
//
|
|
if (FAILED(StringCbCopyEx((PWCHAR)*Data, // Destination
|
|
length, // Destination length
|
|
(PWCHAR)info->Data, // Source
|
|
&stringEnd, // Ptr to end of copy
|
|
&sizeRemaining, // bytes left in the destination
|
|
0
|
|
))) {
|
|
ASSERT(FALSE);
|
|
status = STATUS_INVALID_PARAMETER;
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// In the sanity checking portion of this function,
|
|
// we made sure the buffer was big enough for both
|
|
// NULLs. ASSERT that it's true here anyway.
|
|
//
|
|
ASSERT(sizeRemaining >= 2*sizeof(UNICODE_NULL));
|
|
|
|
*(stringEnd+1) = UNICODE_NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Nothing special to do. Just copy.
|
|
//
|
|
RtlCopyMemory(*Data, info->Data, length);
|
|
}
|
|
|
|
*DataLength = length;
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
cleanup:
|
|
|
|
if (info) {
|
|
ExFreePool(info);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfSendPnpIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIO_STACK_LOCATION Location,
|
|
OUT PULONG_PTR Information OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This builds and send a pnp irp to a device.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The a device in the device stack the irp is to be sent to -
|
|
the top of the device stack is always found and the irp sent there first.
|
|
|
|
Location - The initial stack location to use - contains the IRP minor code
|
|
and any parameters
|
|
|
|
Information - If provided contains the final value of the irps information
|
|
field.
|
|
|
|
Return Value:
|
|
|
|
The final status of the completed irp or an error if the irp couldn't be sent
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status;
|
|
PIRP irp = NULL;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PDEVICE_OBJECT targetDevice = NULL;
|
|
KEVENT irpCompleted;
|
|
IO_STATUS_BLOCK statusBlock;
|
|
|
|
ASSERT(Location->MajorFunction == IRP_MJ_PNP);
|
|
|
|
//
|
|
// Find out where we are sending the irp
|
|
//
|
|
|
|
targetDevice = IoGetAttachedDeviceReference(DeviceObject);
|
|
|
|
//
|
|
// Get an IRP
|
|
//
|
|
|
|
KeInitializeEvent(&irpCompleted, SynchronizationEvent, FALSE);
|
|
|
|
irp = IoBuildSynchronousFsdRequest(IRP_MJ_PNP,
|
|
targetDevice,
|
|
NULL, // Buffer
|
|
0, // Length
|
|
0, // StartingOffset
|
|
&irpCompleted,
|
|
&statusBlock
|
|
);
|
|
|
|
|
|
if (!irp) {
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
goto cleanup;
|
|
}
|
|
|
|
irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
|
|
irp->IoStatus.Information = 0;
|
|
|
|
//
|
|
// Initialize the stack location
|
|
//
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
ASSERT(irpStack->MajorFunction == IRP_MJ_PNP);
|
|
|
|
irpStack->MinorFunction = Location->MinorFunction;
|
|
irpStack->Parameters = Location->Parameters;
|
|
|
|
//
|
|
// Call the driver and wait for completion
|
|
//
|
|
|
|
status = IoCallDriver(targetDevice, irp);
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(&irpCompleted, Executive, KernelMode, FALSE, NULL);
|
|
status = statusBlock.Status;
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
goto cleanup;
|
|
}
|
|
|
|
//
|
|
// Return the information
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(Information)) {
|
|
*Information = statusBlock.Information;
|
|
}
|
|
|
|
ObDereferenceObject(targetDevice);
|
|
|
|
ASSERT(status == STATUS_PENDING || status == statusBlock.Status);
|
|
|
|
return statusBlock.Status;
|
|
|
|
cleanup:
|
|
|
|
if (targetDevice) {
|
|
ObDereferenceObject(targetDevice);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
DEVICE_POWER_STATE
|
|
MfUpdateChildrenPowerReferences(
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN DEVICE_POWER_STATE PreviousPowerState,
|
|
IN DEVICE_POWER_STATE NewPowerState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Calculates the lowest power state the mf parent can be put into
|
|
based on the power states of its children.
|
|
|
|
Arguments:
|
|
|
|
Parent - The MF parent device
|
|
|
|
Return Value:
|
|
|
|
The lowest power state
|
|
|
|
--*/
|
|
|
|
{
|
|
PMF_CHILD_EXTENSION currentChild;
|
|
PLIST_ENTRY currentEntry;
|
|
DEVICE_POWER_STATE lowest;
|
|
KIRQL oldIrql;
|
|
|
|
DEBUG_MSG(1,
|
|
("Scanning 0x%08x's childrens power states:\n",
|
|
Parent->Self
|
|
));
|
|
|
|
|
|
KeAcquireSpinLock(&Parent->PowerLock, &oldIrql);
|
|
|
|
//
|
|
// ChildrenPowerStates[PowerDeviceUnspecified] will go negative as
|
|
// children leave this state. It will never go positive as
|
|
// children never re-enter this state.
|
|
//
|
|
|
|
Parent->ChildrenPowerReferences[PreviousPowerState]--;
|
|
Parent->ChildrenPowerReferences[NewPowerState]++;
|
|
|
|
//
|
|
// Find the lowest power state
|
|
//
|
|
|
|
for (lowest = PowerDeviceUnspecified; lowest < PowerDeviceMaximum;
|
|
lowest++) {
|
|
if (Parent->ChildrenPowerReferences[lowest] > 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock(&Parent->PowerLock, oldIrql);
|
|
|
|
if (lowest == PowerDeviceMaximum) {
|
|
lowest = PowerDeviceD3;
|
|
}
|
|
|
|
DEBUG_MSG(1, ("Lowest = %s\n", DEVICE_POWER_STRING(lowest)));
|
|
return lowest;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfPowerRequestCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN UCHAR MinorFunction,
|
|
IN POWER_STATE PowerState,
|
|
IN PVOID Context,
|
|
IN PIO_STATUS_BLOCK IoStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the power completion routine for all mf power operations. It copies
|
|
the status of the power operation into the context and then sets the completed
|
|
event.
|
|
|
|
Arguments:
|
|
|
|
As documented for power completion routines.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PMF_POWER_COMPLETION_CONTEXT completion = Context;
|
|
|
|
completion->Status = IoStatus->Status;
|
|
|
|
KeSetEvent(&completion->Event, 0, FALSE);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
MfSendSetPowerIrp(
|
|
IN PDEVICE_OBJECT Target,
|
|
IN POWER_STATE State
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This builds and send a IRP_MN_SET_POWER_IRP to a device.
|
|
|
|
Arguments:
|
|
|
|
Target - The a device in the device stack the irp is to be sent to -
|
|
the top of the device stack is always found and the irp sent there first.
|
|
|
|
State - The device power state that should be requested.
|
|
|
|
Return Value:
|
|
|
|
The final status of the completed irp or an error if the irp couldn't be sent
|
|
|
|
--*/
|
|
|
|
|
|
{
|
|
NTSTATUS status;
|
|
MF_POWER_COMPLETION_CONTEXT completion;
|
|
|
|
KeInitializeEvent(&completion.Event, SynchronizationEvent, FALSE);
|
|
|
|
DEBUG_MSG(1,
|
|
("Sending SET_POWER to 0x%08x for %s\n",
|
|
Target,
|
|
DEVICE_POWER_STRING(State.DeviceState)
|
|
));
|
|
|
|
status = PoRequestPowerIrp(Target,
|
|
IRP_MN_SET_POWER,
|
|
State,
|
|
MfPowerRequestCompletion,
|
|
&completion,
|
|
NULL
|
|
);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
|
|
ASSERT(status == STATUS_PENDING);
|
|
|
|
KeWaitForSingleObject( &completion.Event, Executive, KernelMode, FALSE, NULL );
|
|
|
|
status = completion.Status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
MfUpdateParentPowerState(
|
|
IN PMF_PARENT_EXTENSION Parent,
|
|
IN DEVICE_POWER_STATE TargetPowerState
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Request Po to send the mf parent device a power irp if we need to
|
|
change its power state because of changes to its children power
|
|
states.
|
|
|
|
Arguments:
|
|
|
|
Parent - The MF parent device
|
|
|
|
TargetPowerState - The device power state that the parent should
|
|
be updated to.
|
|
|
|
Return Value:
|
|
|
|
The status of this operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
POWER_STATE newState;
|
|
|
|
//
|
|
// If the parent's power state need changing because of a power down or
|
|
// power up request that it do so
|
|
//
|
|
|
|
if (Parent->Common.PowerState != TargetPowerState) {
|
|
|
|
//
|
|
// Create and send the power irp and wait for its completion
|
|
//
|
|
|
|
DEBUG_MSG(1,
|
|
("Updating parent power state from %s to %s\n",
|
|
DEVICE_POWER_STRING(Parent->Common.PowerState),
|
|
DEVICE_POWER_STRING(TargetPowerState)
|
|
));
|
|
|
|
newState.DeviceState = TargetPowerState;
|
|
|
|
status = MfSendSetPowerIrp(Parent->Self,
|
|
newState);
|
|
|
|
DEBUG_MSG(1,
|
|
("Power update completed with %s (0x%08x)\n",
|
|
STATUS_STRING(status), status
|
|
));
|
|
|
|
} else {
|
|
DEBUG_MSG(1,
|
|
("Parent power already in %s\n",
|
|
DEVICE_POWER_STRING(TargetPowerState)
|
|
));
|
|
}
|
|
|
|
return status;
|
|
|
|
}
|
|
|