windows-nt/Source/XPSP1/NT/base/busdrv/mf/utils.c
2020-09-26 16:20:57 +08:00

804 lines
17 KiB
C

/*++
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
//
string.Length = (USHORT) info->NameLength;
string.MaximumLength = (USHORT) info->NameLength;
string.Buffer = ExAllocatePoolWithTag(PagedPool,
info->NameLength,
MF_POOL_TAG
);
if (!string.Buffer) {
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;
//
// Check parameters
//
if (*Data && !(*DataLength)) {
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);
if (convertSzToMultiSz) {
length = info->DataLength + sizeof(UNICODE_NULL);
} else {
length = info->DataLength;
}
//
// 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.
//
if (Type == REG_DWORD) {
//
// If the data is a REG_DWORD then Data is a pointer to a ULONG to store
// the data
//
*Data = *((PULONG *)info->Data);
} else 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;
}
}
RtlCopyMemory(*Data, info->Data, info->DataLength);
if (convertSzToMultiSz) {
PWSTR term = (PWSTR)*Data + (info->DataLength / sizeof(WCHAR));
//
// Add the final null termination
//
*term = UNICODE_NULL;
}
*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;
}