2 * PROJECT: ReactOS Kernel
3 * COPYRIGHT: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/pnpmgr/plugplay.c
5 * PURPOSE: Plug-and-play interface routines
6 * PROGRAMMERS: Eric Kohl <eric.kohl@t-online.de>
9 /* INCLUDES *****************************************************************/
15 #if defined (ALLOC_PRAGMA)
16 #pragma alloc_text(INIT, IopInitPlugPlayEvents)
19 typedef struct _PNP_EVENT_ENTRY
22 PLUGPLAY_EVENT_BLOCK Event
;
23 } PNP_EVENT_ENTRY
, *PPNP_EVENT_ENTRY
;
26 /* GLOBALS *******************************************************************/
28 static LIST_ENTRY IopPnpEventQueueHead
;
29 static KEVENT IopPnpNotifyEvent
;
31 /* FUNCTIONS *****************************************************************/
33 NTSTATUS INIT_FUNCTION
34 IopInitPlugPlayEvents(VOID
)
36 InitializeListHead(&IopPnpEventQueueHead
);
38 KeInitializeEvent(&IopPnpNotifyEvent
,
42 return STATUS_SUCCESS
;
46 IopQueueTargetDeviceEvent(const GUID
*Guid
,
47 PUNICODE_STRING DeviceIds
)
49 PPNP_EVENT_ENTRY EventEntry
;
56 /* Allocate a big enough buffer */
58 Copy
.MaximumLength
= DeviceIds
->Length
+ sizeof(UNICODE_NULL
);
60 FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK
, TargetDevice
.DeviceIds
) +
63 EventEntry
= ExAllocatePool(NonPagedPool
,
64 TotalSize
+ FIELD_OFFSET(PNP_EVENT_ENTRY
, Event
));
66 return STATUS_INSUFFICIENT_RESOURCES
;
68 /* Fill the buffer with the event GUID */
69 RtlCopyMemory(&EventEntry
->Event
.EventGuid
,
72 EventEntry
->Event
.EventCategory
= TargetDeviceChangeEvent
;
73 EventEntry
->Event
.TotalSize
= TotalSize
;
75 /* Fill the device id */
76 Copy
.Buffer
= EventEntry
->Event
.TargetDevice
.DeviceIds
;
77 Status
= RtlAppendUnicodeStringToString(&Copy
, DeviceIds
);
78 if (!NT_SUCCESS(Status
))
81 InsertHeadList(&IopPnpEventQueueHead
,
82 &EventEntry
->ListEntry
);
83 KeSetEvent(&IopPnpNotifyEvent
,
87 return STATUS_SUCCESS
;
92 * Remove the current PnP event from the tail of the event queue
93 * and signal IopPnpNotifyEvent if there is yet another event in the queue.
96 IopRemovePlugPlayEvent(VOID
)
98 /* Remove a pnp event entry from the tail of the queue */
99 if (!IsListEmpty(&IopPnpEventQueueHead
))
101 ExFreePool(RemoveTailList(&IopPnpEventQueueHead
));
104 /* Signal the next pnp event in the queue */
105 if (!IsListEmpty(&IopPnpEventQueueHead
))
107 KeSetEvent(&IopPnpNotifyEvent
,
112 return STATUS_SUCCESS
;
115 static PDEVICE_OBJECT
116 IopTraverseDeviceNode(PDEVICE_NODE Node
, PUNICODE_STRING DeviceInstance
)
118 PDEVICE_OBJECT DeviceObject
;
119 PDEVICE_NODE ChildNode
;
121 if (RtlEqualUnicodeString(&Node
->InstancePath
,
122 DeviceInstance
, TRUE
))
124 ObReferenceObject(Node
->PhysicalDeviceObject
);
125 return Node
->PhysicalDeviceObject
;
128 /* Traversal of all children nodes */
129 for (ChildNode
= Node
->Child
;
131 ChildNode
= ChildNode
->Sibling
)
133 DeviceObject
= IopTraverseDeviceNode(ChildNode
, DeviceInstance
);
134 if (DeviceObject
!= NULL
)
144 static PDEVICE_OBJECT
145 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance
)
147 if (IopRootDeviceNode
== NULL
)
150 if (DeviceInstance
== NULL
||
151 DeviceInstance
->Length
== 0
154 if (IopRootDeviceNode
->PhysicalDeviceObject
)
156 ObReferenceObject(IopRootDeviceNode
->PhysicalDeviceObject
);
157 return IopRootDeviceNode
->PhysicalDeviceObject
;
163 return IopTraverseDeviceNode(IopRootDeviceNode
, DeviceInstance
);
168 IopCaptureUnicodeString(PUNICODE_STRING DstName
, PUNICODE_STRING SrcName
)
170 NTSTATUS Status
= STATUS_SUCCESS
;
176 Name
.Length
= SrcName
->Length
;
177 Name
.MaximumLength
= SrcName
->MaximumLength
;
178 if (Name
.Length
> Name
.MaximumLength
)
180 Status
= STATUS_INVALID_PARAMETER
;
183 if (Name
.MaximumLength
)
185 ProbeForRead(SrcName
->Buffer
,
188 Name
.Buffer
= ExAllocatePool(NonPagedPool
, Name
.MaximumLength
);
189 if (Name
.Buffer
== NULL
)
191 Status
= STATUS_INSUFFICIENT_RESOURCES
;
194 memcpy(Name
.Buffer
, SrcName
->Buffer
, Name
.MaximumLength
);
198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
200 if (Name
.Buffer
) ExFreePool(Name
.Buffer
);
201 Status
= _SEH2_GetExceptionCode();
209 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData
)
211 PDEVICE_OBJECT DeviceObject
= NULL
;
213 UNICODE_STRING DeviceInstance
;
218 DPRINT("IopGetDeviceProperty() called\n");
219 DPRINT("Device name: %wZ\n", &PropertyData
->DeviceInstance
);
221 Status
= IopCaptureUnicodeString(&DeviceInstance
, &PropertyData
->DeviceInstance
);
222 if (!NT_SUCCESS(Status
))
229 Property
= PropertyData
->Property
;
230 BufferSize
= PropertyData
->BufferSize
;
231 ProbeForWrite(PropertyData
->Buffer
,
235 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
237 ExFreePool(DeviceInstance
.Buffer
);
238 _SEH2_YIELD(return _SEH2_GetExceptionCode());
242 /* Get the device object */
243 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&DeviceInstance
);
244 ExFreePool(DeviceInstance
.Buffer
);
245 if (DeviceObject
== NULL
)
247 return STATUS_NO_SUCH_DEVICE
;
250 Buffer
= ExAllocatePool(NonPagedPool
, BufferSize
);
253 return STATUS_INSUFFICIENT_RESOURCES
;
257 Status
= IoGetDeviceProperty(DeviceObject
,
263 ObDereferenceObject(DeviceObject
);
265 if (NT_SUCCESS(Status
))
269 memcpy(Buffer
, PropertyData
->Buffer
, BufferSize
);
270 PropertyData
->BufferSize
= BufferSize
;
272 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
274 Status
= _SEH2_GetExceptionCode();
284 IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData
)
286 UNICODE_STRING RootDeviceName
;
287 PDEVICE_OBJECT DeviceObject
= NULL
;
288 PDEVICE_NODE DeviceNode
= NULL
;
289 PDEVICE_NODE RelatedDeviceNode
;
290 UNICODE_STRING TargetDeviceInstance
;
291 NTSTATUS Status
= STATUS_SUCCESS
;
293 ULONG MaximumLength
= 0;
295 DPRINT("IopGetRelatedDevice() called\n");
296 DPRINT("Device name: %wZ\n", &RelatedDeviceData
->TargetDeviceInstance
);
298 Status
= IopCaptureUnicodeString(&TargetDeviceInstance
, &RelatedDeviceData
->TargetDeviceInstance
);
299 if (!NT_SUCCESS(Status
))
306 Relation
= RelatedDeviceData
->Relation
;
307 MaximumLength
= RelatedDeviceData
->RelatedDeviceInstanceLength
;
308 ProbeForWrite(RelatedDeviceData
->RelatedDeviceInstance
,
312 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
314 ExFreePool(TargetDeviceInstance
.Buffer
);
315 _SEH2_YIELD(return _SEH2_GetExceptionCode());
319 RtlInitUnicodeString(&RootDeviceName
,
321 if (RtlEqualUnicodeString(&TargetDeviceInstance
,
325 DeviceNode
= IopRootDeviceNode
;
326 ExFreePool(TargetDeviceInstance
.Buffer
);
330 /* Get the device object */
331 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&TargetDeviceInstance
);
332 ExFreePool(TargetDeviceInstance
.Buffer
);
333 if (DeviceObject
== NULL
)
334 return STATUS_NO_SUCH_DEVICE
;
336 DeviceNode
= ((PEXTENDED_DEVOBJ_EXTENSION
)DeviceObject
->DeviceObjectExtension
)->DeviceNode
;
341 case PNP_GET_PARENT_DEVICE
:
342 RelatedDeviceNode
= DeviceNode
->Parent
;
345 case PNP_GET_CHILD_DEVICE
:
346 RelatedDeviceNode
= DeviceNode
->Child
;
349 case PNP_GET_SIBLING_DEVICE
:
350 RelatedDeviceNode
= DeviceNode
->Sibling
;
354 if (DeviceObject
!= NULL
)
356 ObDereferenceObject(DeviceObject
);
359 return STATUS_INVALID_PARAMETER
;
362 if (RelatedDeviceNode
== NULL
)
366 ObDereferenceObject(DeviceObject
);
369 return STATUS_NO_SUCH_DEVICE
;
372 if (RelatedDeviceNode
->InstancePath
.Length
> MaximumLength
)
376 ObDereferenceObject(DeviceObject
);
379 return STATUS_BUFFER_TOO_SMALL
;
382 /* Copy related device instance name */
385 RtlCopyMemory(RelatedDeviceData
->RelatedDeviceInstance
,
386 RelatedDeviceNode
->InstancePath
.Buffer
,
387 RelatedDeviceNode
->InstancePath
.Length
);
388 RelatedDeviceData
->RelatedDeviceInstanceLength
= RelatedDeviceNode
->InstancePath
.Length
;
390 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
392 Status
= _SEH2_GetExceptionCode();
396 if (DeviceObject
!= NULL
)
398 ObDereferenceObject(DeviceObject
);
401 DPRINT("IopGetRelatedDevice() done\n");
408 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData
)
410 PDEVICE_OBJECT DeviceObject
;
411 PDEVICE_NODE DeviceNode
;
413 ULONG DeviceStatus
= 0;
414 ULONG DeviceProblem
= 0;
415 UNICODE_STRING DeviceInstance
;
418 DPRINT("IopDeviceStatus() called\n");
420 Status
= IopCaptureUnicodeString(&DeviceInstance
, &StatusData
->DeviceInstance
);
421 if (!NT_SUCCESS(Status
))
423 DPRINT("Device name: '%wZ'\n", &DeviceInstance
);
427 Operation
= StatusData
->Operation
;
428 if (Operation
== PNP_SET_DEVICE_STATUS
)
430 DeviceStatus
= StatusData
->DeviceStatus
;
431 DeviceProblem
= StatusData
->DeviceProblem
;
434 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
436 if (DeviceInstance
.Buffer
) ExFreePool(DeviceInstance
.Buffer
);
437 _SEH2_YIELD(return _SEH2_GetExceptionCode());
441 /* Get the device object */
442 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&DeviceInstance
);
443 ExFreePool(DeviceInstance
.Buffer
);
444 if (DeviceObject
== NULL
)
445 return STATUS_NO_SUCH_DEVICE
;
447 DeviceNode
= IopGetDeviceNode(DeviceObject
);
451 case PNP_GET_DEVICE_STATUS
:
452 DPRINT("Get status data\n");
453 DeviceStatus
= DeviceNode
->Flags
;
454 DeviceProblem
= DeviceNode
->Problem
;
457 case PNP_SET_DEVICE_STATUS
:
458 DPRINT("Set status data\n");
459 DeviceNode
->Flags
= DeviceStatus
;
460 DeviceNode
->Problem
= DeviceProblem
;
463 case PNP_CLEAR_DEVICE_STATUS
:
464 DPRINT1("FIXME: Clear status data!\n");
468 ObDereferenceObject(DeviceObject
);
470 if (Operation
== PNP_GET_DEVICE_STATUS
)
474 StatusData
->DeviceStatus
= DeviceStatus
;
475 StatusData
->DeviceProblem
= DeviceProblem
;
477 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
479 Status
= _SEH2_GetExceptionCode();
489 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData
)
491 PDEVICE_OBJECT DeviceObject
;
492 PDEVICE_NODE DeviceNode
;
493 UNICODE_STRING DeviceInstance
;
494 NTSTATUS Status
= STATUS_SUCCESS
;
496 DPRINT("IopGetDeviceDepth() called\n");
497 DPRINT("Device name: %wZ\n", &DepthData
->DeviceInstance
);
499 Status
= IopCaptureUnicodeString(&DeviceInstance
, &DepthData
->DeviceInstance
);
500 if (!NT_SUCCESS(Status
))
505 /* Get the device object */
506 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&DeviceInstance
);
507 ExFreePool(DeviceInstance
.Buffer
);
508 if (DeviceObject
== NULL
)
509 return STATUS_NO_SUCH_DEVICE
;
511 DeviceNode
= IopGetDeviceNode(DeviceObject
);
515 DepthData
->Depth
= DeviceNode
->Level
;
517 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
519 Status
= _SEH2_GetExceptionCode();
523 ObDereferenceObject(DeviceObject
);
530 IopResetDevice(PPLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData
)
532 PDEVICE_OBJECT DeviceObject
;
533 PDEVICE_NODE DeviceNode
;
534 NTSTATUS Status
= STATUS_SUCCESS
;
535 UNICODE_STRING DeviceInstance
;
537 Status
= IopCaptureUnicodeString(&DeviceInstance
, &ResetDeviceData
->DeviceInstance
);
538 if (!NT_SUCCESS(Status
))
541 DPRINT("IopResetDevice(%wZ)\n", &DeviceInstance
);
543 /* Get the device object */
544 DeviceObject
= IopGetDeviceObjectFromDeviceInstance(&DeviceInstance
);
545 ExFreePool(DeviceInstance
.Buffer
);
546 if (DeviceObject
== NULL
)
547 return STATUS_NO_SUCH_DEVICE
;
549 DeviceNode
= IopGetDeviceNode(DeviceObject
);
551 /* FIXME: we should stop the device, before starting it again */
553 /* Start the device */
554 IopDeviceNodeClearFlag(DeviceNode
, DNF_DISABLED
);
555 Status
= IopActionConfigureChildServices(DeviceNode
, DeviceNode
->Parent
);
557 if (NT_SUCCESS(Status
))
558 Status
= IopActionInitChildServices(DeviceNode
, DeviceNode
->Parent
);
560 ObDereferenceObject(DeviceObject
);
565 /* PUBLIC FUNCTIONS **********************************************************/
568 * Plug and Play event structure used by NtGetPlugPlayEvent.
571 * Can be one of the following values:
572 * GUID_HWPROFILE_QUERY_CHANGE
573 * GUID_HWPROFILE_CHANGE_CANCELLED
574 * GUID_HWPROFILE_CHANGE_COMPLETE
575 * GUID_TARGET_DEVICE_QUERY_REMOVE
576 * GUID_TARGET_DEVICE_REMOVE_CANCELLED
577 * GUID_TARGET_DEVICE_REMOVE_COMPLETE
578 * GUID_PNP_CUSTOM_NOTIFICATION
579 * GUID_PNP_POWER_NOTIFICATION
580 * GUID_DEVICE_* (see above)
583 * Type of the event that happened.
592 * Size of the event block including the device IDs and other
593 * per category specific fields.
599 * Returns one Plug & Play event from a global queue.
604 * Always set to zero.
607 * The buffer that will be filled with the event information on
608 * successful return from the function.
611 * Size of the buffer pointed by the Buffer parameter. If the
612 * buffer size is not large enough to hold the whole event
613 * information, error STATUS_BUFFER_TOO_SMALL is returned and
614 * the buffer remains untouched.
617 * STATUS_PRIVILEGE_NOT_HELD
618 * STATUS_BUFFER_TOO_SMALL
622 * This function isn't multi-thread safe!
628 NtGetPlugPlayEvent(IN ULONG Reserved1
,
630 OUT PPLUGPLAY_EVENT_BLOCK Buffer
,
633 PPNP_EVENT_ENTRY Entry
;
636 DPRINT("NtGetPlugPlayEvent() called\n");
638 /* Function can only be called from user-mode */
639 if (KeGetPreviousMode() == KernelMode
)
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 /* Wait for a PnP event */
654 DPRINT("Waiting for pnp notification event\n");
655 Status
= KeWaitForSingleObject(&IopPnpNotifyEvent
,
660 if (!NT_SUCCESS(Status
))
662 DPRINT1("KeWaitForSingleObject() failed (Status %lx)\n", Status
);
666 /* Get entry from the tail of the queue */
667 Entry
= CONTAINING_RECORD(IopPnpEventQueueHead
.Blink
,
671 /* Check the buffer size */
672 if (BufferSize
< Entry
->Event
.TotalSize
)
674 DPRINT1("Buffer is too small for the pnp-event\n");
675 return STATUS_BUFFER_TOO_SMALL
;
678 /* Copy event data to the user buffer */
681 Entry
->Event
.TotalSize
);
683 DPRINT("NtGetPlugPlayEvent() done\n");
685 return STATUS_SUCCESS
;
691 * A function for doing various Plug & Play operations from user mode.
694 * PlugPlayControlClass
695 * 0x00 Reenumerate device tree
697 * Buffer points to UNICODE_STRING decribing the instance
698 * path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For
699 * more information about instance paths see !devnode command
700 * in kernel debugger or look at "Inside Windows 2000" book,
701 * chapter "Driver Loading, Initialization, and Installation".
703 * 0x01 Register new device
704 * 0x02 Deregister device
705 * 0x03 Initialize device
707 * 0x06 Query and remove device
710 * Called after processing the message from NtGetPlugPlayEvent.
712 * 0x08 Generate legacy device
713 * 0x09 Get interface device list
714 * 0x0A Get property data
715 * 0x0B Device class association (Registration)
716 * 0x0C Get related device
717 * 0x0D Get device interface alias
718 * 0x0E Get/set/clear device status
719 * 0x0F Get device depth
720 * 0x10 Query device relations
721 * 0x11 Query target device relation
722 * 0x12 Query conflict list
723 * 0x13 Retrieve dock data
726 * 0x16 Get blocked driver data
729 * The buffer contains information that is specific to each control
730 * code. The buffer is read-only.
733 * Size of the buffer pointed by the Buffer parameter. If the
734 * buffer size specifies incorrect value for specified control
735 * code, error ??? is returned.
738 * STATUS_PRIVILEGE_NOT_HELD
746 NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass
,
748 IN ULONG BufferLength
)
750 DPRINT("NtPlugPlayControl(%lu %p %lu) called\n",
751 PlugPlayControlClass
, Buffer
, BufferLength
);
753 /* Function can only be called from user-mode */
754 if (KeGetPreviousMode() == KernelMode
)
756 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
757 return STATUS_ACCESS_DENIED
;
760 /* Check for Tcb privilege */
761 if (!SeSinglePrivilegeCheck(SeTcbPrivilege
,
764 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
765 return STATUS_PRIVILEGE_NOT_HELD
;
768 /* Probe the buffer */
771 ProbeForWrite(Buffer
,
775 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
777 _SEH2_YIELD(return _SEH2_GetExceptionCode());
781 switch (PlugPlayControlClass
)
783 case PlugPlayControlUserResponse
:
784 if (Buffer
|| BufferLength
!= 0)
785 return STATUS_INVALID_PARAMETER
;
786 return IopRemovePlugPlayEvent();
788 case PlugPlayControlProperty
:
789 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA
))
790 return STATUS_INVALID_PARAMETER
;
791 return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA
)Buffer
);
793 case PlugPlayControlGetRelatedDevice
:
794 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA
))
795 return STATUS_INVALID_PARAMETER
;
796 return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA
)Buffer
);
798 case PlugPlayControlDeviceStatus
:
799 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_STATUS_DATA
))
800 return STATUS_INVALID_PARAMETER
;
801 return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA
)Buffer
);
803 case PlugPlayControlGetDeviceDepth
:
804 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_DEPTH_DATA
))
805 return STATUS_INVALID_PARAMETER
;
806 return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA
)Buffer
);
808 case PlugPlayControlResetDevice
:
809 if (!Buffer
|| BufferLength
< sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA
))
810 return STATUS_INVALID_PARAMETER
;
811 return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA
)Buffer
);
814 return STATUS_NOT_IMPLEMENTED
;
817 return STATUS_NOT_IMPLEMENTED
;