2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/io/plugplay.c
5 * PURPOSE: Plug-and-play interface routines
7 * PROGRAMMERS: Eric Kohl <eric.kohl@t-online.de>
10 /* INCLUDES *****************************************************************/
15 #include <internal/debug.h>
18 typedef struct _PNP_EVENT_ENTRY
21 PLUGPLAY_EVENT_BLOCK Event
;
22 } PNP_EVENT_ENTRY
, *PPNP_EVENT_ENTRY
;
25 /* GLOBALS *******************************************************************/
27 static LIST_ENTRY IopPnpEventQueueHead
;
28 static KEVENT IopPnpNotifyEvent
;
30 /* FUNCTIONS *****************************************************************/
32 NTSTATUS INIT_FUNCTION
33 IopInitPlugPlayEvents(VOID
)
35 InitializeListHead(&IopPnpEventQueueHead
);
37 KeInitializeEvent(&IopPnpNotifyEvent
,
41 return STATUS_SUCCESS
;
46 IopQueueTargetDeviceEvent(const GUID
*Guid
,
47 PUNICODE_STRING DeviceIds
)
49 PPNP_EVENT_ENTRY EventEntry
;
53 FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK
, TargetDevice
.DeviceIds
) +
54 DeviceIds
->MaximumLength
;
56 EventEntry
= ExAllocatePool(NonPagedPool
,
57 TotalSize
+ FIELD_OFFSET(PNP_EVENT_ENTRY
, Event
));
58 if (EventEntry
== NULL
)
59 return STATUS_INSUFFICIENT_RESOURCES
;
61 memcpy(&EventEntry
->Event
.EventGuid
,
64 EventEntry
->Event
.EventCategory
= TargetDeviceChangeEvent
;
65 EventEntry
->Event
.TotalSize
= TotalSize
;
67 memcpy(&EventEntry
->Event
.TargetDevice
.DeviceIds
,
69 DeviceIds
->MaximumLength
);
71 InsertHeadList(&IopPnpEventQueueHead
,
72 &EventEntry
->ListEntry
);
73 KeSetEvent(&IopPnpNotifyEvent
,
77 return STATUS_SUCCESS
;
82 * Remove the current PnP event from the tail of the event queue
83 * and signal IopPnpNotifyEvent if there is yet another event in the queue.
86 IopRemovePlugPlayEvent(VOID
)
88 /* Remove a pnp event entry from the tail of the queue */
89 if (!IsListEmpty(&IopPnpEventQueueHead
))
91 ExFreePool(RemoveTailList(&IopPnpEventQueueHead
));
94 /* Signal the next pnp event in the queue */
95 if (!IsListEmpty(&IopPnpEventQueueHead
))
97 KeSetEvent(&IopPnpNotifyEvent
,
102 return STATUS_SUCCESS
;
107 * Plug and Play event structure used by NtGetPlugPlayEvent.
110 * Can be one of the following values:
111 * GUID_HWPROFILE_QUERY_CHANGE
112 * GUID_HWPROFILE_CHANGE_CANCELLED
113 * GUID_HWPROFILE_CHANGE_COMPLETE
114 * GUID_TARGET_DEVICE_QUERY_REMOVE
115 * GUID_TARGET_DEVICE_REMOVE_CANCELLED
116 * GUID_TARGET_DEVICE_REMOVE_COMPLETE
117 * GUID_PNP_CUSTOM_NOTIFICATION
118 * GUID_PNP_POWER_NOTIFICATION
119 * GUID_DEVICE_* (see above)
122 * Type of the event that happened.
131 * Size of the event block including the device IDs and other
132 * per category specific fields.
137 * Returns one Plug & Play event from a global queue.
142 * Always set to zero.
145 * The buffer that will be filled with the event information on
146 * successful return from the function.
149 * Size of the buffer pointed by the Buffer parameter. If the
150 * buffer size is not large enough to hold the whole event
151 * information, error STATUS_BUFFER_TOO_SMALL is returned and
152 * the buffer remains untouched.
155 * STATUS_PRIVILEGE_NOT_HELD
156 * STATUS_BUFFER_TOO_SMALL
160 * This function isn't multi-thread safe!
165 NtGetPlugPlayEvent(IN ULONG Reserved1
,
167 OUT PPLUGPLAY_EVENT_BLOCK Buffer
,
170 PPNP_EVENT_ENTRY Entry
;
173 DPRINT("NtGetPlugPlayEvent() called\n");
175 /* Function can only be called from user-mode */
176 if (KeGetPreviousMode() == KernelMode
)
178 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
179 return STATUS_ACCESS_DENIED
;
182 /* Check for Tcb privilege */
183 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
,
186 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
187 return STATUS_PRIVILEGE_NOT_HELD
;
190 /* Wait for a PnP event */
191 DPRINT("Waiting for pnp notification event\n");
192 Status
= KeWaitForSingleObject(&IopPnpNotifyEvent
,
197 if (!NT_SUCCESS(Status
))
199 DPRINT1("KeWaitForSingleObject() failed (Status %lx)\n", Status
);
203 /* Get entry from the tail of the queue */
204 Entry
= CONTAINING_RECORD(IopPnpEventQueueHead
.Blink
,
208 /* Check the buffer size */
209 if (BufferSize
< Entry
->Event
.TotalSize
)
211 DPRINT1("Buffer is too small for the pnp-event\n");
212 return STATUS_BUFFER_TOO_SMALL
;
215 /* Copy event data to the user buffer */
218 Entry
->Event
.TotalSize
);
220 DPRINT("NtGetPlugPlayEvent() done\n");
222 return STATUS_SUCCESS
;
226 static PDEVICE_OBJECT
227 IopTraverseDeviceNode(PDEVICE_NODE Node
, PUNICODE_STRING DeviceInstance
)
229 PDEVICE_OBJECT DeviceObject
;
230 PDEVICE_NODE ChildNode
;
232 if (RtlEqualUnicodeString(&Node
->InstancePath
,
233 DeviceInstance
, TRUE
))
235 ObReferenceObject(Node
->PhysicalDeviceObject
);
236 return Node
->PhysicalDeviceObject
;
239 /* Traversal of all children nodes */
240 for (ChildNode
= Node
->Child
;
242 ChildNode
= ChildNode
->NextSibling
)
244 DeviceObject
= IopTraverseDeviceNode(ChildNode
, DeviceInstance
);
245 if (DeviceObject
!= NULL
)
255 static PDEVICE_OBJECT
256 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance
)
259 OBJECT_ATTRIBUTES ObjectAttributes
;
260 UNICODE_STRING KeyName
, ValueName
;
261 LPWSTR KeyNameBuffer
;
262 HANDLE InstanceKeyHandle
;
263 HANDLE ControlKeyHandle
;
265 PKEY_VALUE_PARTIAL_INFORMATION ValueInformation
;
266 ULONG ValueInformationLength
;
267 PDEVICE_OBJECT DeviceObject
= NULL
;
269 DPRINT("IopGetDeviceObjectFromDeviceInstance(%wZ) called\n", DeviceInstance
);
271 KeyNameBuffer
= ExAllocatePool(PagedPool
,
272 (49 * sizeof(WCHAR
)) + DeviceInstance
->Length
);
273 if (KeyNameBuffer
== NULL
)
275 DPRINT1("Failed to allocate key name buffer!\n");
279 wcscpy(KeyNameBuffer
, L
"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
280 wcscat(KeyNameBuffer
, DeviceInstance
->Buffer
);
282 RtlInitUnicodeString(&KeyName
,
284 InitializeObjectAttributes(&ObjectAttributes
,
286 OBJ_CASE_INSENSITIVE
,
290 Status
= ZwOpenKey(&InstanceKeyHandle
,
293 ExFreePool(KeyNameBuffer
);
294 if (!NT_SUCCESS(Status
))
296 DPRINT1("Failed to open the instance key (Status %lx)\n", Status
);
300 /* Open the 'Control' subkey */
301 RtlInitUnicodeString(&KeyName
,
303 InitializeObjectAttributes(&ObjectAttributes
,
305 OBJ_CASE_INSENSITIVE
,
309 Status
= ZwOpenKey(&ControlKeyHandle
,
312 ZwClose(InstanceKeyHandle
);
313 if (!NT_SUCCESS(Status
))
315 DPRINT1("Failed to open the 'Control' key (Status %lx)\n", Status
);
319 /* Query the 'DeviceReference' value */
320 RtlInitUnicodeString(&ValueName
,
322 ValueInformationLength
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
,
323 Data
[0]) + sizeof(ULONG
);
324 ValueInformation
= ExAllocatePool(PagedPool
, ValueInformationLength
);
325 if (ValueInformation
== NULL
)
327 DPRINT1("Failed to allocate the name information buffer!\n");
328 ZwClose(ControlKeyHandle
);
332 Status
= ZwQueryValueKey(ControlKeyHandle
,
334 KeyValuePartialInformation
,
336 ValueInformationLength
,
337 &ValueInformationLength
);
338 ZwClose(ControlKeyHandle
);
339 if (!NT_SUCCESS(Status
))
341 DPRINT1("Failed to query the 'DeviceReference' value (Status %lx)\n", Status
);
345 /* Check the device object */
346 RtlCopyMemory(&DeviceObject
,
347 ValueInformation
->Data
,
348 sizeof(PDEVICE_OBJECT
));
350 DPRINT("DeviceObject: %p\n", DeviceObject
);
352 if (DeviceObject
->Type
!= IO_TYPE_DEVICE
||
353 DeviceObject
->DeviceObjectExtension
== NULL
||
354 DeviceObject
->DeviceObjectExtension
->DeviceNode
== NULL
||
355 !RtlEqualUnicodeString(&DeviceObject
->DeviceObjectExtension
->DeviceNode
->InstancePath
,
356 DeviceInstance
, TRUE
))
358 DPRINT1("Invalid object type!\n");
362 DPRINT("Instance path: %wZ\n", &DeviceObject
->DeviceObjectExtension
->DeviceNode
->InstancePath
);
364 ObReferenceObject(DeviceObject
);
366 DPRINT("IopGetDeviceObjectFromDeviceInstance() done\n");
371 if (IopRootDeviceNode
== NULL
)
374 if (DeviceInstance
== NULL
||
375 DeviceInstance
->Length
== 0
378 if (IopRootDeviceNode
->PhysicalDeviceObject
)
380 ObReferenceObject(IopRootDeviceNode
->PhysicalDeviceObject
);
381 return IopRootDeviceNode
->PhysicalDeviceObject
;
387 return IopTraverseDeviceNode(IopRootDeviceNode
, DeviceInstance
);
394 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData
)
396 PDEVICE_OBJECT DeviceObject
= NULL
;
399 DPRINT("IopGetDeviceProperty() called\n");
400 DPRINT("Device name: %wZ\n", &PropertyData
->DeviceInstance
);
402 /* Get the device object */
403 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&PropertyData
->DeviceInstance
);
404 if (DeviceObject
== NULL
)
405 return STATUS_NO_SUCH_DEVICE
;
407 Status
= IoGetDeviceProperty(DeviceObject
,
408 PropertyData
->Property
,
409 PropertyData
->BufferSize
,
410 PropertyData
->Buffer
,
411 &PropertyData
->BufferSize
);
413 ObDereferenceObject(DeviceObject
);
420 IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData
)
422 UNICODE_STRING RootDeviceName
;
423 PDEVICE_OBJECT DeviceObject
= NULL
;
424 PDEVICE_NODE DeviceNode
= NULL
;
425 PDEVICE_NODE RelatedDeviceNode
;
427 DPRINT("IopGetRelatedDevice() called\n");
428 DPRINT("Device name: %wZ\n", &RelatedDeviceData
->TargetDeviceInstance
);
430 RtlInitUnicodeString(&RootDeviceName
,
432 if (RtlEqualUnicodeString(&RelatedDeviceData
->TargetDeviceInstance
,
436 DeviceNode
= IopRootDeviceNode
;
440 /* Get the device object */
441 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&RelatedDeviceData
->TargetDeviceInstance
);
442 if (DeviceObject
== NULL
)
443 return STATUS_NO_SUCH_DEVICE
;
445 DeviceNode
= ((PEXTENDED_DEVOBJ_EXTENSION
)DeviceObject
->DeviceObjectExtension
)->DeviceNode
;
448 switch (RelatedDeviceData
->Relation
)
450 case PNP_GET_PARENT_DEVICE
:
451 RelatedDeviceNode
= DeviceNode
->Parent
;
454 case PNP_GET_CHILD_DEVICE
:
455 RelatedDeviceNode
= DeviceNode
->Child
;
458 case PNP_GET_SIBLING_DEVICE
:
459 RelatedDeviceNode
= DeviceNode
->NextSibling
;
463 if (DeviceObject
!= NULL
)
465 ObDereferenceObject(DeviceObject
);
468 return STATUS_INVALID_PARAMETER
;
471 if (RelatedDeviceNode
== NULL
)
475 ObDereferenceObject(DeviceObject
);
478 return STATUS_NO_SUCH_DEVICE
;
481 if (RelatedDeviceNode
->InstancePath
.Length
>
482 RelatedDeviceData
->RelatedDeviceInstance
.MaximumLength
)
486 ObDereferenceObject(DeviceObject
);
489 return STATUS_BUFFER_TOO_SMALL
;
492 /* Copy related device instance name */
493 RtlCopyMemory(RelatedDeviceData
->RelatedDeviceInstance
.Buffer
,
494 RelatedDeviceNode
->InstancePath
.Buffer
,
495 RelatedDeviceNode
->InstancePath
.Length
);
496 RelatedDeviceData
->RelatedDeviceInstance
.Length
=
497 RelatedDeviceNode
->InstancePath
.Length
;
499 if (DeviceObject
!= NULL
)
501 ObDereferenceObject(DeviceObject
);
504 DPRINT("IopGetRelatedDevice() done\n");
506 return STATUS_SUCCESS
;
511 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData
)
513 PDEVICE_OBJECT DeviceObject
;
514 PDEVICE_NODE DeviceNode
;
516 DPRINT("IopDeviceStatus() called\n");
517 DPRINT("Device name: %wZ\n", &StatusData
->DeviceInstance
);
519 /* Get the device object */
520 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&StatusData
->DeviceInstance
);
521 if (DeviceObject
== NULL
)
522 return STATUS_NO_SUCH_DEVICE
;
524 DeviceNode
= ((PEXTENDED_DEVOBJ_EXTENSION
)DeviceObject
->DeviceObjectExtension
)->DeviceNode
;
526 switch (StatusData
->Operation
)
528 case PNP_GET_DEVICE_STATUS
:
529 DPRINT("Get status data\n");
530 StatusData
->DeviceStatus
= DeviceNode
->Flags
;
531 StatusData
->DeviceProblem
= DeviceNode
->Problem
;
534 case PNP_SET_DEVICE_STATUS
:
535 DPRINT("Set status data\n");
536 DeviceNode
->Flags
= StatusData
->DeviceStatus
;
537 DeviceNode
->Problem
= StatusData
->DeviceProblem
;
540 case PNP_CLEAR_DEVICE_STATUS
:
541 DPRINT1("FIXME: Clear status data!\n");
545 ObDereferenceObject(DeviceObject
);
547 return STATUS_SUCCESS
;
552 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData
)
554 PDEVICE_OBJECT DeviceObject
;
555 PDEVICE_NODE DeviceNode
;
557 DPRINT("IopGetDeviceDepth() called\n");
558 DPRINT("Device name: %wZ\n", &DepthData
->DeviceInstance
);
560 /* Get the device object */
561 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&DepthData
->DeviceInstance
);
562 if (DeviceObject
== NULL
)
563 return STATUS_NO_SUCH_DEVICE
;
565 DeviceNode
= IopGetDeviceNode(DeviceObject
);
567 DepthData
->Depth
= DeviceNode
->Level
;
569 ObDereferenceObject(DeviceObject
);
571 return STATUS_SUCCESS
;
576 IopResetDevice(PPLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData
)
578 PDEVICE_OBJECT DeviceObject
;
579 PDEVICE_NODE DeviceNode
;
582 DPRINT("IopResetDevice() called\n");
583 DPRINT("Device name: %wZ\n", &ResetDeviceData
->DeviceInstance
);
585 /* Get the device object */
586 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&ResetDeviceData
->DeviceInstance
);
587 if (DeviceObject
== NULL
)
588 return STATUS_NO_SUCH_DEVICE
;
590 DeviceNode
= IopGetDeviceNode(DeviceObject
);
592 /* FIXME: we should stop the device, before starting it again */
594 /* Start the device */
595 IopDeviceNodeClearFlag(DeviceNode
, DNF_DISABLED
);
596 Status
= IopActionConfigureChildServices(DeviceNode
, DeviceNode
->Parent
);
598 if (NT_SUCCESS(Status
))
599 Status
= IopActionInitChildServices(DeviceNode
, DeviceNode
->Parent
, FALSE
);
601 ObDereferenceObject(DeviceObject
);
610 * A function for doing various Plug & Play operations from user mode.
613 * PlugPlayControlClass
614 * 0x00 Reenumerate device tree
616 * Buffer points to UNICODE_STRING decribing the instance
617 * path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For
618 * more information about instance paths see !devnode command
619 * in kernel debugger or look at "Inside Windows 2000" book,
620 * chapter "Driver Loading, Initialization, and Installation".
622 * 0x01 Register new device
623 * 0x02 Deregister device
624 * 0x03 Initialize device
626 * 0x06 Query and remove device
629 * Called after processing the message from NtGetPlugPlayEvent.
631 * 0x08 Generate legacy device
632 * 0x09 Get interface device list
633 * 0x0A Get property data
634 * 0x0B Device class association (Registration)
635 * 0x0C Get related device
636 * 0x0D Get device interface alias
637 * 0x0E Get/set/clear device status
638 * 0x0F Get device depth
639 * 0x10 Query device relations
640 * 0x11 Query target device relation
641 * 0x12 Query conflict list
642 * 0x13 Retrieve dock data
645 * 0x16 Get blocked driver data
648 * The buffer contains information that is specific to each control
649 * code. The buffer is read-only.
652 * Size of the buffer pointed by the Buffer parameter. If the
653 * buffer size specifies incorrect value for specified control
654 * code, error ??? is returned.
657 * STATUS_PRIVILEGE_NOT_HELD
664 NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass
,
666 IN ULONG BufferLength
)
668 NTSTATUS Status
= STATUS_SUCCESS
;
670 DPRINT("NtPlugPlayControl(%lu %p %lu) called\n",
671 PlugPlayControlClass
, Buffer
, BufferLength
);
673 /* Function can only be called from user-mode */
674 if (KeGetPreviousMode() == KernelMode
)
676 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
677 return STATUS_ACCESS_DENIED
;
680 /* Check for Tcb privilege */
681 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
,
684 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
685 return STATUS_PRIVILEGE_NOT_HELD
;
688 /* Probe the buffer */
691 ProbeForWrite(Buffer
,
697 Status
= _SEH_GetExceptionCode();
701 if (!NT_SUCCESS(Status
))
706 switch (PlugPlayControlClass
)
708 case PlugPlayControlUserResponse
:
709 if (Buffer
|| BufferLength
!= 0)
710 return STATUS_INVALID_PARAMETER
;
711 return IopRemovePlugPlayEvent();
713 case PlugPlayControlProperty
:
714 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA
))
715 return STATUS_INVALID_PARAMETER
;
716 return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA
)Buffer
);
718 case PlugPlayControlGetRelatedDevice
:
719 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA
))
720 return STATUS_INVALID_PARAMETER
;
721 return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA
)Buffer
);
723 case PlugPlayControlDeviceStatus
:
724 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_STATUS_DATA
))
725 return STATUS_INVALID_PARAMETER
;
726 return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA
)Buffer
);
728 case PlugPlayControlGetDeviceDepth
:
729 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_DEPTH_DATA
))
730 return STATUS_INVALID_PARAMETER
;
731 return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA
)Buffer
);
733 case PlugPlayControlResetDevice
:
734 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA
))
735 return STATUS_INVALID_PARAMETER
;
736 return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA
)Buffer
);
739 return STATUS_NOT_IMPLEMENTED
;
742 return STATUS_NOT_IMPLEMENTED
;