/* * 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 #define NDEBUG #include /* 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); }