/* * PROJECT: ReactOS Kernel * COPYRIGHT: GPL - See COPYING in the top level directory * FILE: ntoskrnl/io/pnpmgr/plugplay.c * PURPOSE: Plug-and-play interface routines * PROGRAMMERS: Eric Kohl */ /* INCLUDES *****************************************************************/ #include #define NDEBUG #include typedef struct _PNP_EVENT_ENTRY { LIST_ENTRY ListEntry; PLUGPLAY_EVENT_BLOCK Event; } PNP_EVENT_ENTRY, *PPNP_EVENT_ENTRY; /* GLOBALS *******************************************************************/ static LIST_ENTRY IopPnpEventQueueHead; static KEVENT IopPnpNotifyEvent; /* FUNCTIONS *****************************************************************/ CODE_SEG("INIT") NTSTATUS IopInitPlugPlayEvents(VOID) { InitializeListHead(&IopPnpEventQueueHead); KeInitializeEvent(&IopPnpNotifyEvent, SynchronizationEvent, FALSE); return STATUS_SUCCESS; } NTSTATUS IopQueueTargetDeviceEvent(const GUID *Guid, PUNICODE_STRING DeviceIds) { PPNP_EVENT_ENTRY EventEntry; UNICODE_STRING Copy; ULONG TotalSize; NTSTATUS Status; ASSERT(DeviceIds); /* Allocate a big enough buffer */ Copy.Length = 0; Copy.MaximumLength = DeviceIds->Length + sizeof(UNICODE_NULL); TotalSize = FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, TargetDevice.DeviceIds) + Copy.MaximumLength; EventEntry = ExAllocatePool(NonPagedPool, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event)); if (!EventEntry) return STATUS_INSUFFICIENT_RESOURCES; RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event)); /* Fill the buffer with the event GUID */ RtlCopyMemory(&EventEntry->Event.EventGuid, Guid, sizeof(GUID)); EventEntry->Event.EventCategory = TargetDeviceChangeEvent; EventEntry->Event.TotalSize = TotalSize; /* Fill the device id */ Copy.Buffer = EventEntry->Event.TargetDevice.DeviceIds; Status = RtlAppendUnicodeStringToString(&Copy, DeviceIds); if (!NT_SUCCESS(Status)) { ExFreePool(EventEntry); return Status; } InsertHeadList(&IopPnpEventQueueHead, &EventEntry->ListEntry); KeSetEvent(&IopPnpNotifyEvent, 0, FALSE); return STATUS_SUCCESS; } static PDEVICE_OBJECT IopTraverseDeviceNode(PDEVICE_NODE Node, PUNICODE_STRING DeviceInstance) { PDEVICE_OBJECT DeviceObject; PDEVICE_NODE ChildNode; if (RtlEqualUnicodeString(&Node->InstancePath, DeviceInstance, TRUE)) { ObReferenceObject(Node->PhysicalDeviceObject); return Node->PhysicalDeviceObject; } /* Traversal of all children nodes */ for (ChildNode = Node->Child; ChildNode != NULL; ChildNode = ChildNode->Sibling) { DeviceObject = IopTraverseDeviceNode(ChildNode, DeviceInstance); if (DeviceObject != NULL) { return DeviceObject; } } return NULL; } PDEVICE_OBJECT IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance) { if (IopRootDeviceNode == NULL) return NULL; if (DeviceInstance == NULL || DeviceInstance->Length == 0) { if (IopRootDeviceNode->PhysicalDeviceObject) { ObReferenceObject(IopRootDeviceNode->PhysicalDeviceObject); return IopRootDeviceNode->PhysicalDeviceObject; } else return NULL; } return IopTraverseDeviceNode(IopRootDeviceNode, DeviceInstance); } static NTSTATUS IopCaptureUnicodeString(PUNICODE_STRING DstName, PUNICODE_STRING SrcName) { NTSTATUS Status = STATUS_SUCCESS; volatile UNICODE_STRING Name; Name.Buffer = NULL; _SEH2_TRY { Name.Length = SrcName->Length; Name.MaximumLength = SrcName->MaximumLength; if (Name.Length > Name.MaximumLength) { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } if (Name.MaximumLength) { ProbeForRead(SrcName->Buffer, Name.MaximumLength, sizeof(WCHAR)); Name.Buffer = ExAllocatePool(NonPagedPool, Name.MaximumLength); if (Name.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } memcpy(Name.Buffer, SrcName->Buffer, Name.MaximumLength); } *DstName = Name; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { if (Name.Buffer) { ExFreePool(Name.Buffer); } Status = _SEH2_GetExceptionCode(); } _SEH2_END; return Status; } static NTSTATUS IopInitializeDevice( _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData) { UNICODE_STRING DeviceInstance; PDEVICE_OBJECT DeviceObject; PDEVICE_NODE DeviceNode; NTSTATUS Status = STATUS_SUCCESS; DPRINT("IopInitializeDevice(%p)\n", ControlData); Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance); if (!NT_SUCCESS(Status)) { return Status; } DPRINT("Device: %wZ\n", &DeviceInstance); /* Leave, if the device already exists */ DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceInstance.Buffer != NULL) { DPRINT1("Device %wZ already exists!\n", &DeviceInstance); Status = STATUS_SUCCESS; goto done; } ObDereferenceObject(DeviceObject); DPRINT("Device %wZ does not exist!\n", &DeviceInstance); /* Create a device node for the device instance */ Status = IoCreateDevice(IopRootDriverObject, 0, NULL, FILE_DEVICE_CONTROLLER, FILE_AUTOGENERATED_DEVICE_NAME, FALSE, &DeviceObject); if (!NT_SUCCESS(Status)) { DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status); goto done; } /* Allocate a new device node */ DeviceNode = PipAllocateDeviceNode(DeviceObject); if (DeviceNode == NULL) { DPRINT1("Failed to allocate a device node!\n"); IoDeleteDevice(DeviceObject); Status = STATUS_INSUFFICIENT_RESOURCES; goto done; } /* Set the device instance of the device node */ Status = RtlDuplicateUnicodeString(0, &DeviceInstance, &DeviceNode->InstancePath); if (!NT_SUCCESS(Status)) { DPRINT1("RtlDuplicateUnicodeString() failed (Status 0x%08lx)\n", Status); IopFreeDeviceNode(DeviceNode); IoDeleteDevice(DeviceObject); goto done; } /* Insert as a root enumerated device node */ PiInsertDevNode(DeviceNode, IopRootDeviceNode); done: ExFreePool(DeviceInstance.Buffer); return Status; } /* * Remove the current PnP event from the tail of the event queue * and signal IopPnpNotifyEvent if there is yet another event in the queue. */ static NTSTATUS IopRemovePlugPlayEvent( _In_ PPLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData) { /* Remove a pnp event entry from the tail of the queue */ if (!IsListEmpty(&IopPnpEventQueueHead)) { ExFreePool(CONTAINING_RECORD(RemoveTailList(&IopPnpEventQueueHead), PNP_EVENT_ENTRY, ListEntry)); } /* Signal the next pnp event in the queue */ if (!IsListEmpty(&IopPnpEventQueueHead)) { KeSetEvent(&IopPnpNotifyEvent, 0, FALSE); } return STATUS_SUCCESS; } static NTSTATUS IopGetInterfaceDeviceList(PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA DeviceList) { NTSTATUS Status; PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA StackList; UNICODE_STRING DeviceInstance; PDEVICE_OBJECT DeviceObject = NULL; GUID FilterGuid; PZZWSTR SymbolicLinkList = NULL, LinkList; SIZE_T TotalLength; _SEH2_TRY { RtlCopyMemory(&StackList, DeviceList, sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA)); ProbeForRead(StackList.FilterGuid, sizeof(GUID), sizeof(UCHAR)); RtlCopyMemory(&FilterGuid, StackList.FilterGuid, sizeof(GUID)); if (StackList.Buffer != NULL && StackList.BufferSize != 0) { ProbeForWrite(StackList.Buffer, StackList.BufferSize, sizeof(UCHAR)); } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; Status = IopCaptureUnicodeString(&DeviceInstance, &StackList.DeviceInstance); if (NT_SUCCESS(Status)) { /* Get the device object */ DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } } Status = IoGetDeviceInterfaces(&FilterGuid, DeviceObject, StackList.Flags, &SymbolicLinkList); ObDereferenceObject(DeviceObject); if (!NT_SUCCESS(Status)) { /* failed */ return Status; } LinkList = SymbolicLinkList; while (*SymbolicLinkList != UNICODE_NULL) { SymbolicLinkList += wcslen(SymbolicLinkList) + (sizeof(UNICODE_NULL) / sizeof(WCHAR)); } TotalLength = ((SymbolicLinkList - LinkList + 1) * sizeof(WCHAR)); _SEH2_TRY { if (StackList.Buffer != NULL && StackList.BufferSize >= TotalLength) { // We've already probed the buffer for writing above. RtlCopyMemory(StackList.Buffer, LinkList, TotalLength); } DeviceList->BufferSize = TotalLength; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { ExFreePool(LinkList); _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; ExFreePool(LinkList); return STATUS_SUCCESS; } static NTSTATUS IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData) { PDEVICE_OBJECT DeviceObject = NULL; PDEVICE_NODE DeviceNode; UNICODE_STRING DeviceInstance; ULONG BufferSize; ULONG Property; DEVICE_REGISTRY_PROPERTY DeviceProperty; PVOID Buffer; NTSTATUS Status; DPRINT("IopGetDeviceProperty() called\n"); DPRINT("Device name: %wZ\n", &PropertyData->DeviceInstance); Status = IopCaptureUnicodeString(&DeviceInstance, &PropertyData->DeviceInstance); if (!NT_SUCCESS(Status)) { return Status; } _SEH2_TRY { Property = PropertyData->Property; BufferSize = PropertyData->BufferSize; ProbeForWrite(PropertyData->Buffer, BufferSize, sizeof(UCHAR)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Get the device object */ DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } if (DeviceObject == NULL) { return STATUS_NO_SUCH_DEVICE; } Buffer = ExAllocatePool(NonPagedPool, BufferSize); if (Buffer == NULL) { ObDereferenceObject(DeviceObject); return STATUS_INSUFFICIENT_RESOURCES; } DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode; if (Property == PNP_PROPERTY_POWER_DATA) { if (BufferSize < sizeof(CM_POWER_DATA)) { BufferSize = 0; Status = STATUS_BUFFER_TOO_SMALL; } else { DEVICE_CAPABILITIES DeviceCapabilities; PCM_POWER_DATA PowerData; IO_STACK_LOCATION Stack; IO_STATUS_BLOCK IoStatusBlock; PowerData = (PCM_POWER_DATA)Buffer; RtlZeroMemory(PowerData, sizeof(CM_POWER_DATA)); PowerData->PD_Size = sizeof(CM_POWER_DATA); RtlZeroMemory(&DeviceCapabilities, sizeof(DEVICE_CAPABILITIES)); DeviceCapabilities.Size = sizeof(DEVICE_CAPABILITIES); DeviceCapabilities.Version = 1; DeviceCapabilities.Address = -1; DeviceCapabilities.UINumber = -1; Stack.Parameters.DeviceCapabilities.Capabilities = &DeviceCapabilities; Status = IopInitiatePnpIrp(DeviceObject, &IoStatusBlock, IRP_MN_QUERY_CAPABILITIES, &Stack); if (NT_SUCCESS(Status)) { DPRINT("Got device capabiliities\n"); PowerData->PD_MostRecentPowerState = PowerDeviceD0; // FIXME if (DeviceCapabilities.DeviceD1) PowerData->PD_Capabilities |= PDCAP_D1_SUPPORTED; if (DeviceCapabilities.DeviceD2) PowerData->PD_Capabilities |= PDCAP_D2_SUPPORTED; if (DeviceCapabilities.WakeFromD0) PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D0_SUPPORTED; if (DeviceCapabilities.WakeFromD1) PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D1_SUPPORTED; if (DeviceCapabilities.WakeFromD2) PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D2_SUPPORTED; if (DeviceCapabilities.WakeFromD3) PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D3_SUPPORTED; if (DeviceCapabilities.WarmEjectSupported) PowerData->PD_Capabilities |= PDCAP_WARM_EJECT_SUPPORTED; PowerData->PD_D1Latency = DeviceCapabilities.D1Latency; PowerData->PD_D2Latency = DeviceCapabilities.D2Latency; PowerData->PD_D3Latency = DeviceCapabilities.D3Latency; RtlCopyMemory(&PowerData->PD_PowerStateMapping, &DeviceCapabilities.DeviceState, sizeof(DeviceCapabilities.DeviceState)); PowerData->PD_DeepestSystemWake = DeviceCapabilities.SystemWake; } else { DPRINT("IRP_MN_QUERY_CAPABILITIES failed (Status 0x%08lx)\n", Status); PowerData->PD_Capabilities = PDCAP_D0_SUPPORTED | PDCAP_D3_SUPPORTED; PowerData->PD_MostRecentPowerState = PowerDeviceD0; } } } else if (Property == PNP_PROPERTY_REMOVAL_POLICY_OVERRIDE) { UNIMPLEMENTED; BufferSize = 0; Status = STATUS_NOT_IMPLEMENTED; } else if (Property == PNP_PROPERTY_REMOVAL_POLICY_HARDWARE_DEFAULT) { if (BufferSize < sizeof(DeviceNode->HardwareRemovalPolicy)) { BufferSize = 0; Status = STATUS_BUFFER_TOO_SMALL; } else { BufferSize = sizeof(DeviceNode->HardwareRemovalPolicy); RtlCopyMemory(Buffer, &DeviceNode->HardwareRemovalPolicy, BufferSize); } } else { switch (Property) { case PNP_PROPERTY_UI_NUMBER: DeviceProperty = DevicePropertyUINumber; break; case PNP_PROPERTY_PHYSICAL_DEVICE_OBJECT_NAME: DeviceProperty = DevicePropertyPhysicalDeviceObjectName; break; case PNP_PROPERTY_BUSTYPEGUID: DeviceProperty = DevicePropertyBusTypeGuid; break; case PNP_PROPERTY_LEGACYBUSTYPE: DeviceProperty = DevicePropertyLegacyBusType; break; case PNP_PROPERTY_BUSNUMBER: DeviceProperty = DevicePropertyBusNumber; break; case PNP_PROPERTY_REMOVAL_POLICY: DeviceProperty = DevicePropertyRemovalPolicy; break; case PNP_PROPERTY_ADDRESS: DeviceProperty = DevicePropertyAddress; break; case PNP_PROPERTY_ENUMERATOR_NAME: DeviceProperty = DevicePropertyEnumeratorName; break; case PNP_PROPERTY_INSTALL_STATE: DeviceProperty = DevicePropertyInstallState; break; #if (WINVER >= _WIN32_WINNT_WS03) case PNP_PROPERTY_LOCATION_PATHS: UNIMPLEMENTED; BufferSize = 0; Status = STATUS_NOT_IMPLEMENTED; break; #endif #if (WINVER >= _WIN32_WINNT_WIN7) case PNP_PROPERTY_CONTAINERID: DeviceProperty = DevicePropertyContainerID; break; #endif default: BufferSize = 0; Status = STATUS_INVALID_PARAMETER; break; } if (Status == STATUS_SUCCESS) { Status = IoGetDeviceProperty(DeviceObject, DeviceProperty, BufferSize, Buffer, &BufferSize); } } ObDereferenceObject(DeviceObject); if (NT_SUCCESS(Status)) { _SEH2_TRY { RtlCopyMemory(PropertyData->Buffer, Buffer, BufferSize); PropertyData->BufferSize = BufferSize; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } ExFreePool(Buffer); return Status; } static NTSTATUS IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData) { UNICODE_STRING RootDeviceName; PDEVICE_OBJECT DeviceObject = NULL; PDEVICE_NODE DeviceNode = NULL; PDEVICE_NODE RelatedDeviceNode; UNICODE_STRING TargetDeviceInstance; NTSTATUS Status = STATUS_SUCCESS; ULONG Relation = 0; ULONG MaximumLength = 0; DPRINT("IopGetRelatedDevice() called\n"); DPRINT("Device name: %wZ\n", &RelatedDeviceData->TargetDeviceInstance); Status = IopCaptureUnicodeString(&TargetDeviceInstance, &RelatedDeviceData->TargetDeviceInstance); if (!NT_SUCCESS(Status)) { return Status; } _SEH2_TRY { Relation = RelatedDeviceData->Relation; MaximumLength = RelatedDeviceData->RelatedDeviceInstanceLength; ProbeForWrite(RelatedDeviceData->RelatedDeviceInstance, MaximumLength, sizeof(WCHAR)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { if (TargetDeviceInstance.Buffer != NULL) { ExFreePool(TargetDeviceInstance.Buffer); } _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; RtlInitUnicodeString(&RootDeviceName, L"HTREE\\ROOT\\0"); if (RtlEqualUnicodeString(&TargetDeviceInstance, &RootDeviceName, TRUE)) { DeviceNode = IopRootDeviceNode; if (TargetDeviceInstance.Buffer != NULL) { ExFreePool(TargetDeviceInstance.Buffer); } } else { /* Get the device object */ DeviceObject = IopGetDeviceObjectFromDeviceInstance(&TargetDeviceInstance); if (TargetDeviceInstance.Buffer != NULL) { ExFreePool(TargetDeviceInstance.Buffer); } if (DeviceObject == NULL) return STATUS_NO_SUCH_DEVICE; DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode; } switch (Relation) { case PNP_GET_PARENT_DEVICE: RelatedDeviceNode = DeviceNode->Parent; break; case PNP_GET_CHILD_DEVICE: RelatedDeviceNode = DeviceNode->Child; break; case PNP_GET_SIBLING_DEVICE: RelatedDeviceNode = DeviceNode->Sibling; break; default: if (DeviceObject != NULL) { ObDereferenceObject(DeviceObject); } return STATUS_INVALID_PARAMETER; } if (RelatedDeviceNode == NULL) { if (DeviceObject) { ObDereferenceObject(DeviceObject); } return STATUS_NO_SUCH_DEVICE; } if (RelatedDeviceNode->InstancePath.Length > MaximumLength) { if (DeviceObject) { ObDereferenceObject(DeviceObject); } return STATUS_BUFFER_TOO_SMALL; } /* Copy related device instance name */ _SEH2_TRY { RtlCopyMemory(RelatedDeviceData->RelatedDeviceInstance, RelatedDeviceNode->InstancePath.Buffer, RelatedDeviceNode->InstancePath.Length); RelatedDeviceData->RelatedDeviceInstanceLength = RelatedDeviceNode->InstancePath.Length; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; if (DeviceObject != NULL) { ObDereferenceObject(DeviceObject); } DPRINT("IopGetRelatedDevice() done\n"); return Status; } static BOOLEAN PiIsDevNodeStarted( _In_ PDEVICE_NODE DeviceNode) { return (DeviceNode->State == DeviceNodeStartPending || DeviceNode->State == DeviceNodeStartCompletion || DeviceNode->State == DeviceNodeStartPostWork || DeviceNode->State == DeviceNodeStarted || DeviceNode->State == DeviceNodeQueryStopped || DeviceNode->State == DeviceNodeEnumeratePending || DeviceNode->State == DeviceNodeEnumerateCompletion || DeviceNode->State == DeviceNodeStopped || DeviceNode->State == DeviceNodeRestartCompletion); } static ULONG IopGetDeviceNodeStatus(PDEVICE_NODE DeviceNode) { ULONG Output = DN_NT_ENUMERATOR | DN_NT_DRIVER; if (DeviceNode->Parent == IopRootDeviceNode) Output |= DN_ROOT_ENUMERATED; // FIXME: review for deleted and removed states if (DeviceNode->State >= DeviceNodeDriversAdded) Output |= DN_DRIVER_LOADED; if (PiIsDevNodeStarted(DeviceNode)) Output |= DN_STARTED; if (DeviceNode->UserFlags & DNUF_WILL_BE_REMOVED) Output |= DN_WILL_BE_REMOVED; if (DeviceNode->Flags & DNF_HAS_PROBLEM) Output |= DN_HAS_PROBLEM; if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM) Output |= DN_PRIVATE_PROBLEM; if (DeviceNode->Flags & DNF_DRIVER_BLOCKED) Output |= DN_DRIVER_BLOCKED; if (DeviceNode->Flags & DNF_CHILD_WITH_INVALID_ID) Output |= DN_CHILD_WITH_INVALID_ID; if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM) Output |= DN_PRIVATE_PROBLEM; if (DeviceNode->Flags & DNF_LEGACY_DRIVER) Output |= DN_LEGACY_DRIVER; if (DeviceNode->UserFlags & DNUF_DONT_SHOW_IN_UI) Output |= DN_NO_SHOW_IN_DM; if (!(DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE)) Output |= DN_DISABLEABLE; return Output; } static NTSTATUS IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData) { PDEVICE_OBJECT DeviceObject; PDEVICE_NODE DeviceNode; ULONG Operation = 0; ULONG DeviceStatus = 0; ULONG DeviceProblem = 0; UNICODE_STRING DeviceInstance; NTSTATUS Status; DPRINT("IopDeviceStatus() called\n"); Status = IopCaptureUnicodeString(&DeviceInstance, &StatusData->DeviceInstance); if (!NT_SUCCESS(Status)) { return Status; } DPRINT("Device name: '%wZ'\n", &DeviceInstance); _SEH2_TRY { Operation = StatusData->Operation; if (Operation == PNP_SET_DEVICE_STATUS) { DeviceStatus = StatusData->DeviceStatus; DeviceProblem = StatusData->DeviceProblem; } } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; /* Get the device object */ DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } if (DeviceObject == NULL) { return STATUS_NO_SUCH_DEVICE; } DeviceNode = IopGetDeviceNode(DeviceObject); switch (Operation) { case PNP_GET_DEVICE_STATUS: DPRINT("Get status data\n"); DeviceStatus = IopGetDeviceNodeStatus(DeviceNode); DeviceProblem = DeviceNode->Problem; break; case PNP_SET_DEVICE_STATUS: DPRINT1("Set status data is NOT SUPPORTED\n"); break; case PNP_CLEAR_DEVICE_STATUS: DPRINT1("FIXME: Clear status data!\n"); break; } ObDereferenceObject(DeviceObject); if (Operation == PNP_GET_DEVICE_STATUS) { _SEH2_TRY { StatusData->DeviceStatus = DeviceStatus; StatusData->DeviceProblem = DeviceProblem; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; } return Status; } static NTSTATUS IopGetDeviceRelations(PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA RelationsData) { UNICODE_STRING DeviceInstance; PDEVICE_OBJECT DeviceObject = NULL; IO_STACK_LOCATION Stack; IO_STATUS_BLOCK IoStatusBlock; PDEVICE_RELATIONS DeviceRelations = NULL; PDEVICE_OBJECT ChildDeviceObject; PDEVICE_NODE ChildDeviceNode; ULONG i; ULONG Relations; ULONG BufferSize, RequiredSize; ULONG BufferLeft; PWCHAR Buffer, Ptr; NTSTATUS Status = STATUS_SUCCESS; DPRINT("IopGetDeviceRelations() called\n"); DPRINT("Device name: %wZ\n", &RelationsData->DeviceInstance); DPRINT("Relations: %lu\n", RelationsData->Relations); DPRINT("BufferSize: %lu\n", RelationsData->BufferSize); DPRINT("Buffer: %p\n", RelationsData->Buffer); _SEH2_TRY { Relations = RelationsData->Relations; BufferSize = RelationsData->BufferSize; Buffer = RelationsData->Buffer; ProbeForWrite(Buffer, BufferSize, sizeof(CHAR)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; Status = IopCaptureUnicodeString(&DeviceInstance, &RelationsData->DeviceInstance); if (!NT_SUCCESS(Status)) { DPRINT1("IopCaptureUnicodeString() failed (Status 0x%08lx)\n", Status); return Status; } /* Get the device object */ DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceObject == NULL) { DPRINT1("IopGetDeviceObjectFromDeviceInstance() returned NULL\n"); Status = STATUS_NO_SUCH_DEVICE; goto done; } switch (Relations) { case PNP_EJECT_RELATIONS: Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations; break; case PNP_REMOVAL_RELATIONS: Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations; break; case PNP_POWER_RELATIONS: Stack.Parameters.QueryDeviceRelations.Type = PowerRelations; break; case PNP_BUS_RELATIONS: Stack.Parameters.QueryDeviceRelations.Type = BusRelations; break; default: Status = STATUS_INVALID_PARAMETER; goto done; } Status = IopInitiatePnpIrp(DeviceObject, &IoStatusBlock, IRP_MN_QUERY_DEVICE_RELATIONS, &Stack); if (!NT_SUCCESS(Status)) { DPRINT1("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status); goto done; } DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information; DPRINT("Found %d device relations\n", DeviceRelations->Count); _SEH2_TRY { RequiredSize = 0; BufferLeft = BufferSize; Ptr = Buffer; for (i = 0; i < DeviceRelations->Count; i++) { ChildDeviceObject = DeviceRelations->Objects[i]; ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject); if (ChildDeviceNode) { DPRINT("Device instance: %wZ\n", &ChildDeviceNode->InstancePath); DPRINT("RequiredSize: %hu\n", ChildDeviceNode->InstancePath.Length + sizeof(WCHAR)); if (Ptr != NULL) { if (BufferLeft < ChildDeviceNode->InstancePath.Length + 2 * sizeof(WCHAR)) { Status = STATUS_BUFFER_TOO_SMALL; break; } RtlCopyMemory(Ptr, ChildDeviceNode->InstancePath.Buffer, ChildDeviceNode->InstancePath.Length); Ptr = (PWCHAR)((ULONG_PTR)Ptr + ChildDeviceNode->InstancePath.Length); *Ptr = UNICODE_NULL; Ptr = (PWCHAR)((ULONG_PTR)Ptr + sizeof(WCHAR)); BufferLeft -= (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR)); } RequiredSize += (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR)); } } if (Ptr != NULL && BufferLeft >= sizeof(WCHAR)) *Ptr = UNICODE_NULL; if (RequiredSize > 0) RequiredSize += sizeof(WCHAR); DPRINT("BufferSize: %lu RequiredSize: %lu\n", RelationsData->BufferSize, RequiredSize); RelationsData->BufferSize = RequiredSize; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; done: if (DeviceRelations != NULL) ExFreePool(DeviceRelations); if (DeviceObject != NULL) ObDereferenceObject(DeviceObject); if (DeviceInstance.Buffer != NULL) ExFreePool(DeviceInstance.Buffer); return Status; } static NTSTATUS IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData) { PDEVICE_OBJECT DeviceObject; PDEVICE_NODE DeviceNode; UNICODE_STRING DeviceInstance; NTSTATUS Status = STATUS_SUCCESS; DPRINT("IopGetDeviceDepth() called\n"); DPRINT("Device name: %wZ\n", &DepthData->DeviceInstance); Status = IopCaptureUnicodeString(&DeviceInstance, &DepthData->DeviceInstance); if (!NT_SUCCESS(Status)) { return Status; } /* Get the device object */ DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } if (DeviceObject == NULL) { return STATUS_NO_SUCH_DEVICE; } DeviceNode = IopGetDeviceNode(DeviceObject); _SEH2_TRY { DepthData->Depth = DeviceNode->Level; } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { Status = _SEH2_GetExceptionCode(); } _SEH2_END; ObDereferenceObject(DeviceObject); return Status; } static NTSTATUS PiControlSyncDeviceAction( _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceData, _In_ PLUGPLAY_CONTROL_CLASS ControlClass) { PDEVICE_OBJECT DeviceObject; NTSTATUS Status; UNICODE_STRING DeviceInstance; ASSERT(ControlClass == PlugPlayControlEnumerateDevice || ControlClass == PlugPlayControlStartDevice || ControlClass == PlugPlayControlResetDevice); Status = IopCaptureUnicodeString(&DeviceInstance, &DeviceData->DeviceInstance); if (!NT_SUCCESS(Status)) { return Status; } DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } if (DeviceObject == NULL) { return STATUS_NO_SUCH_DEVICE; } DEVICE_ACTION Action; switch (ControlClass) { case PlugPlayControlEnumerateDevice: Action = PiActionEnumDeviceTree; break; case PlugPlayControlStartDevice: Action = PiActionStartDevice; break; case PlugPlayControlResetDevice: Action = PiActionResetDevice; break; default: UNREACHABLE; break; } Status = PiPerformSyncDeviceAction(DeviceObject, Action); ObDereferenceObject(DeviceObject); return Status; } static NTSTATUS PiControlQueryRemoveDevice( _In_ PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA ControlData) { PDEVICE_OBJECT DeviceObject; NTSTATUS Status; UNICODE_STRING DeviceInstance; Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance); if (!NT_SUCCESS(Status)) { return Status; } DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance); if (DeviceInstance.Buffer != NULL) { ExFreePool(DeviceInstance.Buffer); } if (DeviceObject == NULL) { return STATUS_NO_SUCH_DEVICE; } UNIMPLEMENTED; Status = STATUS_NOT_IMPLEMENTED; ObDereferenceObject(DeviceObject); return Status; } /* PUBLIC FUNCTIONS **********************************************************/ /* * Plug and Play event structure used by NtGetPlugPlayEvent. * * EventGuid * Can be one of the following values: * GUID_HWPROFILE_QUERY_CHANGE * GUID_HWPROFILE_CHANGE_CANCELLED * GUID_HWPROFILE_CHANGE_COMPLETE * GUID_TARGET_DEVICE_QUERY_REMOVE * GUID_TARGET_DEVICE_REMOVE_CANCELLED * GUID_TARGET_DEVICE_REMOVE_COMPLETE * GUID_PNP_CUSTOM_NOTIFICATION * GUID_PNP_POWER_NOTIFICATION * GUID_DEVICE_* (see above) * * EventCategory * Type of the event that happened. * * Result * ? * * Flags * ? * * TotalSize * Size of the event block including the device IDs and other * per category specific fields. */ /* * NtGetPlugPlayEvent * * Returns one Plug & Play event from a global queue. * * Parameters * Reserved1 * Reserved2 * Always set to zero. * * Buffer * The buffer that will be filled with the event information on * successful return from the function. * * BufferSize * Size of the buffer pointed by the Buffer parameter. If the * buffer size is not large enough to hold the whole event * information, error STATUS_BUFFER_TOO_SMALL is returned and * the buffer remains untouched. * * Return Values * STATUS_PRIVILEGE_NOT_HELD * STATUS_BUFFER_TOO_SMALL * STATUS_SUCCESS * * Remarks * This function isn't multi-thread safe! * * @implemented */ NTSTATUS NTAPI NtGetPlugPlayEvent(IN ULONG Reserved1, IN ULONG Reserved2, OUT PPLUGPLAY_EVENT_BLOCK Buffer, IN ULONG BufferSize) { PPNP_EVENT_ENTRY Entry; NTSTATUS Status; DPRINT("NtGetPlugPlayEvent() called\n"); /* Function can only be called from user-mode */ if (KeGetPreviousMode() == KernelMode) { DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n"); return STATUS_ACCESS_DENIED; } /* Check for Tcb privilege */ if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) { DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n"); return STATUS_PRIVILEGE_NOT_HELD; } /* Wait for a PnP event */ DPRINT("Waiting for pnp notification event\n"); Status = KeWaitForSingleObject(&IopPnpNotifyEvent, UserRequest, UserMode, FALSE, NULL); if (!NT_SUCCESS(Status) || Status == STATUS_USER_APC) { DPRINT("KeWaitForSingleObject() failed (Status %lx)\n", Status); ASSERT(Status == STATUS_USER_APC); return Status; } /* Get entry from the tail of the queue */ Entry = CONTAINING_RECORD(IopPnpEventQueueHead.Blink, PNP_EVENT_ENTRY, ListEntry); /* Check the buffer size */ if (BufferSize < Entry->Event.TotalSize) { DPRINT1("Buffer is too small for the pnp-event\n"); return STATUS_BUFFER_TOO_SMALL; } /* Copy event data to the user buffer */ _SEH2_TRY { ProbeForWrite(Buffer, Entry->Event.TotalSize, sizeof(UCHAR)); RtlCopyMemory(Buffer, &Entry->Event, Entry->Event.TotalSize); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; DPRINT("NtGetPlugPlayEvent() done\n"); return STATUS_SUCCESS; } /* * NtPlugPlayControl * * A function for doing various Plug & Play operations from user mode. * * Parameters * PlugPlayControlClass * 0x00 Reenumerate device tree * * Buffer points to UNICODE_STRING decribing the instance * path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For * more information about instance paths see !devnode command * in kernel debugger or look at "Inside Windows 2000" book, * chapter "Driver Loading, Initialization, and Installation". * * 0x01 Register new device * 0x02 Deregister device * 0x03 Initialize device * 0x04 Start device * 0x06 Query and remove device * 0x07 User response * * Called after processing the message from NtGetPlugPlayEvent. * * 0x08 Generate legacy device * 0x09 Get interface device list * 0x0A Get property data * 0x0B Device class association (Registration) * 0x0C Get related device * 0x0D Get device interface alias * 0x0E Get/set/clear device status * 0x0F Get device depth * 0x10 Query device relations * 0x11 Query target device relation * 0x12 Query conflict list * 0x13 Retrieve dock data * 0x14 Reset device * 0x15 Halt device * 0x16 Get blocked driver data * * Buffer * The buffer contains information that is specific to each control * code. The buffer is read-only. * * BufferSize * Size of the buffer pointed by the Buffer parameter. If the * buffer size specifies incorrect value for specified control * code, error ??? is returned. * * Return Values * STATUS_PRIVILEGE_NOT_HELD * STATUS_SUCCESS * ... * * @unimplemented */ NTSTATUS NTAPI NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass, IN OUT PVOID Buffer, IN ULONG BufferLength) { DPRINT("NtPlugPlayControl(%d %p %lu) called\n", PlugPlayControlClass, Buffer, BufferLength); /* Function can only be called from user-mode */ if (KeGetPreviousMode() == KernelMode) { DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n"); return STATUS_ACCESS_DENIED; } /* Check for Tcb privilege */ if (!SeSinglePrivilegeCheck(SeTcbPrivilege, UserMode)) { DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n"); return STATUS_PRIVILEGE_NOT_HELD; } /* Probe the buffer */ _SEH2_TRY { ProbeForWrite(Buffer, BufferLength, sizeof(ULONG)); } _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { _SEH2_YIELD(return _SEH2_GetExceptionCode()); } _SEH2_END; switch (PlugPlayControlClass) { case PlugPlayControlEnumerateDevice: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_ENUMERATE_DEVICE_DATA)) return STATUS_INVALID_PARAMETER; // the Flags field is not used anyway return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer, PlugPlayControlClass); // case PlugPlayControlRegisterNewDevice: // case PlugPlayControlDeregisterDevice: case PlugPlayControlInitializeDevice: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)) return STATUS_INVALID_PARAMETER; return IopInitializeDevice((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer); case PlugPlayControlStartDevice: case PlugPlayControlResetDevice: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)) return STATUS_INVALID_PARAMETER; return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer, PlugPlayControlClass); // case PlugPlayControlUnlockDevice: case PlugPlayControlQueryAndRemoveDevice: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_QUERY_REMOVE_DATA)) return STATUS_INVALID_PARAMETER; return PiControlQueryRemoveDevice((PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA)Buffer); case PlugPlayControlUserResponse: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA)) return STATUS_INVALID_PARAMETER; return IopRemovePlugPlayEvent((PPLUGPLAY_CONTROL_USER_RESPONSE_DATA)Buffer); // case PlugPlayControlGenerateLegacyDevice: case PlugPlayControlGetInterfaceDeviceList: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA)) return STATUS_INVALID_PARAMETER; return IopGetInterfaceDeviceList((PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA)Buffer); case PlugPlayControlProperty: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA)) return STATUS_INVALID_PARAMETER; return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer); // case PlugPlayControlDeviceClassAssociation: case PlugPlayControlGetRelatedDevice: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA)) return STATUS_INVALID_PARAMETER; return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer); // case PlugPlayControlGetInterfaceDeviceAlias: case PlugPlayControlDeviceStatus: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA)) return STATUS_INVALID_PARAMETER; return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer); case PlugPlayControlGetDeviceDepth: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA)) return STATUS_INVALID_PARAMETER; return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer); case PlugPlayControlQueryDeviceRelations: if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA)) return STATUS_INVALID_PARAMETER; return IopGetDeviceRelations((PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA)Buffer); // case PlugPlayControlTargetDeviceRelation: // case PlugPlayControlQueryConflictList: // case PlugPlayControlRetrieveDock: // case PlugPlayControlHaltDevice: // case PlugPlayControlGetBlockedDriverList: default: return STATUS_NOT_IMPLEMENTED; } return STATUS_NOT_IMPLEMENTED; }