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() != UserMode
)
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
))
234 return Node
->PhysicalDeviceObject
;
236 /* Traversal of all children nodes */
237 for (ChildNode
= Node
->Child
;
239 ChildNode
= ChildNode
->NextSibling
)
241 DeviceObject
= IopTraverseDeviceNode(ChildNode
, DeviceInstance
);
242 if (DeviceObject
!= NULL
)
252 static PDEVICE_OBJECT
253 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance
)
256 OBJECT_ATTRIBUTES ObjectAttributes
;
257 UNICODE_STRING KeyName
, ValueName
;
258 LPWSTR KeyNameBuffer
;
259 HANDLE InstanceKeyHandle
;
260 HANDLE ControlKeyHandle
;
262 PKEY_VALUE_PARTIAL_INFORMATION ValueInformation
;
263 ULONG ValueInformationLength
;
264 PDEVICE_OBJECT DeviceObject
= NULL
;
266 DPRINT("IopGetDeviceObjectFromDeviceInstance(%wZ) called\n", DeviceInstance
);
268 KeyNameBuffer
= ExAllocatePool(PagedPool
,
269 (49 * sizeof(WCHAR
)) + DeviceInstance
->Length
);
270 if (KeyNameBuffer
== NULL
)
272 DPRINT1("Failed to allocate key name buffer!\n");
276 wcscpy(KeyNameBuffer
, L
"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
277 wcscat(KeyNameBuffer
, DeviceInstance
->Buffer
);
279 RtlInitUnicodeString(&KeyName
,
281 InitializeObjectAttributes(&ObjectAttributes
,
283 OBJ_CASE_INSENSITIVE
,
287 Status
= ZwOpenKey(&InstanceKeyHandle
,
290 ExFreePool(KeyNameBuffer
);
291 if (!NT_SUCCESS(Status
))
293 DPRINT1("Failed to open the instance key (Status %lx)\n", Status
);
297 /* Open the 'Control' subkey */
298 RtlInitUnicodeString(&KeyName
,
300 InitializeObjectAttributes(&ObjectAttributes
,
302 OBJ_CASE_INSENSITIVE
,
306 Status
= ZwOpenKey(&ControlKeyHandle
,
309 ZwClose(InstanceKeyHandle
);
310 if (!NT_SUCCESS(Status
))
312 DPRINT1("Failed to open the 'Control' key (Status %lx)\n", Status
);
316 /* Query the 'DeviceReference' value */
317 RtlInitUnicodeString(&ValueName
,
319 ValueInformationLength
= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION
,
320 Data
[0]) + sizeof(ULONG
);
321 ValueInformation
= ExAllocatePool(PagedPool
, ValueInformationLength
);
322 if (ValueInformation
== NULL
)
324 DPRINT1("Failed to allocate the name information buffer!\n");
325 ZwClose(ControlKeyHandle
);
329 Status
= ZwQueryValueKey(ControlKeyHandle
,
331 KeyValuePartialInformation
,
333 ValueInformationLength
,
334 &ValueInformationLength
);
335 ZwClose(ControlKeyHandle
);
336 if (!NT_SUCCESS(Status
))
338 DPRINT1("Failed to query the 'DeviceReference' value (Status %lx)\n", Status
);
342 /* Check the device object */
343 RtlCopyMemory(&DeviceObject
,
344 ValueInformation
->Data
,
345 sizeof(PDEVICE_OBJECT
));
347 DPRINT("DeviceObject: %p\n", DeviceObject
);
349 if (DeviceObject
->Type
!= IO_TYPE_DEVICE
||
350 DeviceObject
->DeviceObjectExtension
== NULL
||
351 DeviceObject
->DeviceObjectExtension
->DeviceNode
== NULL
||
352 !RtlEqualUnicodeString(&DeviceObject
->DeviceObjectExtension
->DeviceNode
->InstancePath
,
353 DeviceInstance
, TRUE
))
355 DPRINT1("Invalid object type!\n");
359 DPRINT("Instance path: %wZ\n", &DeviceObject
->DeviceObjectExtension
->DeviceNode
->InstancePath
);
361 ObReferenceObject(DeviceObject
);
363 DPRINT("IopGetDeviceObjectFromDeviceInstance() done\n");
368 if (IopRootDeviceNode
== NULL
)
371 if (DeviceInstance
== NULL
||
372 DeviceInstance
->Length
== 0
375 if (IopRootDeviceNode
->PhysicalDeviceObject
)
377 ObReferenceObject(IopRootDeviceNode
->PhysicalDeviceObject
);
378 return IopRootDeviceNode
->PhysicalDeviceObject
;
384 return IopTraverseDeviceNode(IopRootDeviceNode
, DeviceInstance
);
391 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData
)
393 PDEVICE_OBJECT DeviceObject
= NULL
;
396 DPRINT("IopGetDeviceProperty() called\n");
397 DPRINT("Device name: %wZ\n", &PropertyData
->DeviceInstance
);
399 /* Get the device object */
400 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&PropertyData
->DeviceInstance
);
401 if (DeviceObject
== NULL
)
402 return STATUS_NO_SUCH_DEVICE
;
404 Status
= IoGetDeviceProperty(DeviceObject
,
405 PropertyData
->Property
,
406 PropertyData
->BufferSize
,
407 PropertyData
->Buffer
,
408 &PropertyData
->BufferSize
);
410 ObDereferenceObject(DeviceObject
);
417 IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData
)
419 UNICODE_STRING RootDeviceName
;
420 PDEVICE_OBJECT DeviceObject
= NULL
;
421 PDEVICE_NODE DeviceNode
= NULL
;
422 PDEVICE_NODE RelatedDeviceNode
;
424 DPRINT("IopGetRelatedDevice() called\n");
425 DPRINT("Device name: %wZ\n", &RelatedDeviceData
->TargetDeviceInstance
);
427 RtlInitUnicodeString(&RootDeviceName
,
429 if (RtlEqualUnicodeString(&RelatedDeviceData
->TargetDeviceInstance
,
433 DeviceNode
= IopRootDeviceNode
;
437 /* Get the device object */
438 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&RelatedDeviceData
->TargetDeviceInstance
);
439 if (DeviceObject
== NULL
)
440 return STATUS_NO_SUCH_DEVICE
;
442 DeviceNode
= DeviceObject
->DeviceObjectExtension
->DeviceNode
;
445 switch (RelatedDeviceData
->Relation
)
447 case PNP_GET_PARENT_DEVICE
:
448 RelatedDeviceNode
= DeviceNode
->Parent
;
451 case PNP_GET_CHILD_DEVICE
:
452 RelatedDeviceNode
= DeviceNode
->Child
;
455 case PNP_GET_SIBLING_DEVICE
:
456 RelatedDeviceNode
= DeviceNode
->NextSibling
;
460 if (DeviceObject
!= NULL
)
462 ObDereferenceObject(DeviceObject
);
465 return STATUS_INVALID_PARAMETER
;
468 if (RelatedDeviceNode
== NULL
)
472 ObDereferenceObject(DeviceObject
);
475 return STATUS_NO_SUCH_DEVICE
;
478 if (RelatedDeviceNode
->InstancePath
.Length
>
479 RelatedDeviceData
->RelatedDeviceInstance
.MaximumLength
)
483 ObDereferenceObject(DeviceObject
);
486 return STATUS_BUFFER_TOO_SMALL
;
489 /* Copy related device instance name */
490 RtlCopyMemory(RelatedDeviceData
->RelatedDeviceInstance
.Buffer
,
491 RelatedDeviceNode
->InstancePath
.Buffer
,
492 RelatedDeviceNode
->InstancePath
.Length
);
493 RelatedDeviceData
->RelatedDeviceInstance
.Length
=
494 RelatedDeviceNode
->InstancePath
.Length
;
496 if (DeviceObject
!= NULL
)
498 ObDereferenceObject(DeviceObject
);
501 DPRINT("IopGetRelatedDevice() done\n");
503 return STATUS_SUCCESS
;
508 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData
)
510 PDEVICE_OBJECT DeviceObject
;
511 PDEVICE_NODE DeviceNode
;
513 DPRINT("IopDeviceStatus() called\n");
514 DPRINT("Device name: %wZ\n", &StatusData
->DeviceInstance
);
516 /* Get the device object */
517 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&StatusData
->DeviceInstance
);
518 if (DeviceObject
== NULL
)
519 return STATUS_NO_SUCH_DEVICE
;
521 DeviceNode
= DeviceObject
->DeviceObjectExtension
->DeviceNode
;
523 switch (StatusData
->Operation
)
525 case PNP_GET_DEVICE_STATUS
:
526 DPRINT("Get status data\n");
527 StatusData
->DeviceStatus
= DeviceNode
->Flags
;
528 StatusData
->DeviceProblem
= DeviceNode
->Problem
;
531 case PNP_SET_DEVICE_STATUS
:
532 DPRINT("Set status data\n");
533 DeviceNode
->Flags
= StatusData
->DeviceStatus
;
534 DeviceNode
->Problem
= StatusData
->DeviceProblem
;
537 case PNP_CLEAR_DEVICE_STATUS
:
538 DPRINT1("FIXME: Clear status data!\n");
542 ObDereferenceObject(DeviceObject
);
544 return STATUS_SUCCESS
;
549 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData
)
551 PDEVICE_OBJECT DeviceObject
;
552 PDEVICE_NODE DeviceNode
;
554 DPRINT("IopGetDeviceDepth() called\n");
555 DPRINT("Device name: %wZ\n", &DepthData
->DeviceInstance
);
557 /* Get the device object */
558 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&DepthData
->DeviceInstance
);
559 if (DeviceObject
== NULL
)
560 return STATUS_NO_SUCH_DEVICE
;
562 DeviceNode
= DeviceObject
->DeviceObjectExtension
->DeviceNode
;
564 DepthData
->Depth
= DeviceNode
->Level
;
566 ObDereferenceObject(DeviceObject
);
568 return STATUS_SUCCESS
;
575 * A function for doing various Plug & Play operations from user mode.
578 * PlugPlayControlClass
579 * 0x00 Reenumerate device tree
581 * Buffer points to UNICODE_STRING decribing the instance
582 * path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For
583 * more information about instance paths see !devnode command
584 * in kernel debugger or look at "Inside Windows 2000" book,
585 * chapter "Driver Loading, Initialization, and Installation".
587 * 0x01 Register new device
588 * 0x02 Deregister device
589 * 0x03 Initialize device
591 * 0x06 Query and remove device
594 * Called after processing the message from NtGetPlugPlayEvent.
596 * 0x08 Generate legacy device
597 * 0x09 Get interface device list
598 * 0x0A Get property data
599 * 0x0B Device class association (Registration)
600 * 0x0C Get related device
601 * 0x0D Get device interface alias
602 * 0x0E Get/set/clear device status
603 * 0x0F Get device depth
604 * 0x10 Query device relations
605 * 0x11 Query target device relation
606 * 0x12 Query conflict list
607 * 0x13 Retrieve dock data
610 * 0x16 Get blocked driver data
613 * The buffer contains information that is specific to each control
614 * code. The buffer is read-only.
617 * Size of the buffer pointed by the Buffer parameter. If the
618 * buffer size specifies incorrect value for specified control
619 * code, error ??? is returned.
622 * STATUS_PRIVILEGE_NOT_HELD
629 NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass
,
631 IN ULONG BufferLength
)
633 NTSTATUS Status
= STATUS_SUCCESS
;
635 DPRINT("NtPlugPlayControl(%lu %p %lu) called\n",
636 PlugPlayControlClass
, Buffer
, BufferLength
);
638 /* Function can only be called from user-mode */
639 if (KeGetPreviousMode() != UserMode
)
641 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
642 return STATUS_ACCESS_DENIED
;
645 /* Check for Tcb privilege */
646 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
,
649 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
650 return STATUS_PRIVILEGE_NOT_HELD
;
653 /* Probe the buffer */
656 ProbeForWrite(Buffer
,
662 Status
= _SEH_GetExceptionCode();
666 if (!NT_SUCCESS(Status
))
671 switch (PlugPlayControlClass
)
673 case PlugPlayControlUserResponse
:
674 if (Buffer
|| BufferLength
!= 0)
675 return STATUS_INVALID_PARAMETER
;
676 return IopRemovePlugPlayEvent();
678 case PlugPlayControlProperty
:
679 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA
))
680 return STATUS_INVALID_PARAMETER
;
681 return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA
)Buffer
);
683 case PlugPlayControlGetRelatedDevice
:
684 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA
))
685 return STATUS_INVALID_PARAMETER
;
686 return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA
)Buffer
);
688 case PlugPlayControlDeviceStatus
:
689 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_STATUS_DATA
))
690 return STATUS_INVALID_PARAMETER
;
691 return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA
)Buffer
);
693 case PlugPlayControlGetDeviceDepth
:
694 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_DEPTH_DATA
))
695 return STATUS_INVALID_PARAMETER
;
696 return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA
)Buffer
);
699 return STATUS_NOT_IMPLEMENTED
;
702 return STATUS_NOT_IMPLEMENTED
;