reactos/ntoskrnl/io/pnpmgr/devaction.c
Victor Perevertkin 21e9e2baa5
[NTOS:IO] Move device manipulation functions from pnpmgr/pnpmgr.c to pnpmgr/devaction.c
And rearrange them in more logical order.

This effectively splits the file, leaving public "Io" functions in
pnpmgr.c along with some things not related do device object management.
Functions which manipulate the device tree are left in devaction.c.
In future all these functions will only be accessed from
DeviceActionWorker.
While being public API, IoRequestDeviceEject and IoInvalidateDeviceState
have been moved to devaction.c as well. In next commits they will be
converted to DeviceActionWorker routines and their callers will be put
in pnpmgr.c
2020-06-24 04:03:35 +03:00

2376 lines
71 KiB
C

/*
* PROJECT: ReactOS Kernel
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
* PURPOSE: PnP manager device manipulation functions
* COPYRIGHT: Casper S. Hornstrup (chorns@users.sourceforge.net)
* 2007 Hervé Poussineau (hpoussin@reactos.org)
* 2014-2017 Thomas Faber (thomas.faber@reactos.org)
*/
/* INCLUDES ******************************************************************/
#include <ntoskrnl.h>
#define NDEBUG
#include <debug.h>
/* GLOBALS *******************************************************************/
extern ERESOURCE IopDriverLoadResource;
extern BOOLEAN PnpSystemInit;
extern PDEVICE_NODE IopRootDeviceNode;
#define MAX_DEVICE_ID_LEN 200
#define MAX_SEPARATORS_INSTANCEID 0
#define MAX_SEPARATORS_DEVICEID 1
/* DATA **********************************************************************/
LIST_ENTRY IopDeviceActionRequestList;
WORK_QUEUE_ITEM IopDeviceActionWorkItem;
BOOLEAN IopDeviceActionInProgress;
KSPIN_LOCK IopDeviceActionLock;
/* FUNCTIONS *****************************************************************/
PDEVICE_OBJECT
IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance);
NTSTATUS
IopGetParentIdPrefix(PDEVICE_NODE DeviceNode, PUNICODE_STRING ParentIdPrefix);
USHORT
NTAPI
IopGetBusTypeGuidIndex(LPGUID BusTypeGuid);
NTSTATUS
IopSetDeviceInstanceData(HANDLE InstanceKey, PDEVICE_NODE DeviceNode);
VOID
NTAPI
IopInstallCriticalDevice(PDEVICE_NODE DeviceNode);
static
VOID
IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject);
static
NTSTATUS
IopPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject, BOOLEAN Force);
static
BOOLEAN
IopValidateID(
_In_ PWCHAR Id,
_In_ BUS_QUERY_ID_TYPE QueryType)
{
PWCHAR PtrChar;
PWCHAR StringEnd;
WCHAR Char;
ULONG SeparatorsCount = 0;
PWCHAR PtrPrevChar = NULL;
ULONG MaxSeparators;
BOOLEAN IsMultiSz;
PAGED_CODE();
switch (QueryType)
{
case BusQueryDeviceID:
MaxSeparators = MAX_SEPARATORS_DEVICEID;
IsMultiSz = FALSE;
break;
case BusQueryInstanceID:
MaxSeparators = MAX_SEPARATORS_INSTANCEID;
IsMultiSz = FALSE;
break;
case BusQueryHardwareIDs:
case BusQueryCompatibleIDs:
MaxSeparators = MAX_SEPARATORS_DEVICEID;
IsMultiSz = TRUE;
break;
default:
DPRINT1("IopValidateID: Not handled QueryType - %x\n", QueryType);
return FALSE;
}
StringEnd = Id + MAX_DEVICE_ID_LEN;
for (PtrChar = Id; PtrChar < StringEnd; PtrChar++)
{
Char = *PtrChar;
if (Char == UNICODE_NULL)
{
if (!IsMultiSz || (PtrPrevChar && PtrChar == PtrPrevChar + 1))
{
if (MaxSeparators == SeparatorsCount || IsMultiSz)
{
return TRUE;
}
DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
SeparatorsCount, MaxSeparators);
goto ErrorExit;
}
StringEnd = PtrChar + MAX_DEVICE_ID_LEN + 1;
PtrPrevChar = PtrChar;
SeparatorsCount = 0;
}
else if (Char < ' ' || Char > 0x7F || Char == ',')
{
DPRINT1("IopValidateID: Invalid character - %04X\n", Char);
goto ErrorExit;
}
else if (Char == ' ')
{
*PtrChar = '_';
}
else if (Char == '\\')
{
SeparatorsCount++;
if (SeparatorsCount > MaxSeparators)
{
DPRINT1("IopValidateID: SeparatorsCount - %lu, MaxSeparators - %lu\n",
SeparatorsCount, MaxSeparators);
goto ErrorExit;
}
}
}
DPRINT1("IopValidateID: Not terminated ID\n");
ErrorExit:
// FIXME logging
return FALSE;
}
static
NTSTATUS
IopCreateDeviceInstancePath(
_In_ PDEVICE_NODE DeviceNode,
_Out_ PUNICODE_STRING InstancePath)
{
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING DeviceId;
UNICODE_STRING InstanceId;
IO_STACK_LOCATION Stack;
NTSTATUS Status;
UNICODE_STRING ParentIdPrefix = { 0, 0, NULL };
DEVICE_CAPABILITIES DeviceCapabilities;
BOOLEAN IsValidID;
DPRINT("Sending IRP_MN_QUERY_ID.BusQueryDeviceID to device stack\n");
Stack.Parameters.QueryId.IdType = BusQueryDeviceID;
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_ID,
&Stack);
if (!NT_SUCCESS(Status))
{
DPRINT1("IopInitiatePnpIrp(BusQueryDeviceID) failed (Status %x)\n", Status);
return Status;
}
IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryDeviceID);
if (!IsValidID)
{
DPRINT1("Invalid DeviceID. DeviceNode - %p\n", DeviceNode);
}
/* Save the device id string */
RtlInitUnicodeString(&DeviceId, (PWSTR)IoStatusBlock.Information);
DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after enumeration)\n");
Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
if (!NT_SUCCESS(Status))
{
DPRINT1("IopQueryDeviceCapabilities() failed (Status 0x%08lx)\n", Status);
RtlFreeUnicodeString(&DeviceId);
return Status;
}
/* This bit is only check after enumeration */
if (DeviceCapabilities.HardwareDisabled)
{
/* FIXME: Cleanup device */
DeviceNode->Flags |= DNF_DISABLED;
RtlFreeUnicodeString(&DeviceId);
return STATUS_PLUGPLAY_NO_DEVICE;
}
else
{
DeviceNode->Flags &= ~DNF_DISABLED;
}
if (!DeviceCapabilities.UniqueID)
{
/* Device has not a unique ID. We need to prepend parent bus unique identifier */
DPRINT("Instance ID is not unique\n");
Status = IopGetParentIdPrefix(DeviceNode, &ParentIdPrefix);
if (!NT_SUCCESS(Status))
{
DPRINT1("IopGetParentIdPrefix() failed (Status 0x%08lx)\n", Status);
RtlFreeUnicodeString(&DeviceId);
return Status;
}
}
DPRINT("Sending IRP_MN_QUERY_ID.BusQueryInstanceID to device stack\n");
Stack.Parameters.QueryId.IdType = BusQueryInstanceID;
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_ID,
&Stack);
if (!NT_SUCCESS(Status))
{
DPRINT("IopInitiatePnpIrp(BusQueryInstanceID) failed (Status %lx)\n", Status);
ASSERT(IoStatusBlock.Information == 0);
}
if (IoStatusBlock.Information)
{
IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryInstanceID);
if (!IsValidID)
{
DPRINT1("Invalid InstanceID. DeviceNode - %p\n", DeviceNode);
}
}
RtlInitUnicodeString(&InstanceId,
(PWSTR)IoStatusBlock.Information);
InstancePath->Length = 0;
InstancePath->MaximumLength = DeviceId.Length + sizeof(WCHAR) +
ParentIdPrefix.Length +
InstanceId.Length +
sizeof(UNICODE_NULL);
if (ParentIdPrefix.Length && InstanceId.Length)
{
InstancePath->MaximumLength += sizeof(WCHAR);
}
InstancePath->Buffer = ExAllocatePoolWithTag(PagedPool,
InstancePath->MaximumLength,
TAG_IO);
if (!InstancePath->Buffer)
{
RtlFreeUnicodeString(&InstanceId);
RtlFreeUnicodeString(&ParentIdPrefix);
RtlFreeUnicodeString(&DeviceId);
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Start with the device id */
RtlCopyUnicodeString(InstancePath, &DeviceId);
RtlAppendUnicodeToString(InstancePath, L"\\");
/* Add information from parent bus device to InstancePath */
RtlAppendUnicodeStringToString(InstancePath, &ParentIdPrefix);
if (ParentIdPrefix.Length && InstanceId.Length)
{
RtlAppendUnicodeToString(InstancePath, L"&");
}
/* Finally, add the id returned by the driver stack */
RtlAppendUnicodeStringToString(InstancePath, &InstanceId);
/*
* FIXME: Check for valid characters, if there is invalid characters
* then bugcheck
*/
RtlFreeUnicodeString(&InstanceId);
RtlFreeUnicodeString(&DeviceId);
RtlFreeUnicodeString(&ParentIdPrefix);
return STATUS_SUCCESS;
}
NTSTATUS
NTAPI
IopQueryDeviceCapabilities(PDEVICE_NODE DeviceNode,
PDEVICE_CAPABILITIES DeviceCaps)
{
IO_STATUS_BLOCK StatusBlock;
IO_STACK_LOCATION Stack;
NTSTATUS Status;
HANDLE InstanceKey;
UNICODE_STRING ValueName;
/* Set up the Header */
RtlZeroMemory(DeviceCaps, sizeof(DEVICE_CAPABILITIES));
DeviceCaps->Size = sizeof(DEVICE_CAPABILITIES);
DeviceCaps->Version = 1;
DeviceCaps->Address = -1;
DeviceCaps->UINumber = -1;
/* Set up the Stack */
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.Parameters.DeviceCapabilities.Capabilities = DeviceCaps;
/* Send the IRP */
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&StatusBlock,
IRP_MN_QUERY_CAPABILITIES,
&Stack);
if (!NT_SUCCESS(Status))
{
if (Status != STATUS_NOT_SUPPORTED)
{
DPRINT1("IRP_MN_QUERY_CAPABILITIES failed with status 0x%lx\n", Status);
}
return Status;
}
/* Map device capabilities to capability flags */
DeviceNode->CapabilityFlags = 0;
if (DeviceCaps->LockSupported)
DeviceNode->CapabilityFlags |= 0x00000001; // CM_DEVCAP_LOCKSUPPORTED
if (DeviceCaps->EjectSupported)
DeviceNode->CapabilityFlags |= 0x00000002; // CM_DEVCAP_EJECTSUPPORTED
if (DeviceCaps->Removable)
DeviceNode->CapabilityFlags |= 0x00000004; // CM_DEVCAP_REMOVABLE
if (DeviceCaps->DockDevice)
DeviceNode->CapabilityFlags |= 0x00000008; // CM_DEVCAP_DOCKDEVICE
if (DeviceCaps->UniqueID)
DeviceNode->CapabilityFlags |= 0x00000010; // CM_DEVCAP_UNIQUEID
if (DeviceCaps->SilentInstall)
DeviceNode->CapabilityFlags |= 0x00000020; // CM_DEVCAP_SILENTINSTALL
if (DeviceCaps->RawDeviceOK)
DeviceNode->CapabilityFlags |= 0x00000040; // CM_DEVCAP_RAWDEVICEOK
if (DeviceCaps->SurpriseRemovalOK)
DeviceNode->CapabilityFlags |= 0x00000080; // CM_DEVCAP_SURPRISEREMOVALOK
if (DeviceCaps->HardwareDisabled)
DeviceNode->CapabilityFlags |= 0x00000100; // CM_DEVCAP_HARDWAREDISABLED
if (DeviceCaps->NonDynamic)
DeviceNode->CapabilityFlags |= 0x00000200; // CM_DEVCAP_NONDYNAMIC
if (DeviceCaps->NoDisplayInUI)
DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
else
DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
if (NT_SUCCESS(Status))
{
/* Set 'Capabilities' value */
RtlInitUnicodeString(&ValueName, L"Capabilities");
Status = ZwSetValueKey(InstanceKey,
&ValueName,
0,
REG_DWORD,
&DeviceNode->CapabilityFlags,
sizeof(ULONG));
/* Set 'UINumber' value */
if (DeviceCaps->UINumber != MAXULONG)
{
RtlInitUnicodeString(&ValueName, L"UINumber");
Status = ZwSetValueKey(InstanceKey,
&ValueName,
0,
REG_DWORD,
&DeviceCaps->UINumber,
sizeof(ULONG));
}
ZwClose(InstanceKey);
}
return Status;
}
static
NTSTATUS
IopQueryHardwareIds(PDEVICE_NODE DeviceNode,
HANDLE InstanceKey)
{
IO_STACK_LOCATION Stack;
IO_STATUS_BLOCK IoStatusBlock;
PWSTR Ptr;
UNICODE_STRING ValueName;
NTSTATUS Status;
ULONG Length, TotalLength;
BOOLEAN IsValidID;
DPRINT("Sending IRP_MN_QUERY_ID.BusQueryHardwareIDs to device stack\n");
RtlZeroMemory(&Stack, sizeof(Stack));
Stack.Parameters.QueryId.IdType = BusQueryHardwareIDs;
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_ID,
&Stack);
if (NT_SUCCESS(Status))
{
IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryHardwareIDs);
if (!IsValidID)
{
DPRINT1("Invalid HardwareIDs. DeviceNode - %p\n", DeviceNode);
}
TotalLength = 0;
Ptr = (PWSTR)IoStatusBlock.Information;
DPRINT("Hardware IDs:\n");
while (*Ptr)
{
DPRINT(" %S\n", Ptr);
Length = (ULONG)wcslen(Ptr) + 1;
Ptr += Length;
TotalLength += Length;
}
DPRINT("TotalLength: %hu\n", TotalLength);
DPRINT("\n");
RtlInitUnicodeString(&ValueName, L"HardwareID");
Status = ZwSetValueKey(InstanceKey,
&ValueName,
0,
REG_MULTI_SZ,
(PVOID)IoStatusBlock.Information,
(TotalLength + 1) * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
}
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
}
return Status;
}
static
NTSTATUS
IopQueryCompatibleIds(PDEVICE_NODE DeviceNode,
HANDLE InstanceKey)
{
IO_STACK_LOCATION Stack;
IO_STATUS_BLOCK IoStatusBlock;
PWSTR Ptr;
UNICODE_STRING ValueName;
NTSTATUS Status;
ULONG Length, TotalLength;
BOOLEAN IsValidID;
DPRINT("Sending IRP_MN_QUERY_ID.BusQueryCompatibleIDs to device stack\n");
RtlZeroMemory(&Stack, sizeof(Stack));
Stack.Parameters.QueryId.IdType = BusQueryCompatibleIDs;
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_ID,
&Stack);
if (NT_SUCCESS(Status) && IoStatusBlock.Information)
{
IsValidID = IopValidateID((PWCHAR)IoStatusBlock.Information, BusQueryCompatibleIDs);
if (!IsValidID)
{
DPRINT1("Invalid CompatibleIDs. DeviceNode - %p\n", DeviceNode);
}
TotalLength = 0;
Ptr = (PWSTR)IoStatusBlock.Information;
DPRINT("Compatible IDs:\n");
while (*Ptr)
{
DPRINT(" %S\n", Ptr);
Length = (ULONG)wcslen(Ptr) + 1;
Ptr += Length;
TotalLength += Length;
}
DPRINT("TotalLength: %hu\n", TotalLength);
DPRINT("\n");
RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
Status = ZwSetValueKey(InstanceKey,
&ValueName,
0,
REG_MULTI_SZ,
(PVOID)IoStatusBlock.Information,
(TotalLength + 1) * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwSetValueKey() failed (Status %lx) or no Compatible ID returned\n", Status);
}
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %x)\n", Status);
}
return Status;
}
/*
* IopActionInterrogateDeviceStack
*
* Retrieve information for all (direct) child nodes of a parent node.
*
* Parameters
* DeviceNode
* Pointer to device node.
* Context
* Pointer to parent node to retrieve child node information for.
*
* Remarks
* Any errors that occur are logged instead so that all child services have a chance
* of being interrogated.
*/
NTSTATUS
IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
PVOID Context)
{
IO_STATUS_BLOCK IoStatusBlock;
PWSTR DeviceDescription;
PWSTR LocationInformation;
PDEVICE_NODE ParentDeviceNode;
IO_STACK_LOCATION Stack;
NTSTATUS Status;
ULONG RequiredLength;
LCID LocaleId;
HANDLE InstanceKey = NULL;
UNICODE_STRING ValueName;
UNICODE_STRING InstancePathU;
PDEVICE_OBJECT OldDeviceObject;
DPRINT("IopActionInterrogateDeviceStack(%p, %p)\n", DeviceNode, Context);
DPRINT("PDO 0x%p\n", DeviceNode->PhysicalDeviceObject);
ParentDeviceNode = (PDEVICE_NODE)Context;
/*
* We are called for the parent too, but we don't need to do special
* handling for this node
*/
if (DeviceNode == ParentDeviceNode)
{
DPRINT("Success\n");
return STATUS_SUCCESS;
}
/*
* Make sure this device node is a direct child of the parent device node
* that is given as an argument
*/
if (DeviceNode->Parent != ParentDeviceNode)
{
DPRINT("Skipping 2+ level child\n");
return STATUS_SUCCESS;
}
/* Skip processing if it was already completed before */
if (DeviceNode->Flags & DNF_PROCESSED)
{
/* Nothing to do */
return STATUS_SUCCESS;
}
/* Get Locale ID */
Status = ZwQueryDefaultLocale(FALSE, &LocaleId);
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwQueryDefaultLocale() failed with status 0x%lx\n", Status);
return Status;
}
/*
* FIXME: For critical errors, cleanup and disable device, but always
* return STATUS_SUCCESS.
*/
Status = IopCreateDeviceInstancePath(DeviceNode, &InstancePathU);
if (!NT_SUCCESS(Status))
{
if (Status != STATUS_PLUGPLAY_NO_DEVICE)
{
DPRINT1("IopCreateDeviceInstancePath() failed with status 0x%lx\n", Status);
}
/* We have to return success otherwise we abort the traverse operation */
return STATUS_SUCCESS;
}
/* Verify that this is not a duplicate */
OldDeviceObject = IopGetDeviceObjectFromDeviceInstance(&InstancePathU);
if (OldDeviceObject != NULL)
{
PDEVICE_NODE OldDeviceNode = IopGetDeviceNode(OldDeviceObject);
DPRINT1("Duplicate device instance '%wZ'\n", &InstancePathU);
DPRINT1("Current instance parent: '%wZ'\n", &DeviceNode->Parent->InstancePath);
DPRINT1("Old instance parent: '%wZ'\n", &OldDeviceNode->Parent->InstancePath);
KeBugCheckEx(PNP_DETECTED_FATAL_ERROR,
0x01,
(ULONG_PTR)DeviceNode->PhysicalDeviceObject,
(ULONG_PTR)OldDeviceObject,
0);
}
DeviceNode->InstancePath = InstancePathU;
DPRINT("InstancePath is %S\n", DeviceNode->InstancePath.Buffer);
/*
* Create registry key for the instance id, if it doesn't exist yet
*/
Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceKey);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to create the instance key! (Status %lx)\n", Status);
/* We have to return success otherwise we abort the traverse operation */
return STATUS_SUCCESS;
}
IopQueryHardwareIds(DeviceNode, InstanceKey);
IopQueryCompatibleIds(DeviceNode, InstanceKey);
DPRINT("Sending IRP_MN_QUERY_DEVICE_TEXT.DeviceTextDescription to device stack\n");
Stack.Parameters.QueryDeviceText.DeviceTextType = DeviceTextDescription;
Stack.Parameters.QueryDeviceText.LocaleId = LocaleId;
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_DEVICE_TEXT,
&Stack);
DeviceDescription = NT_SUCCESS(Status) ? (PWSTR)IoStatusBlock.Information
: NULL;
/* This key is mandatory, so even if the Irp fails, we still write it */
RtlInitUnicodeString(&ValueName, L"DeviceDesc");
if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
{
if (DeviceDescription &&
*DeviceDescription != UNICODE_NULL)
{
/* This key is overriden when a driver is installed. Don't write the
* new description if another one already exists */
Status = ZwSetValueKey(InstanceKey,
&ValueName,
0,
REG_SZ,
DeviceDescription,
((ULONG)wcslen(DeviceDescription) + 1) * sizeof(WCHAR));
}
else
{
UNICODE_STRING DeviceDesc = RTL_CONSTANT_STRING(L"Unknown device");
DPRINT("Driver didn't return DeviceDesc (Status 0x%08lx), so place unknown device there\n", Status);
Status = ZwSetValueKey(InstanceKey,
&ValueName,
0,
REG_SZ,
DeviceDesc.Buffer,
DeviceDesc.MaximumLength);
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwSetValueKey() failed (Status 0x%lx)\n", Status);
}
}
}
if (DeviceDescription)
{
ExFreePoolWithTag(DeviceDescription, 0);
}
DPRINT("Sending IRP_MN_QUERY_DEVICE_TEXT.DeviceTextLocation to device stack\n");
Stack.Parameters.QueryDeviceText.DeviceTextType = DeviceTextLocationInformation;
Stack.Parameters.QueryDeviceText.LocaleId = LocaleId;
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_DEVICE_TEXT,
&Stack);
if (NT_SUCCESS(Status) && IoStatusBlock.Information)
{
LocationInformation = (PWSTR)IoStatusBlock.Information;
DPRINT("LocationInformation: %S\n", LocationInformation);
RtlInitUnicodeString(&ValueName, L"LocationInformation");
Status = ZwSetValueKey(InstanceKey,
&ValueName,
0,
REG_SZ,
LocationInformation,
((ULONG)wcslen(LocationInformation) + 1) * sizeof(WCHAR));
if (!NT_SUCCESS(Status))
{
DPRINT1("ZwSetValueKey() failed (Status %lx)\n", Status);
}
ExFreePoolWithTag(LocationInformation, 0);
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
}
DPRINT("Sending IRP_MN_QUERY_BUS_INFORMATION to device stack\n");
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_BUS_INFORMATION,
NULL);
if (NT_SUCCESS(Status) && IoStatusBlock.Information)
{
PPNP_BUS_INFORMATION BusInformation = (PPNP_BUS_INFORMATION)IoStatusBlock.Information;
DeviceNode->ChildBusNumber = BusInformation->BusNumber;
DeviceNode->ChildInterfaceType = BusInformation->LegacyBusType;
DeviceNode->ChildBusTypeIndex = IopGetBusTypeGuidIndex(&BusInformation->BusTypeGuid);
ExFreePoolWithTag(BusInformation, 0);
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
DeviceNode->ChildBusNumber = 0xFFFFFFF0;
DeviceNode->ChildInterfaceType = InterfaceTypeUndefined;
DeviceNode->ChildBusTypeIndex = -1;
}
DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_RESOURCES,
NULL);
if (NT_SUCCESS(Status) && IoStatusBlock.Information)
{
DeviceNode->BootResources = (PCM_RESOURCE_LIST)IoStatusBlock.Information;
IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
DeviceNode->BootResources = NULL;
}
DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
Status = IopInitiatePnpIrp(DeviceNode->PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
NULL);
if (NT_SUCCESS(Status))
{
DeviceNode->ResourceRequirements = (PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
DeviceNode->ResourceRequirements = NULL;
}
if (InstanceKey != NULL)
{
IopSetDeviceInstanceData(InstanceKey, DeviceNode);
}
ZwClose(InstanceKey);
IopDeviceNodeSetFlag(DeviceNode, DNF_PROCESSED);
if (!IopDeviceNodeHasFlag(DeviceNode, DNF_LEGACY_DRIVER))
{
/* Report the device to the user-mode pnp manager */
IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
&DeviceNode->InstancePath);
}
return STATUS_SUCCESS;
}
/*
* IopActionConfigureChildServices
*
* Retrieve configuration for all (direct) child nodes of a parent node.
*
* Parameters
* DeviceNode
* Pointer to device node.
* Context
* Pointer to parent node to retrieve child node configuration for.
*
* Remarks
* Any errors that occur are logged instead so that all child services have a chance of beeing
* configured.
*/
NTSTATUS
IopActionConfigureChildServices(PDEVICE_NODE DeviceNode,
PVOID Context)
{
RTL_QUERY_REGISTRY_TABLE QueryTable[3];
PDEVICE_NODE ParentDeviceNode;
PUNICODE_STRING Service;
UNICODE_STRING ClassGUID;
NTSTATUS Status;
DEVICE_CAPABILITIES DeviceCaps;
DPRINT("IopActionConfigureChildServices(%p, %p)\n", DeviceNode, Context);
ParentDeviceNode = (PDEVICE_NODE)Context;
/*
* We are called for the parent too, but we don't need to do special
* handling for this node
*/
if (DeviceNode == ParentDeviceNode)
{
DPRINT("Success\n");
return STATUS_SUCCESS;
}
/*
* Make sure this device node is a direct child of the parent device node
* that is given as an argument
*/
if (DeviceNode->Parent != ParentDeviceNode)
{
DPRINT("Skipping 2+ level child\n");
return STATUS_SUCCESS;
}
if (!(DeviceNode->Flags & DNF_PROCESSED))
{
DPRINT1("Child not ready to be configured\n");
return STATUS_SUCCESS;
}
if (!(DeviceNode->Flags & (DNF_DISABLED | DNF_STARTED | DNF_ADDED)))
{
UNICODE_STRING RegKey;
/* Install the service for this if it's in the CDDB */
IopInstallCriticalDevice(DeviceNode);
/*
* Retrieve configuration from Enum key
*/
Service = &DeviceNode->ServiceName;
RtlZeroMemory(QueryTable, sizeof(QueryTable));
RtlInitUnicodeString(Service, NULL);
RtlInitUnicodeString(&ClassGUID, NULL);
QueryTable[0].Name = L"Service";
QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
QueryTable[0].EntryContext = Service;
QueryTable[1].Name = L"ClassGUID";
QueryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
QueryTable[1].EntryContext = &ClassGUID;
QueryTable[1].DefaultType = REG_SZ;
QueryTable[1].DefaultData = L"";
QueryTable[1].DefaultLength = 0;
RegKey.Length = 0;
RegKey.MaximumLength = sizeof(ENUM_ROOT) + sizeof(WCHAR) + DeviceNode->InstancePath.Length;
RegKey.Buffer = ExAllocatePoolWithTag(PagedPool,
RegKey.MaximumLength,
TAG_IO);
if (RegKey.Buffer == NULL)
{
IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlAppendUnicodeToString(&RegKey, ENUM_ROOT);
RtlAppendUnicodeToString(&RegKey, L"\\");
RtlAppendUnicodeStringToString(&RegKey, &DeviceNode->InstancePath);
Status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
RegKey.Buffer, QueryTable, NULL, NULL);
ExFreePoolWithTag(RegKey.Buffer, TAG_IO);
if (!NT_SUCCESS(Status))
{
/* FIXME: Log the error */
DPRINT("Could not retrieve configuration for device %wZ (Status 0x%08x)\n",
&DeviceNode->InstancePath, Status);
IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
return STATUS_SUCCESS;
}
if (Service->Buffer == NULL)
{
if (NT_SUCCESS(IopQueryDeviceCapabilities(DeviceNode, &DeviceCaps)) &&
DeviceCaps.RawDeviceOK)
{
DPRINT("%wZ is using parent bus driver (%wZ)\n", &DeviceNode->InstancePath, &ParentDeviceNode->ServiceName);
RtlInitEmptyUnicodeString(&DeviceNode->ServiceName, NULL, 0);
}
else if (ClassGUID.Length != 0)
{
/* Device has a ClassGUID value, but no Service value.
* Suppose it is using the NULL driver, so state the
* device is started */
DPRINT("%wZ is using NULL driver\n", &DeviceNode->InstancePath);
IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
}
else
{
DeviceNode->Problem = CM_PROB_FAILED_INSTALL;
IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
}
return STATUS_SUCCESS;
}
DPRINT("Got Service %S\n", Service->Buffer);
}
return STATUS_SUCCESS;
}
/*
* IopActionInitChildServices
*
* Initialize the service for all (direct) child nodes of a parent node
*
* Parameters
* DeviceNode
* Pointer to device node.
* Context
* Pointer to parent node to initialize child node services for.
*
* Remarks
* If the driver image for a service is not loaded and initialized
* it is done here too. Any errors that occur are logged instead so
* that all child services have a chance of being initialized.
*/
NTSTATUS
IopActionInitChildServices(PDEVICE_NODE DeviceNode,
PVOID Context)
{
PDEVICE_NODE ParentDeviceNode;
NTSTATUS Status;
BOOLEAN BootDrivers = !PnpSystemInit;
DPRINT("IopActionInitChildServices(%p, %p)\n", DeviceNode, Context);
ParentDeviceNode = Context;
/*
* We are called for the parent too, but we don't need to do special
* handling for this node
*/
if (DeviceNode == ParentDeviceNode)
{
DPRINT("Success\n");
return STATUS_SUCCESS;
}
/*
* We don't want to check for a direct child because
* this function is called during boot to reinitialize
* devices with drivers that couldn't load yet due to
* stage 0 limitations (ie can't load from disk yet).
*/
if (!(DeviceNode->Flags & DNF_PROCESSED))
{
DPRINT1("Child not ready to be added\n");
return STATUS_SUCCESS;
}
if (IopDeviceNodeHasFlag(DeviceNode, DNF_STARTED) ||
IopDeviceNodeHasFlag(DeviceNode, DNF_ADDED) ||
IopDeviceNodeHasFlag(DeviceNode, DNF_DISABLED))
return STATUS_SUCCESS;
if (DeviceNode->ServiceName.Buffer == NULL)
{
/* We don't need to worry about loading the driver because we're
* being driven in raw mode so our parent must be loaded to get here */
Status = IopInitializeDevice(DeviceNode, NULL);
if (NT_SUCCESS(Status))
{
Status = IopStartDevice(DeviceNode);
if (!NT_SUCCESS(Status))
{
DPRINT1("IopStartDevice(%wZ) failed with status 0x%08x\n",
&DeviceNode->InstancePath, Status);
}
}
}
else
{
PLDR_DATA_TABLE_ENTRY ModuleObject;
PDRIVER_OBJECT DriverObject;
KeEnterCriticalRegion();
ExAcquireResourceExclusiveLite(&IopDriverLoadResource, TRUE);
/* Get existing DriverObject pointer (in case the driver has
already been loaded and initialized) */
Status = IopGetDriverObject(
&DriverObject,
&DeviceNode->ServiceName,
FALSE);
if (!NT_SUCCESS(Status))
{
/* Driver is not initialized, try to load it */
Status = IopLoadServiceModule(&DeviceNode->ServiceName, &ModuleObject);
if (NT_SUCCESS(Status) || Status == STATUS_IMAGE_ALREADY_LOADED)
{
/* Initialize the driver */
Status = IopInitializeDriverModule(DeviceNode, ModuleObject,
&DeviceNode->ServiceName, FALSE, &DriverObject);
if (!NT_SUCCESS(Status))
DeviceNode->Problem = CM_PROB_FAILED_DRIVER_ENTRY;
}
else if (Status == STATUS_DRIVER_UNABLE_TO_LOAD)
{
DPRINT1("Service '%wZ' is disabled\n", &DeviceNode->ServiceName);
DeviceNode->Problem = CM_PROB_DISABLED_SERVICE;
}
else
{
DPRINT("IopLoadServiceModule(%wZ) failed with status 0x%08x\n",
&DeviceNode->ServiceName, Status);
if (!BootDrivers)
DeviceNode->Problem = CM_PROB_DRIVER_FAILED_LOAD;
}
}
ExReleaseResourceLite(&IopDriverLoadResource);
KeLeaveCriticalRegion();
/* Driver is loaded and initialized at this point */
if (NT_SUCCESS(Status))
{
/* Initialize the device, including all filters */
Status = PipCallDriverAddDevice(DeviceNode, FALSE, DriverObject);
/* Remove the extra reference */
ObDereferenceObject(DriverObject);
}
else
{
/*
* Don't disable when trying to load only boot drivers
*/
if (!BootDrivers)
{
IopDeviceNodeSetFlag(DeviceNode, DNF_DISABLED);
}
}
}
return STATUS_SUCCESS;
}
static
NTSTATUS
IopSetServiceEnumData(PDEVICE_NODE DeviceNode)
{
UNICODE_STRING ServicesKeyPath = RTL_CONSTANT_STRING(L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\");
UNICODE_STRING ServiceKeyName;
UNICODE_STRING EnumKeyName;
UNICODE_STRING ValueName;
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
HANDLE ServiceKey = NULL, ServiceEnumKey = NULL;
ULONG Disposition;
ULONG Count = 0, NextInstance = 0;
WCHAR ValueBuffer[6];
NTSTATUS Status = STATUS_SUCCESS;
DPRINT("IopSetServiceEnumData(%p)\n", DeviceNode);
DPRINT("Instance: %wZ\n", &DeviceNode->InstancePath);
DPRINT("Service: %wZ\n", &DeviceNode->ServiceName);
if (DeviceNode->ServiceName.Buffer == NULL)
{
DPRINT1("No service!\n");
return STATUS_SUCCESS;
}
ServiceKeyName.MaximumLength = ServicesKeyPath.Length + DeviceNode->ServiceName.Length + sizeof(UNICODE_NULL);
ServiceKeyName.Length = 0;
ServiceKeyName.Buffer = ExAllocatePool(PagedPool, ServiceKeyName.MaximumLength);
if (ServiceKeyName.Buffer == NULL)
{
DPRINT1("No ServiceKeyName.Buffer!\n");
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlAppendUnicodeStringToString(&ServiceKeyName, &ServicesKeyPath);
RtlAppendUnicodeStringToString(&ServiceKeyName, &DeviceNode->ServiceName);
DPRINT("ServiceKeyName: %wZ\n", &ServiceKeyName);
Status = IopOpenRegistryKeyEx(&ServiceKey, NULL, &ServiceKeyName, KEY_CREATE_SUB_KEY);
if (!NT_SUCCESS(Status))
{
goto done;
}
RtlInitUnicodeString(&EnumKeyName, L"Enum");
Status = IopCreateRegistryKeyEx(&ServiceEnumKey,
ServiceKey,
&EnumKeyName,
KEY_SET_VALUE,
REG_OPTION_VOLATILE,
&Disposition);
if (NT_SUCCESS(Status))
{
if (Disposition == REG_OPENED_EXISTING_KEY)
{
/* Read the NextInstance value */
Status = IopGetRegistryValue(ServiceEnumKey,
L"Count",
&KeyValueInformation);
if (!NT_SUCCESS(Status))
goto done;
if ((KeyValueInformation->Type == REG_DWORD) &&
(KeyValueInformation->DataLength))
{
/* Read it */
Count = *(PULONG)((ULONG_PTR)KeyValueInformation +
KeyValueInformation->DataOffset);
}
ExFreePool(KeyValueInformation);
KeyValueInformation = NULL;
/* Read the NextInstance value */
Status = IopGetRegistryValue(ServiceEnumKey,
L"NextInstance",
&KeyValueInformation);
if (!NT_SUCCESS(Status))
goto done;
if ((KeyValueInformation->Type == REG_DWORD) &&
(KeyValueInformation->DataLength))
{
NextInstance = *(PULONG)((ULONG_PTR)KeyValueInformation +
KeyValueInformation->DataOffset);
}
ExFreePool(KeyValueInformation);
KeyValueInformation = NULL;
}
/* Set the instance path */
swprintf(ValueBuffer, L"%lu", NextInstance);
RtlInitUnicodeString(&ValueName, ValueBuffer);
Status = ZwSetValueKey(ServiceEnumKey,
&ValueName,
0,
REG_SZ,
DeviceNode->InstancePath.Buffer,
DeviceNode->InstancePath.MaximumLength);
if (!NT_SUCCESS(Status))
goto done;
/* Increment Count and NextInstance */
Count++;
NextInstance++;
/* Set the new Count value */
RtlInitUnicodeString(&ValueName, L"Count");
Status = ZwSetValueKey(ServiceEnumKey,
&ValueName,
0,
REG_DWORD,
&Count,
sizeof(Count));
if (!NT_SUCCESS(Status))
goto done;
/* Set the new NextInstance value */
RtlInitUnicodeString(&ValueName, L"NextInstance");
Status = ZwSetValueKey(ServiceEnumKey,
&ValueName,
0,
REG_DWORD,
&NextInstance,
sizeof(NextInstance));
}
done:
if (ServiceEnumKey != NULL)
ZwClose(ServiceEnumKey);
if (ServiceKey != NULL)
ZwClose(ServiceKey);
ExFreePool(ServiceKeyName.Buffer);
return Status;
}
static
VOID
NTAPI
IopStartDevice2(IN PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
PDEVICE_NODE DeviceNode;
NTSTATUS Status;
PVOID Dummy;
DEVICE_CAPABILITIES DeviceCapabilities;
/* Get the device node */
DeviceNode = IopGetDeviceNode(DeviceObject);
ASSERT(!(DeviceNode->Flags & DNF_DISABLED));
/* Build the I/O stack location */
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_START_DEVICE;
Stack.Parameters.StartDevice.AllocatedResources =
DeviceNode->ResourceList;
Stack.Parameters.StartDevice.AllocatedResourcesTranslated =
DeviceNode->ResourceListTranslated;
/* Do the call */
Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
if (!NT_SUCCESS(Status))
{
/* Send an IRP_MN_REMOVE_DEVICE request */
IopRemoveDevice(DeviceNode);
/* Set the appropriate flag */
DeviceNode->Flags |= DNF_START_FAILED;
DeviceNode->Problem = CM_PROB_FAILED_START;
DPRINT1("Warning: PnP Start failed (%wZ) [Status: 0x%x]\n", &DeviceNode->InstancePath, Status);
return;
}
DPRINT("Sending IRP_MN_QUERY_CAPABILITIES to device stack (after start)\n");
Status = IopQueryDeviceCapabilities(DeviceNode, &DeviceCapabilities);
if (!NT_SUCCESS(Status))
{
DPRINT("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
}
/* Invalidate device state so IRP_MN_QUERY_PNP_DEVICE_STATE is sent */
IoInvalidateDeviceState(DeviceObject);
/* Otherwise, mark us as started */
DeviceNode->Flags |= DNF_STARTED;
DeviceNode->Flags &= ~DNF_STOPPED;
/* We now need enumeration */
DeviceNode->Flags |= DNF_NEED_ENUMERATION_ONLY;
}
static
NTSTATUS
NTAPI
IopStartAndEnumerateDevice(IN PDEVICE_NODE DeviceNode)
{
PDEVICE_OBJECT DeviceObject;
NTSTATUS Status;
PAGED_CODE();
/* Sanity check */
ASSERT((DeviceNode->Flags & DNF_ADDED));
ASSERT((DeviceNode->Flags & (DNF_RESOURCE_ASSIGNED |
DNF_RESOURCE_REPORTED |
DNF_NO_RESOURCE_REQUIRED)));
/* Get the device object */
DeviceObject = DeviceNode->PhysicalDeviceObject;
/* Check if we're not started yet */
if (!(DeviceNode->Flags & DNF_STARTED))
{
/* Start us */
IopStartDevice2(DeviceObject);
}
/* Do we need to query IDs? This happens in the case of manual reporting */
#if 0
if (DeviceNode->Flags & DNF_NEED_QUERY_IDS)
{
DPRINT1("Warning: Device node has DNF_NEED_QUERY_IDS\n");
/* And that case shouldn't happen yet */
ASSERT(FALSE);
}
#endif
IopSetServiceEnumData(DeviceNode);
/* Make sure we're started, and check if we need enumeration */
if ((DeviceNode->Flags & DNF_STARTED) &&
(DeviceNode->Flags & DNF_NEED_ENUMERATION_ONLY))
{
/* Enumerate us */
IoSynchronousInvalidateDeviceRelations(DeviceObject, BusRelations);
Status = STATUS_SUCCESS;
}
else
{
/* Nothing to do */
Status = STATUS_SUCCESS;
}
/* Return */
return Status;
}
NTSTATUS
IopStartDevice(
PDEVICE_NODE DeviceNode)
{
NTSTATUS Status;
HANDLE InstanceHandle = NULL, ControlHandle = NULL;
UNICODE_STRING KeyName, ValueString;
OBJECT_ATTRIBUTES ObjectAttributes;
if (DeviceNode->Flags & DNF_DISABLED)
return STATUS_SUCCESS;
Status = IopAssignDeviceResources(DeviceNode);
if (!NT_SUCCESS(Status))
goto ByeBye;
/* New PnP ABI */
IopStartAndEnumerateDevice(DeviceNode);
/* FIX: Should be done in new device instance code */
Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_NON_VOLATILE, &InstanceHandle);
if (!NT_SUCCESS(Status))
goto ByeBye;
/* FIX: Should be done in IoXxxPrepareDriverLoading */
// {
RtlInitUnicodeString(&KeyName, L"Control");
InitializeObjectAttributes(&ObjectAttributes,
&KeyName,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
InstanceHandle,
NULL);
Status = ZwCreateKey(&ControlHandle,
KEY_SET_VALUE,
&ObjectAttributes,
0,
NULL,
REG_OPTION_VOLATILE,
NULL);
if (!NT_SUCCESS(Status))
goto ByeBye;
RtlInitUnicodeString(&KeyName, L"ActiveService");
ValueString = DeviceNode->ServiceName;
if (!ValueString.Buffer)
RtlInitUnicodeString(&ValueString, L"");
Status = ZwSetValueKey(ControlHandle, &KeyName, 0, REG_SZ, ValueString.Buffer, ValueString.Length + sizeof(UNICODE_NULL));
// }
ByeBye:
if (ControlHandle != NULL)
ZwClose(ControlHandle);
if (InstanceHandle != NULL)
ZwClose(InstanceHandle);
return Status;
}
static
NTSTATUS
NTAPI
IopQueryStopDevice(IN PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
PVOID Dummy;
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_QUERY_STOP_DEVICE;
return IopSynchronousCall(DeviceObject, &Stack, &Dummy);
}
static
VOID
NTAPI
IopSendStopDevice(IN PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
PVOID Dummy;
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_STOP_DEVICE;
/* Drivers should never fail a IRP_MN_STOP_DEVICE request */
IopSynchronousCall(DeviceObject, &Stack, &Dummy);
}
NTSTATUS
IopStopDevice(
PDEVICE_NODE DeviceNode)
{
NTSTATUS Status;
DPRINT("Stopping device: %wZ\n", &DeviceNode->InstancePath);
Status = IopQueryStopDevice(DeviceNode->PhysicalDeviceObject);
if (NT_SUCCESS(Status))
{
IopSendStopDevice(DeviceNode->PhysicalDeviceObject);
DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
DeviceNode->Flags |= DNF_STOPPED;
return STATUS_SUCCESS;
}
return Status;
}
/* PUBLIC FUNCTIONS **********************************************************/
static
VOID
NTAPI
IopSendRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
PVOID Dummy;
PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
/* Drop all our state for this device in case it isn't really going away */
DeviceNode->Flags &= DNF_ENUMERATED | DNF_PROCESSED;
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_REMOVE_DEVICE;
/* Drivers should never fail a IRP_MN_REMOVE_DEVICE request */
IopSynchronousCall(DeviceObject, &Stack, &Dummy);
IopNotifyPlugPlayNotification(DeviceObject,
EventCategoryTargetDeviceChange,
&GUID_TARGET_DEVICE_REMOVE_COMPLETE,
NULL,
NULL);
ObDereferenceObject(DeviceObject);
}
static
VOID
IopSendRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
{
/* This function DOES dereference the device objects in all cases */
ULONG i;
for (i = 0; i < DeviceRelations->Count; i++)
{
IopSendRemoveDevice(DeviceRelations->Objects[i]);
DeviceRelations->Objects[i] = NULL;
}
ExFreePool(DeviceRelations);
}
static
VOID
IopSendRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
{
PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
KIRQL OldIrql;
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
ChildDeviceNode = ParentDeviceNode->Child;
while (ChildDeviceNode != NULL)
{
NextDeviceNode = ChildDeviceNode->Sibling;
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
IopSendRemoveDevice(ChildDeviceNode->PhysicalDeviceObject);
ChildDeviceNode = NextDeviceNode;
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
}
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
}
static
VOID
NTAPI
IopSendSurpriseRemoval(IN PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
PVOID Dummy;
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_SURPRISE_REMOVAL;
/* Drivers should never fail a IRP_MN_SURPRISE_REMOVAL request */
IopSynchronousCall(DeviceObject, &Stack, &Dummy);
}
static
VOID
NTAPI
IopCancelRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
PVOID Dummy;
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_CANCEL_REMOVE_DEVICE;
/* Drivers should never fail a IRP_MN_CANCEL_REMOVE_DEVICE request */
IopSynchronousCall(DeviceObject, &Stack, &Dummy);
IopNotifyPlugPlayNotification(DeviceObject,
EventCategoryTargetDeviceChange,
&GUID_TARGET_DEVICE_REMOVE_CANCELLED,
NULL,
NULL);
}
static
VOID
IopCancelRemoveChildDevices(PDEVICE_NODE ParentDeviceNode)
{
PDEVICE_NODE ChildDeviceNode, NextDeviceNode;
KIRQL OldIrql;
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
ChildDeviceNode = ParentDeviceNode->Child;
while (ChildDeviceNode != NULL)
{
NextDeviceNode = ChildDeviceNode->Sibling;
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
ChildDeviceNode = NextDeviceNode;
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
}
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
}
static
VOID
IopCancelRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations)
{
/* This function DOES dereference the device objects in all cases */
ULONG i;
for (i = 0; i < DeviceRelations->Count; i++)
{
IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
ObDereferenceObject(DeviceRelations->Objects[i]);
DeviceRelations->Objects[i] = NULL;
}
ExFreePool(DeviceRelations);
}
static
VOID
IopCancelPrepareDeviceForRemoval(PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
IO_STATUS_BLOCK IoStatusBlock;
PDEVICE_RELATIONS DeviceRelations;
NTSTATUS Status;
IopCancelRemoveDevice(DeviceObject);
Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
Status = IopInitiatePnpIrp(DeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_DEVICE_RELATIONS,
&Stack);
if (!NT_SUCCESS(Status))
{
DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
DeviceRelations = NULL;
}
else
{
DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
}
if (DeviceRelations)
IopCancelRemoveDeviceRelations(DeviceRelations);
}
static
NTSTATUS
NTAPI
IopQueryRemoveDevice(IN PDEVICE_OBJECT DeviceObject)
{
PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
IO_STACK_LOCATION Stack;
PVOID Dummy;
NTSTATUS Status;
ASSERT(DeviceNode);
IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVE_PENDING,
&DeviceNode->InstancePath);
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_QUERY_REMOVE_DEVICE;
Status = IopSynchronousCall(DeviceObject, &Stack, &Dummy);
IopNotifyPlugPlayNotification(DeviceObject,
EventCategoryTargetDeviceChange,
&GUID_TARGET_DEVICE_QUERY_REMOVE,
NULL,
NULL);
if (!NT_SUCCESS(Status))
{
DPRINT1("Removal vetoed by %wZ\n", &DeviceNode->InstancePath);
IopQueueTargetDeviceEvent(&GUID_DEVICE_REMOVAL_VETOED,
&DeviceNode->InstancePath);
}
return Status;
}
static
NTSTATUS
IopQueryRemoveChildDevices(PDEVICE_NODE ParentDeviceNode, BOOLEAN Force)
{
PDEVICE_NODE ChildDeviceNode, NextDeviceNode, FailedRemoveDevice;
NTSTATUS Status;
KIRQL OldIrql;
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
ChildDeviceNode = ParentDeviceNode->Child;
while (ChildDeviceNode != NULL)
{
NextDeviceNode = ChildDeviceNode->Sibling;
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
Status = IopPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject, Force);
if (!NT_SUCCESS(Status))
{
FailedRemoveDevice = ChildDeviceNode;
goto cleanup;
}
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
ChildDeviceNode = NextDeviceNode;
}
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
return STATUS_SUCCESS;
cleanup:
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
ChildDeviceNode = ParentDeviceNode->Child;
while (ChildDeviceNode != NULL)
{
NextDeviceNode = ChildDeviceNode->Sibling;
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
IopCancelPrepareDeviceForRemoval(ChildDeviceNode->PhysicalDeviceObject);
/* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
* that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
if (ChildDeviceNode == FailedRemoveDevice)
return Status;
ChildDeviceNode = NextDeviceNode;
KeAcquireSpinLock(&IopDeviceTreeLock, &OldIrql);
}
KeReleaseSpinLock(&IopDeviceTreeLock, OldIrql);
return Status;
}
static
NTSTATUS
IopQueryRemoveDeviceRelations(PDEVICE_RELATIONS DeviceRelations, BOOLEAN Force)
{
/* This function DOES NOT dereference the device objects on SUCCESS
* but it DOES dereference device objects on FAILURE */
ULONG i, j;
NTSTATUS Status;
for (i = 0; i < DeviceRelations->Count; i++)
{
Status = IopPrepareDeviceForRemoval(DeviceRelations->Objects[i], Force);
if (!NT_SUCCESS(Status))
{
j = i;
goto cleanup;
}
}
return STATUS_SUCCESS;
cleanup:
/* IRP_MN_CANCEL_REMOVE_DEVICE is also sent to the device
* that failed the IRP_MN_QUERY_REMOVE_DEVICE request */
for (i = 0; i <= j; i++)
{
IopCancelPrepareDeviceForRemoval(DeviceRelations->Objects[i]);
ObDereferenceObject(DeviceRelations->Objects[i]);
DeviceRelations->Objects[i] = NULL;
}
for (; i < DeviceRelations->Count; i++)
{
ObDereferenceObject(DeviceRelations->Objects[i]);
DeviceRelations->Objects[i] = NULL;
}
ExFreePool(DeviceRelations);
return Status;
}
static
NTSTATUS
IopPrepareDeviceForRemoval(IN PDEVICE_OBJECT DeviceObject, BOOLEAN Force)
{
PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
IO_STACK_LOCATION Stack;
IO_STATUS_BLOCK IoStatusBlock;
PDEVICE_RELATIONS DeviceRelations;
NTSTATUS Status;
if ((DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE) && !Force)
{
DPRINT1("Removal not allowed for %wZ\n", &DeviceNode->InstancePath);
return STATUS_UNSUCCESSFUL;
}
if (!Force && IopQueryRemoveDevice(DeviceObject) != STATUS_SUCCESS)
{
DPRINT1("Removal vetoed by failing the query remove request\n");
IopCancelRemoveDevice(DeviceObject);
return STATUS_UNSUCCESSFUL;
}
Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
Status = IopInitiatePnpIrp(DeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_DEVICE_RELATIONS,
&Stack);
if (!NT_SUCCESS(Status))
{
DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
DeviceRelations = NULL;
}
else
{
DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
}
if (DeviceRelations)
{
Status = IopQueryRemoveDeviceRelations(DeviceRelations, Force);
if (!NT_SUCCESS(Status))
return Status;
}
Status = IopQueryRemoveChildDevices(DeviceNode, Force);
if (!NT_SUCCESS(Status))
{
if (DeviceRelations)
IopCancelRemoveDeviceRelations(DeviceRelations);
return Status;
}
if (DeviceRelations)
IopSendRemoveDeviceRelations(DeviceRelations);
IopSendRemoveChildDevices(DeviceNode);
return STATUS_SUCCESS;
}
static
VOID
IopHandleDeviceRemoval(
IN PDEVICE_NODE DeviceNode,
IN PDEVICE_RELATIONS DeviceRelations)
{
PDEVICE_NODE Child = DeviceNode->Child, NextChild;
ULONG i;
BOOLEAN Found;
if (DeviceNode == IopRootDeviceNode)
return;
while (Child != NULL)
{
NextChild = Child->Sibling;
Found = FALSE;
for (i = 0; DeviceRelations && i < DeviceRelations->Count; i++)
{
if (IopGetDeviceNode(DeviceRelations->Objects[i]) == Child)
{
Found = TRUE;
break;
}
}
if (!Found && !(Child->Flags & DNF_WILL_BE_REMOVED))
{
/* Send removal IRPs to all of its children */
IopPrepareDeviceForRemoval(Child->PhysicalDeviceObject, TRUE);
/* Send the surprise removal IRP */
IopSendSurpriseRemoval(Child->PhysicalDeviceObject);
/* Tell the user-mode PnP manager that a device was removed */
IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
&Child->InstancePath);
/* Send the remove device IRP */
IopSendRemoveDevice(Child->PhysicalDeviceObject);
}
Child = NextChild;
}
}
NTSTATUS
IopRemoveDevice(PDEVICE_NODE DeviceNode)
{
NTSTATUS Status;
DPRINT("Removing device: %wZ\n", &DeviceNode->InstancePath);
Status = IopPrepareDeviceForRemoval(DeviceNode->PhysicalDeviceObject, FALSE);
if (NT_SUCCESS(Status))
{
IopSendRemoveDevice(DeviceNode->PhysicalDeviceObject);
IopQueueTargetDeviceEvent(&GUID_DEVICE_SAFE_REMOVAL,
&DeviceNode->InstancePath);
return STATUS_SUCCESS;
}
return Status;
}
/*
* @implemented
*/
VOID
NTAPI
IoInvalidateDeviceState(IN PDEVICE_OBJECT PhysicalDeviceObject)
{
PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
IO_STACK_LOCATION Stack;
ULONG_PTR PnPFlags;
NTSTATUS Status;
IO_STATUS_BLOCK IoStatusBlock;
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_QUERY_PNP_DEVICE_STATE;
Status = IopSynchronousCall(PhysicalDeviceObject, &Stack, (PVOID*)&PnPFlags);
if (!NT_SUCCESS(Status))
{
if (Status != STATUS_NOT_SUPPORTED)
{
DPRINT1("IRP_MN_QUERY_PNP_DEVICE_STATE failed with status 0x%lx\n", Status);
}
return;
}
if (PnPFlags & PNP_DEVICE_NOT_DISABLEABLE)
DeviceNode->UserFlags |= DNUF_NOT_DISABLEABLE;
else
DeviceNode->UserFlags &= ~DNUF_NOT_DISABLEABLE;
if (PnPFlags & PNP_DEVICE_DONT_DISPLAY_IN_UI)
DeviceNode->UserFlags |= DNUF_DONT_SHOW_IN_UI;
else
DeviceNode->UserFlags &= ~DNUF_DONT_SHOW_IN_UI;
if ((PnPFlags & PNP_DEVICE_REMOVED) ||
((PnPFlags & PNP_DEVICE_FAILED) && !(PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)))
{
/* Flag it if it's failed */
if (PnPFlags & PNP_DEVICE_FAILED) DeviceNode->Problem = CM_PROB_FAILED_POST_START;
/* Send removal IRPs to all of its children */
IopPrepareDeviceForRemoval(PhysicalDeviceObject, TRUE);
/* Send surprise removal */
IopSendSurpriseRemoval(PhysicalDeviceObject);
/* Tell the user-mode PnP manager that a device was removed */
IopQueueTargetDeviceEvent(&GUID_DEVICE_SURPRISE_REMOVAL,
&DeviceNode->InstancePath);
IopSendRemoveDevice(PhysicalDeviceObject);
}
else if ((PnPFlags & PNP_DEVICE_FAILED) && (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED))
{
/* Stop for resource rebalance */
Status = IopStopDevice(DeviceNode);
if (!NT_SUCCESS(Status))
{
DPRINT1("Failed to stop device for rebalancing\n");
/* Stop failed so don't rebalance */
PnPFlags &= ~PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED;
}
}
/* Resource rebalance */
if (PnPFlags & PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED)
{
DPRINT("Sending IRP_MN_QUERY_RESOURCES to device stack\n");
Status = IopInitiatePnpIrp(PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_RESOURCES,
NULL);
if (NT_SUCCESS(Status) && IoStatusBlock.Information)
{
DeviceNode->BootResources =
(PCM_RESOURCE_LIST)IoStatusBlock.Information;
IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %x) or IoStatusBlock.Information=NULL\n", Status);
DeviceNode->BootResources = NULL;
}
DPRINT("Sending IRP_MN_QUERY_RESOURCE_REQUIREMENTS to device stack\n");
Status = IopInitiatePnpIrp(PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_RESOURCE_REQUIREMENTS,
NULL);
if (NT_SUCCESS(Status))
{
DeviceNode->ResourceRequirements =
(PIO_RESOURCE_REQUIREMENTS_LIST)IoStatusBlock.Information;
}
else
{
DPRINT("IopInitiatePnpIrp() failed (Status %08lx)\n", Status);
DeviceNode->ResourceRequirements = NULL;
}
/* IRP_MN_FILTER_RESOURCE_REQUIREMENTS is called indirectly by IopStartDevice */
if (IopStartDevice(DeviceNode) != STATUS_SUCCESS)
{
DPRINT1("Restart after resource rebalance failed\n");
DeviceNode->Flags &= ~(DNF_STARTED | DNF_START_REQUEST_PENDING);
DeviceNode->Flags |= DNF_START_FAILED;
IopRemoveDevice(DeviceNode);
}
}
}
/*
* IopInitializePnpServices
*
* Initialize services for discovered children
*
* Parameters
* DeviceNode
* Top device node to start initializing services.
*
* Return Value
* Status
*/
NTSTATUS
IopInitializePnpServices(IN PDEVICE_NODE DeviceNode)
{
DEVICETREE_TRAVERSE_CONTEXT Context;
DPRINT("IopInitializePnpServices(%p)\n", DeviceNode);
IopInitDeviceTreeTraverseContext(
&Context,
DeviceNode,
IopActionInitChildServices,
DeviceNode);
return IopTraverseDeviceTree(&Context);
}
NTSTATUS
IopEnumerateDevice(
IN PDEVICE_OBJECT DeviceObject)
{
PDEVICE_NODE DeviceNode = IopGetDeviceNode(DeviceObject);
DEVICETREE_TRAVERSE_CONTEXT Context;
PDEVICE_RELATIONS DeviceRelations;
PDEVICE_OBJECT ChildDeviceObject;
IO_STATUS_BLOCK IoStatusBlock;
PDEVICE_NODE ChildDeviceNode;
IO_STACK_LOCATION Stack;
NTSTATUS Status;
ULONG i;
DPRINT("DeviceObject 0x%p\n", DeviceObject);
if (DeviceNode->Flags & DNF_NEED_ENUMERATION_ONLY)
{
DeviceNode->Flags &= ~DNF_NEED_ENUMERATION_ONLY;
DPRINT("Sending GUID_DEVICE_ARRIVAL\n");
IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
&DeviceNode->InstancePath);
}
DPRINT("Sending IRP_MN_QUERY_DEVICE_RELATIONS to device stack\n");
Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
Status = IopInitiatePnpIrp(
DeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_DEVICE_RELATIONS,
&Stack);
if (!NT_SUCCESS(Status) || Status == STATUS_PENDING)
{
DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
return Status;
}
DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
/*
* Send removal IRPs for devices that have disappeared
* NOTE: This code handles the case where no relations are specified
*/
IopHandleDeviceRemoval(DeviceNode, DeviceRelations);
/* Now we bail if nothing was returned */
if (!DeviceRelations)
{
/* We're all done */
DPRINT("No PDOs\n");
return STATUS_SUCCESS;
}
DPRINT("Got %u PDOs\n", DeviceRelations->Count);
/*
* Create device nodes for all discovered devices
*/
for (i = 0; i < DeviceRelations->Count; i++)
{
ChildDeviceObject = DeviceRelations->Objects[i];
ASSERT((ChildDeviceObject->Flags & DO_DEVICE_INITIALIZING) == 0);
ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
if (!ChildDeviceNode)
{
/* One doesn't exist, create it */
Status = IopCreateDeviceNode(
DeviceNode,
ChildDeviceObject,
NULL,
&ChildDeviceNode);
if (NT_SUCCESS(Status))
{
/* Mark the node as enumerated */
ChildDeviceNode->Flags |= DNF_ENUMERATED;
/* Mark the DO as bus enumerated */
ChildDeviceObject->Flags |= DO_BUS_ENUMERATED_DEVICE;
}
else
{
/* Ignore this DO */
DPRINT1("IopCreateDeviceNode() failed with status 0x%08x. Skipping PDO %u\n", Status, i);
ObDereferenceObject(ChildDeviceObject);
}
}
else
{
/* Mark it as enumerated */
ChildDeviceNode->Flags |= DNF_ENUMERATED;
ObDereferenceObject(ChildDeviceObject);
}
}
ExFreePool(DeviceRelations);
/*
* Retrieve information about all discovered children from the bus driver
*/
IopInitDeviceTreeTraverseContext(
&Context,
DeviceNode,
IopActionInterrogateDeviceStack,
DeviceNode);
Status = IopTraverseDeviceTree(&Context);
if (!NT_SUCCESS(Status))
{
DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
return Status;
}
/*
* Retrieve configuration from the registry for discovered children
*/
IopInitDeviceTreeTraverseContext(
&Context,
DeviceNode,
IopActionConfigureChildServices,
DeviceNode);
Status = IopTraverseDeviceTree(&Context);
if (!NT_SUCCESS(Status))
{
DPRINT("IopTraverseDeviceTree() failed with status 0x%08lx\n", Status);
return Status;
}
/*
* Initialize services for discovered children.
*/
Status = IopInitializePnpServices(DeviceNode);
if (!NT_SUCCESS(Status))
{
DPRINT("IopInitializePnpServices() failed with status 0x%08lx\n", Status);
return Status;
}
DPRINT("IopEnumerateDevice() finished\n");
return STATUS_SUCCESS;
}
static
NTSTATUS
NTAPI
IopSendEject(IN PDEVICE_OBJECT DeviceObject)
{
IO_STACK_LOCATION Stack;
PVOID Dummy;
RtlZeroMemory(&Stack, sizeof(IO_STACK_LOCATION));
Stack.MajorFunction = IRP_MJ_PNP;
Stack.MinorFunction = IRP_MN_EJECT;
return IopSynchronousCall(DeviceObject, &Stack, &Dummy);
}
/*
* @implemented
*/
VOID
NTAPI
IoRequestDeviceEject(IN PDEVICE_OBJECT PhysicalDeviceObject)
{
PDEVICE_NODE DeviceNode = IopGetDeviceNode(PhysicalDeviceObject);
PDEVICE_RELATIONS DeviceRelations;
IO_STATUS_BLOCK IoStatusBlock;
IO_STACK_LOCATION Stack;
DEVICE_CAPABILITIES Capabilities;
NTSTATUS Status;
IopQueueTargetDeviceEvent(&GUID_DEVICE_KERNEL_INITIATED_EJECT,
&DeviceNode->InstancePath);
if (IopQueryDeviceCapabilities(DeviceNode, &Capabilities) != STATUS_SUCCESS)
{
goto cleanup;
}
Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
Status = IopInitiatePnpIrp(PhysicalDeviceObject,
&IoStatusBlock,
IRP_MN_QUERY_DEVICE_RELATIONS,
&Stack);
if (!NT_SUCCESS(Status))
{
DPRINT("IopInitiatePnpIrp() failed with status 0x%08lx\n", Status);
DeviceRelations = NULL;
}
else
{
DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
}
if (DeviceRelations)
{
Status = IopQueryRemoveDeviceRelations(DeviceRelations, FALSE);
if (!NT_SUCCESS(Status))
goto cleanup;
}
Status = IopQueryRemoveChildDevices(DeviceNode, FALSE);
if (!NT_SUCCESS(Status))
{
if (DeviceRelations)
IopCancelRemoveDeviceRelations(DeviceRelations);
goto cleanup;
}
if (IopPrepareDeviceForRemoval(PhysicalDeviceObject, FALSE) != STATUS_SUCCESS)
{
if (DeviceRelations)
IopCancelRemoveDeviceRelations(DeviceRelations);
IopCancelRemoveChildDevices(DeviceNode);
goto cleanup;
}
if (DeviceRelations)
IopSendRemoveDeviceRelations(DeviceRelations);
IopSendRemoveChildDevices(DeviceNode);
DeviceNode->Problem = CM_PROB_HELD_FOR_EJECT;
if (Capabilities.EjectSupported)
{
if (IopSendEject(PhysicalDeviceObject) != STATUS_SUCCESS)
{
goto cleanup;
}
}
else
{
DeviceNode->Flags |= DNF_DISABLED;
}
IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT,
&DeviceNode->InstancePath);
return;
cleanup:
IopQueueTargetDeviceEvent(&GUID_DEVICE_EJECT_VETOED,
&DeviceNode->InstancePath);
}
static
VOID
NTAPI
IopDeviceActionWorker(
_In_ PVOID Context)
{
PLIST_ENTRY ListEntry;
PDEVICE_ACTION_DATA Data;
KIRQL OldIrql;
KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
while (!IsListEmpty(&IopDeviceActionRequestList))
{
ListEntry = RemoveHeadList(&IopDeviceActionRequestList);
KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
Data = CONTAINING_RECORD(ListEntry,
DEVICE_ACTION_DATA,
RequestListEntry);
switch (Data->Action)
{
case DeviceActionInvalidateDeviceRelations:
IoSynchronousInvalidateDeviceRelations(Data->DeviceObject,
Data->InvalidateDeviceRelations.Type);
break;
default:
DPRINT1("Unimplemented device action %u\n", Data->Action);
break;
}
ObDereferenceObject(Data->DeviceObject);
ExFreePoolWithTag(Data, TAG_IO);
KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
}
IopDeviceActionInProgress = FALSE;
KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
}
VOID
IopQueueDeviceAction(
_In_ PDEVICE_ACTION_DATA ActionData)
{
PDEVICE_ACTION_DATA Data;
KIRQL OldIrql;
DPRINT("IopQueueDeviceAction(%p)\n", ActionData);
Data = ExAllocatePoolWithTag(NonPagedPool,
sizeof(DEVICE_ACTION_DATA),
TAG_IO);
if (!Data)
return;
ObReferenceObject(ActionData->DeviceObject);
RtlCopyMemory(Data, ActionData, sizeof(DEVICE_ACTION_DATA));
DPRINT("Action %u\n", Data->Action);
KeAcquireSpinLock(&IopDeviceActionLock, &OldIrql);
InsertTailList(&IopDeviceActionRequestList, &Data->RequestListEntry);
if (IopDeviceActionInProgress)
{
KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
return;
}
IopDeviceActionInProgress = TRUE;
KeReleaseSpinLock(&IopDeviceActionLock, OldIrql);
ExInitializeWorkItem(&IopDeviceActionWorkItem,
IopDeviceActionWorker,
NULL);
ExQueueWorkItem(&IopDeviceActionWorkItem,
DelayedWorkQueue);
}