xref: /reactos/ntoskrnl/io/pnpmgr/plugplay.c (revision 4572aad1)
1 /*
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>
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 typedef struct _PNP_EVENT_ENTRY
16 {
17     LIST_ENTRY ListEntry;
18     PLUGPLAY_EVENT_BLOCK Event;
19 } PNP_EVENT_ENTRY, *PPNP_EVENT_ENTRY;
20 
21 
22 /* GLOBALS *******************************************************************/
23 
24 static LIST_ENTRY IopPnpEventQueueHead;
25 static KEVENT IopPnpNotifyEvent;
26 
27 /* FUNCTIONS *****************************************************************/
28 
29 CODE_SEG("INIT")
30 NTSTATUS
31 IopInitPlugPlayEvents(VOID)
32 {
33     InitializeListHead(&IopPnpEventQueueHead);
34 
35     KeInitializeEvent(&IopPnpNotifyEvent,
36                       SynchronizationEvent,
37                       FALSE);
38 
39     return STATUS_SUCCESS;
40 }
41 
42 NTSTATUS
43 IopQueueTargetDeviceEvent(const GUID *Guid,
44                           PUNICODE_STRING DeviceIds)
45 {
46     PPNP_EVENT_ENTRY EventEntry;
47     UNICODE_STRING Copy;
48     ULONG TotalSize;
49     NTSTATUS Status;
50 
51     ASSERT(DeviceIds);
52 
53     /* Allocate a big enough buffer */
54     Copy.Length = 0;
55     Copy.MaximumLength = DeviceIds->Length + sizeof(UNICODE_NULL);
56     TotalSize =
57         FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, TargetDevice.DeviceIds) +
58         Copy.MaximumLength;
59 
60     EventEntry = ExAllocatePool(NonPagedPool,
61                                 TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
62     if (!EventEntry)
63         return STATUS_INSUFFICIENT_RESOURCES;
64     RtlZeroMemory(EventEntry, TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
65 
66     /* Fill the buffer with the event GUID */
67     RtlCopyMemory(&EventEntry->Event.EventGuid,
68                   Guid,
69                   sizeof(GUID));
70     EventEntry->Event.EventCategory = TargetDeviceChangeEvent;
71     EventEntry->Event.TotalSize = TotalSize;
72 
73     /* Fill the device id */
74     Copy.Buffer = EventEntry->Event.TargetDevice.DeviceIds;
75     Status = RtlAppendUnicodeStringToString(&Copy, DeviceIds);
76     if (!NT_SUCCESS(Status))
77     {
78         ExFreePool(EventEntry);
79         return Status;
80     }
81 
82     InsertHeadList(&IopPnpEventQueueHead,
83                    &EventEntry->ListEntry);
84     KeSetEvent(&IopPnpNotifyEvent,
85                0,
86                FALSE);
87 
88     return STATUS_SUCCESS;
89 }
90 
91 
92 static PDEVICE_OBJECT
93 IopTraverseDeviceNode(PDEVICE_NODE Node, PUNICODE_STRING DeviceInstance)
94 {
95     PDEVICE_OBJECT DeviceObject;
96     PDEVICE_NODE ChildNode;
97 
98     if (RtlEqualUnicodeString(&Node->InstancePath,
99                               DeviceInstance, TRUE))
100     {
101         ObReferenceObject(Node->PhysicalDeviceObject);
102         return Node->PhysicalDeviceObject;
103     }
104 
105     /* Traversal of all children nodes */
106     for (ChildNode = Node->Child;
107          ChildNode != NULL;
108          ChildNode = ChildNode->Sibling)
109     {
110         DeviceObject = IopTraverseDeviceNode(ChildNode, DeviceInstance);
111         if (DeviceObject != NULL)
112         {
113             return DeviceObject;
114         }
115     }
116 
117     return NULL;
118 }
119 
120 
121 PDEVICE_OBJECT
122 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)
123 {
124     if (IopRootDeviceNode == NULL)
125         return NULL;
126 
127     if (DeviceInstance == NULL ||
128         DeviceInstance->Length == 0)
129     {
130         if (IopRootDeviceNode->PhysicalDeviceObject)
131         {
132             ObReferenceObject(IopRootDeviceNode->PhysicalDeviceObject);
133             return IopRootDeviceNode->PhysicalDeviceObject;
134         }
135         else
136             return NULL;
137     }
138 
139     return IopTraverseDeviceNode(IopRootDeviceNode, DeviceInstance);
140 }
141 
142 static NTSTATUS
143 IopCaptureUnicodeString(PUNICODE_STRING DstName, PUNICODE_STRING SrcName)
144 {
145     NTSTATUS Status = STATUS_SUCCESS;
146     volatile UNICODE_STRING Name;
147 
148     Name.Buffer = NULL;
149     _SEH2_TRY
150     {
151         Name.Length = SrcName->Length;
152         Name.MaximumLength = SrcName->MaximumLength;
153         if (Name.Length > Name.MaximumLength)
154         {
155             Status = STATUS_INVALID_PARAMETER;
156             _SEH2_LEAVE;
157         }
158 
159         if (Name.MaximumLength)
160         {
161             ProbeForRead(SrcName->Buffer,
162                          Name.MaximumLength,
163                          sizeof(WCHAR));
164             Name.Buffer = ExAllocatePool(NonPagedPool, Name.MaximumLength);
165             if (Name.Buffer == NULL)
166             {
167                 Status = STATUS_INSUFFICIENT_RESOURCES;
168                 _SEH2_LEAVE;
169             }
170 
171             memcpy(Name.Buffer, SrcName->Buffer, Name.MaximumLength);
172         }
173 
174         *DstName = Name;
175     }
176     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
177     {
178         if (Name.Buffer)
179         {
180             ExFreePool(Name.Buffer);
181         }
182         Status = _SEH2_GetExceptionCode();
183     }
184     _SEH2_END;
185 
186     return Status;
187 }
188 
189 
190 static
191 NTSTATUS
192 IopInitializeDevice(
193     _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA ControlData)
194 {
195     UNICODE_STRING DeviceInstance;
196     PDEVICE_OBJECT DeviceObject;
197     PDEVICE_NODE DeviceNode;
198     NTSTATUS Status = STATUS_SUCCESS;
199 
200     DPRINT("IopInitializeDevice(%p)\n", ControlData);
201 
202     Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance);
203     if (!NT_SUCCESS(Status))
204     {
205         return Status;
206     }
207 
208     DPRINT("Device: %wZ\n", &DeviceInstance);
209 
210     /* Leave, if the device already exists */
211     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
212     if (DeviceInstance.Buffer != NULL)
213     {
214         DPRINT1("Device %wZ already exists!\n", &DeviceInstance);
215         Status = STATUS_SUCCESS;
216         goto done;
217     }
218 
219     ObDereferenceObject(DeviceObject);
220 
221     DPRINT("Device %wZ does not exist!\n", &DeviceInstance);
222 
223     /* Create a device node for the device instance */
224     Status = IoCreateDevice(IopRootDriverObject,
225                             0,
226                             NULL,
227                             FILE_DEVICE_CONTROLLER,
228                             FILE_AUTOGENERATED_DEVICE_NAME,
229                             FALSE,
230                             &DeviceObject);
231     if (!NT_SUCCESS(Status))
232     {
233         DPRINT1("IoCreateDevice() failed (Status 0x%08lx)\n", Status);
234         goto done;
235     }
236 
237     /* Allocate a new device node */
238     DeviceNode = PipAllocateDeviceNode(DeviceObject);
239     if (DeviceNode == NULL)
240     {
241         DPRINT1("Failed to allocate a device node!\n");
242         IoDeleteDevice(DeviceObject);
243         Status = STATUS_INSUFFICIENT_RESOURCES;
244         goto done;
245     }
246 
247     /* Set the device instance of the device node */
248     Status = RtlDuplicateUnicodeString(0, &DeviceInstance, &DeviceNode->InstancePath);
249     if (!NT_SUCCESS(Status))
250     {
251         DPRINT1("RtlDuplicateUnicodeString() failed (Status 0x%08lx)\n", Status);
252         IopFreeDeviceNode(DeviceNode);
253         IoDeleteDevice(DeviceObject);
254         goto done;
255     }
256 
257     /* Insert as a root enumerated device node */
258     PiInsertDevNode(DeviceNode, IopRootDeviceNode);
259 
260 done:
261     ExFreePool(DeviceInstance.Buffer);
262 
263     return Status;
264 }
265 
266 
267 /*
268  * Remove the current PnP event from the tail of the event queue
269  * and signal IopPnpNotifyEvent if there is yet another event in the queue.
270  */
271 static
272 NTSTATUS
273 IopRemovePlugPlayEvent(
274     _In_ PPLUGPLAY_CONTROL_USER_RESPONSE_DATA ResponseData)
275 {
276     /* Remove a pnp event entry from the tail of the queue */
277     if (!IsListEmpty(&IopPnpEventQueueHead))
278     {
279         ExFreePool(CONTAINING_RECORD(RemoveTailList(&IopPnpEventQueueHead), PNP_EVENT_ENTRY, ListEntry));
280     }
281 
282     /* Signal the next pnp event in the queue */
283     if (!IsListEmpty(&IopPnpEventQueueHead))
284     {
285         KeSetEvent(&IopPnpNotifyEvent,
286                    0,
287                    FALSE);
288     }
289 
290     return STATUS_SUCCESS;
291 }
292 
293 
294 static NTSTATUS
295 IopGetInterfaceDeviceList(PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA DeviceList)
296 {
297     NTSTATUS Status;
298     PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA StackList;
299     UNICODE_STRING DeviceInstance;
300     PDEVICE_OBJECT DeviceObject = NULL;
301     GUID FilterGuid;
302     PZZWSTR SymbolicLinkList = NULL, LinkList;
303     SIZE_T TotalLength;
304 
305     _SEH2_TRY
306     {
307         RtlCopyMemory(&StackList, DeviceList, sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA));
308 
309         ProbeForRead(StackList.FilterGuid, sizeof(GUID), sizeof(UCHAR));
310         RtlCopyMemory(&FilterGuid, StackList.FilterGuid, sizeof(GUID));
311 
312         if (StackList.Buffer != NULL && StackList.BufferSize != 0)
313         {
314             ProbeForWrite(StackList.Buffer, StackList.BufferSize, sizeof(UCHAR));
315         }
316     }
317     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
318     {
319         _SEH2_YIELD(return _SEH2_GetExceptionCode());
320     }
321     _SEH2_END;
322 
323     Status = IopCaptureUnicodeString(&DeviceInstance, &StackList.DeviceInstance);
324     if (NT_SUCCESS(Status))
325     {
326         /* Get the device object */
327         DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
328         if (DeviceInstance.Buffer != NULL)
329         {
330             ExFreePool(DeviceInstance.Buffer);
331         }
332     }
333 
334     Status = IoGetDeviceInterfaces(&FilterGuid, DeviceObject, StackList.Flags, &SymbolicLinkList);
335     ObDereferenceObject(DeviceObject);
336 
337     if (!NT_SUCCESS(Status))
338     {
339         /* failed */
340         return Status;
341     }
342 
343     LinkList = SymbolicLinkList;
344     while (*SymbolicLinkList != UNICODE_NULL)
345     {
346         SymbolicLinkList += wcslen(SymbolicLinkList) + (sizeof(UNICODE_NULL) / sizeof(WCHAR));
347     }
348     TotalLength = ((SymbolicLinkList - LinkList + 1) * sizeof(WCHAR));
349 
350     _SEH2_TRY
351     {
352         if (StackList.Buffer != NULL &&
353             StackList.BufferSize >= TotalLength)
354         {
355             // We've already probed the buffer for writing above.
356             RtlCopyMemory(StackList.Buffer, LinkList, TotalLength);
357         }
358 
359         DeviceList->BufferSize = TotalLength;
360     }
361     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
362     {
363         ExFreePool(LinkList);
364         _SEH2_YIELD(return _SEH2_GetExceptionCode());
365     }
366     _SEH2_END;
367 
368     ExFreePool(LinkList);
369     return STATUS_SUCCESS;
370 }
371 
372 static NTSTATUS
373 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
374 {
375     PDEVICE_OBJECT DeviceObject = NULL;
376     PDEVICE_NODE DeviceNode;
377     UNICODE_STRING DeviceInstance;
378     ULONG BufferSize;
379     ULONG Property;
380     DEVICE_REGISTRY_PROPERTY DeviceProperty;
381     PVOID Buffer;
382     NTSTATUS Status;
383 
384     DPRINT("IopGetDeviceProperty() called\n");
385     DPRINT("Device name: %wZ\n", &PropertyData->DeviceInstance);
386 
387     Status = IopCaptureUnicodeString(&DeviceInstance, &PropertyData->DeviceInstance);
388     if (!NT_SUCCESS(Status))
389     {
390         return Status;
391     }
392 
393     _SEH2_TRY
394     {
395         Property = PropertyData->Property;
396         BufferSize = PropertyData->BufferSize;
397         ProbeForWrite(PropertyData->Buffer,
398                       BufferSize,
399                       sizeof(UCHAR));
400     }
401     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
402     {
403         if (DeviceInstance.Buffer != NULL)
404         {
405             ExFreePool(DeviceInstance.Buffer);
406         }
407         _SEH2_YIELD(return _SEH2_GetExceptionCode());
408     }
409     _SEH2_END;
410 
411     /* Get the device object */
412     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
413     if (DeviceInstance.Buffer != NULL)
414     {
415         ExFreePool(DeviceInstance.Buffer);
416     }
417     if (DeviceObject == NULL)
418     {
419         return STATUS_NO_SUCH_DEVICE;
420     }
421 
422     Buffer = ExAllocatePool(NonPagedPool, BufferSize);
423     if (Buffer == NULL)
424     {
425         ObDereferenceObject(DeviceObject);
426         return STATUS_INSUFFICIENT_RESOURCES;
427     }
428 
429 
430     DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
431 
432     if (Property == PNP_PROPERTY_POWER_DATA)
433     {
434         if (BufferSize < sizeof(CM_POWER_DATA))
435         {
436             BufferSize = 0;
437             Status = STATUS_BUFFER_TOO_SMALL;
438         }
439         else
440         {
441             DEVICE_CAPABILITIES DeviceCapabilities;
442             PCM_POWER_DATA PowerData;
443             IO_STACK_LOCATION Stack;
444             IO_STATUS_BLOCK IoStatusBlock;
445 
446             PowerData = (PCM_POWER_DATA)Buffer;
447             RtlZeroMemory(PowerData, sizeof(CM_POWER_DATA));
448             PowerData->PD_Size = sizeof(CM_POWER_DATA);
449 
450             RtlZeroMemory(&DeviceCapabilities, sizeof(DEVICE_CAPABILITIES));
451             DeviceCapabilities.Size = sizeof(DEVICE_CAPABILITIES);
452             DeviceCapabilities.Version = 1;
453             DeviceCapabilities.Address = -1;
454             DeviceCapabilities.UINumber = -1;
455 
456             Stack.Parameters.DeviceCapabilities.Capabilities = &DeviceCapabilities;
457 
458             Status = IopInitiatePnpIrp(DeviceObject,
459                                        &IoStatusBlock,
460                                        IRP_MN_QUERY_CAPABILITIES,
461                                        &Stack);
462             if (NT_SUCCESS(Status))
463             {
464                 DPRINT("Got device capabiliities\n");
465 
466                 PowerData->PD_MostRecentPowerState = PowerDeviceD0; // FIXME
467                 if (DeviceCapabilities.DeviceD1)
468                     PowerData->PD_Capabilities |= PDCAP_D1_SUPPORTED;
469                 if (DeviceCapabilities.DeviceD2)
470                     PowerData->PD_Capabilities |= PDCAP_D2_SUPPORTED;
471                 if (DeviceCapabilities.WakeFromD0)
472                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D0_SUPPORTED;
473                 if (DeviceCapabilities.WakeFromD1)
474                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D1_SUPPORTED;
475                 if (DeviceCapabilities.WakeFromD2)
476                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D2_SUPPORTED;
477                 if (DeviceCapabilities.WakeFromD3)
478                     PowerData->PD_Capabilities |= PDCAP_WAKE_FROM_D3_SUPPORTED;
479                 if (DeviceCapabilities.WarmEjectSupported)
480                     PowerData->PD_Capabilities |= PDCAP_WARM_EJECT_SUPPORTED;
481                 PowerData->PD_D1Latency = DeviceCapabilities.D1Latency;
482                 PowerData->PD_D2Latency = DeviceCapabilities.D2Latency;
483                 PowerData->PD_D3Latency = DeviceCapabilities.D3Latency;
484                 RtlCopyMemory(&PowerData->PD_PowerStateMapping,
485                               &DeviceCapabilities.DeviceState,
486                               sizeof(DeviceCapabilities.DeviceState));
487                 PowerData->PD_DeepestSystemWake = DeviceCapabilities.SystemWake;
488             }
489             else
490             {
491                 DPRINT("IRP_MN_QUERY_CAPABILITIES failed (Status 0x%08lx)\n", Status);
492 
493                 PowerData->PD_Capabilities = PDCAP_D0_SUPPORTED | PDCAP_D3_SUPPORTED;
494                 PowerData->PD_MostRecentPowerState = PowerDeviceD0;
495             }
496         }
497     }
498     else if (Property == PNP_PROPERTY_REMOVAL_POLICY_OVERRIDE)
499     {
500         UNIMPLEMENTED;
501         BufferSize = 0;
502         Status = STATUS_NOT_IMPLEMENTED;
503     }
504     else if (Property == PNP_PROPERTY_REMOVAL_POLICY_HARDWARE_DEFAULT)
505     {
506         if (BufferSize < sizeof(DeviceNode->HardwareRemovalPolicy))
507         {
508             BufferSize = 0;
509             Status = STATUS_BUFFER_TOO_SMALL;
510         }
511         else
512         {
513             BufferSize = sizeof(DeviceNode->HardwareRemovalPolicy);
514             RtlCopyMemory(Buffer,
515                           &DeviceNode->HardwareRemovalPolicy,
516                           BufferSize);
517         }
518     }
519     else
520     {
521         switch (Property)
522         {
523             case PNP_PROPERTY_UI_NUMBER:
524                 DeviceProperty = DevicePropertyUINumber;
525                 break;
526 
527             case PNP_PROPERTY_PHYSICAL_DEVICE_OBJECT_NAME:
528                 DeviceProperty = DevicePropertyPhysicalDeviceObjectName;
529                 break;
530 
531             case PNP_PROPERTY_BUSTYPEGUID:
532                 DeviceProperty = DevicePropertyBusTypeGuid;
533                 break;
534 
535             case PNP_PROPERTY_LEGACYBUSTYPE:
536                 DeviceProperty = DevicePropertyLegacyBusType;
537                 break;
538 
539             case PNP_PROPERTY_BUSNUMBER:
540                 DeviceProperty = DevicePropertyBusNumber;
541                 break;
542 
543             case PNP_PROPERTY_REMOVAL_POLICY:
544                 DeviceProperty = DevicePropertyRemovalPolicy;
545                 break;
546 
547             case PNP_PROPERTY_ADDRESS:
548                 DeviceProperty = DevicePropertyAddress;
549                 break;
550 
551             case PNP_PROPERTY_ENUMERATOR_NAME:
552                 DeviceProperty = DevicePropertyEnumeratorName;
553                 break;
554 
555             case PNP_PROPERTY_INSTALL_STATE:
556                 DeviceProperty = DevicePropertyInstallState;
557                 break;
558 
559 #if (WINVER >= _WIN32_WINNT_WS03)
560             case PNP_PROPERTY_LOCATION_PATHS:
561                 UNIMPLEMENTED;
562                 BufferSize = 0;
563                 Status = STATUS_NOT_IMPLEMENTED;
564                 break;
565 #endif
566 
567 #if (WINVER >= _WIN32_WINNT_WIN7)
568             case PNP_PROPERTY_CONTAINERID:
569                 DeviceProperty = DevicePropertyContainerID;
570                 break;
571 #endif
572 
573             default:
574                 BufferSize = 0;
575                 Status = STATUS_INVALID_PARAMETER;
576                 break;
577         }
578 
579         if (Status == STATUS_SUCCESS)
580         {
581             Status = IoGetDeviceProperty(DeviceObject,
582                                          DeviceProperty,
583                                          BufferSize,
584                                          Buffer,
585                                          &BufferSize);
586         }
587     }
588 
589     ObDereferenceObject(DeviceObject);
590 
591     if (NT_SUCCESS(Status))
592     {
593         _SEH2_TRY
594         {
595             RtlCopyMemory(PropertyData->Buffer, Buffer, BufferSize);
596             PropertyData->BufferSize = BufferSize;
597         }
598         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
599         {
600             Status = _SEH2_GetExceptionCode();
601         }
602         _SEH2_END;
603     }
604 
605     ExFreePool(Buffer);
606     return Status;
607 }
608 
609 
610 static NTSTATUS
611 IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)
612 {
613     UNICODE_STRING RootDeviceName;
614     PDEVICE_OBJECT DeviceObject = NULL;
615     PDEVICE_NODE DeviceNode = NULL;
616     PDEVICE_NODE RelatedDeviceNode;
617     UNICODE_STRING TargetDeviceInstance;
618     NTSTATUS Status = STATUS_SUCCESS;
619     ULONG Relation = 0;
620     ULONG MaximumLength = 0;
621 
622     DPRINT("IopGetRelatedDevice() called\n");
623     DPRINT("Device name: %wZ\n", &RelatedDeviceData->TargetDeviceInstance);
624 
625     Status = IopCaptureUnicodeString(&TargetDeviceInstance, &RelatedDeviceData->TargetDeviceInstance);
626     if (!NT_SUCCESS(Status))
627     {
628         return Status;
629     }
630 
631     _SEH2_TRY
632     {
633         Relation = RelatedDeviceData->Relation;
634         MaximumLength = RelatedDeviceData->RelatedDeviceInstanceLength;
635         ProbeForWrite(RelatedDeviceData->RelatedDeviceInstance,
636                       MaximumLength,
637                       sizeof(WCHAR));
638     }
639     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
640     {
641         if (TargetDeviceInstance.Buffer != NULL)
642         {
643             ExFreePool(TargetDeviceInstance.Buffer);
644         }
645         _SEH2_YIELD(return _SEH2_GetExceptionCode());
646     }
647     _SEH2_END;
648 
649     RtlInitUnicodeString(&RootDeviceName,
650                          L"HTREE\\ROOT\\0");
651     if (RtlEqualUnicodeString(&TargetDeviceInstance,
652                               &RootDeviceName,
653                               TRUE))
654     {
655         DeviceNode = IopRootDeviceNode;
656         if (TargetDeviceInstance.Buffer != NULL)
657         {
658             ExFreePool(TargetDeviceInstance.Buffer);
659         }
660     }
661     else
662     {
663         /* Get the device object */
664         DeviceObject = IopGetDeviceObjectFromDeviceInstance(&TargetDeviceInstance);
665         if (TargetDeviceInstance.Buffer != NULL)
666         {
667             ExFreePool(TargetDeviceInstance.Buffer);
668         }
669         if (DeviceObject == NULL)
670             return STATUS_NO_SUCH_DEVICE;
671 
672         DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
673     }
674 
675     switch (Relation)
676     {
677         case PNP_GET_PARENT_DEVICE:
678             RelatedDeviceNode = DeviceNode->Parent;
679             break;
680 
681         case PNP_GET_CHILD_DEVICE:
682             RelatedDeviceNode = DeviceNode->Child;
683             break;
684 
685         case PNP_GET_SIBLING_DEVICE:
686             RelatedDeviceNode = DeviceNode->Sibling;
687             break;
688 
689         default:
690             if (DeviceObject != NULL)
691             {
692                 ObDereferenceObject(DeviceObject);
693             }
694 
695             return STATUS_INVALID_PARAMETER;
696     }
697 
698     if (RelatedDeviceNode == NULL)
699     {
700         if (DeviceObject)
701         {
702             ObDereferenceObject(DeviceObject);
703         }
704 
705         return STATUS_NO_SUCH_DEVICE;
706     }
707 
708     if (RelatedDeviceNode->InstancePath.Length > MaximumLength)
709     {
710         if (DeviceObject)
711         {
712             ObDereferenceObject(DeviceObject);
713         }
714 
715         return STATUS_BUFFER_TOO_SMALL;
716     }
717 
718     /* Copy related device instance name */
719     _SEH2_TRY
720     {
721         RtlCopyMemory(RelatedDeviceData->RelatedDeviceInstance,
722                       RelatedDeviceNode->InstancePath.Buffer,
723                       RelatedDeviceNode->InstancePath.Length);
724         RelatedDeviceData->RelatedDeviceInstanceLength = RelatedDeviceNode->InstancePath.Length;
725     }
726     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
727     {
728         Status = _SEH2_GetExceptionCode();
729     }
730     _SEH2_END;
731 
732     if (DeviceObject != NULL)
733     {
734         ObDereferenceObject(DeviceObject);
735     }
736 
737     DPRINT("IopGetRelatedDevice() done\n");
738 
739     return Status;
740 }
741 
742 static
743 BOOLEAN
744 PiIsDevNodeStarted(
745     _In_ PDEVICE_NODE DeviceNode)
746 {
747     return (DeviceNode->State == DeviceNodeStartPending ||
748             DeviceNode->State == DeviceNodeStartCompletion ||
749             DeviceNode->State == DeviceNodeStartPostWork ||
750             DeviceNode->State == DeviceNodeStarted ||
751             DeviceNode->State == DeviceNodeQueryStopped ||
752             DeviceNode->State == DeviceNodeEnumeratePending ||
753             DeviceNode->State == DeviceNodeEnumerateCompletion ||
754             DeviceNode->State == DeviceNodeStopped ||
755             DeviceNode->State == DeviceNodeRestartCompletion);
756 }
757 
758 static ULONG
759 IopGetDeviceNodeStatus(PDEVICE_NODE DeviceNode)
760 {
761     ULONG Output = DN_NT_ENUMERATOR | DN_NT_DRIVER;
762 
763     if (DeviceNode->Parent == IopRootDeviceNode)
764         Output |= DN_ROOT_ENUMERATED;
765 
766     // FIXME: review for deleted and removed states
767     if (DeviceNode->State >= DeviceNodeDriversAdded)
768         Output |= DN_DRIVER_LOADED;
769 
770     if (PiIsDevNodeStarted(DeviceNode))
771         Output |= DN_STARTED;
772 
773     if (DeviceNode->UserFlags & DNUF_WILL_BE_REMOVED)
774         Output |= DN_WILL_BE_REMOVED;
775 
776     if (DeviceNode->Flags & DNF_HAS_PROBLEM)
777         Output |= DN_HAS_PROBLEM;
778 
779     if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM)
780         Output |= DN_PRIVATE_PROBLEM;
781 
782     if (DeviceNode->Flags & DNF_DRIVER_BLOCKED)
783         Output |= DN_DRIVER_BLOCKED;
784 
785     if (DeviceNode->Flags & DNF_CHILD_WITH_INVALID_ID)
786         Output |= DN_CHILD_WITH_INVALID_ID;
787 
788     if (DeviceNode->Flags & DNF_HAS_PRIVATE_PROBLEM)
789         Output |= DN_PRIVATE_PROBLEM;
790 
791     if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
792         Output |= DN_LEGACY_DRIVER;
793 
794     if (DeviceNode->UserFlags & DNUF_DONT_SHOW_IN_UI)
795         Output |= DN_NO_SHOW_IN_DM;
796 
797     if (!(DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE))
798         Output |= DN_DISABLEABLE;
799 
800     return Output;
801 }
802 
803 static NTSTATUS
804 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
805 {
806     PDEVICE_OBJECT DeviceObject;
807     PDEVICE_NODE DeviceNode;
808     ULONG Operation = 0;
809     ULONG DeviceStatus = 0;
810     ULONG DeviceProblem = 0;
811     UNICODE_STRING DeviceInstance;
812     NTSTATUS Status;
813 
814     DPRINT("IopDeviceStatus() called\n");
815 
816     Status = IopCaptureUnicodeString(&DeviceInstance, &StatusData->DeviceInstance);
817     if (!NT_SUCCESS(Status))
818     {
819         return Status;
820     }
821 
822     DPRINT("Device name: '%wZ'\n", &DeviceInstance);
823 
824     _SEH2_TRY
825     {
826         Operation = StatusData->Operation;
827         if (Operation == PNP_SET_DEVICE_STATUS)
828         {
829             DeviceStatus = StatusData->DeviceStatus;
830             DeviceProblem = StatusData->DeviceProblem;
831         }
832     }
833     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
834     {
835         if (DeviceInstance.Buffer != NULL)
836         {
837             ExFreePool(DeviceInstance.Buffer);
838         }
839         _SEH2_YIELD(return _SEH2_GetExceptionCode());
840     }
841     _SEH2_END;
842 
843     /* Get the device object */
844     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
845     if (DeviceInstance.Buffer != NULL)
846     {
847         ExFreePool(DeviceInstance.Buffer);
848     }
849     if (DeviceObject == NULL)
850     {
851         return STATUS_NO_SUCH_DEVICE;
852     }
853 
854     DeviceNode = IopGetDeviceNode(DeviceObject);
855 
856     switch (Operation)
857     {
858         case PNP_GET_DEVICE_STATUS:
859             DPRINT("Get status data\n");
860             DeviceStatus = IopGetDeviceNodeStatus(DeviceNode);
861             DeviceProblem = DeviceNode->Problem;
862             break;
863 
864         case PNP_SET_DEVICE_STATUS:
865             DPRINT1("Set status data is NOT SUPPORTED\n");
866             break;
867 
868         case PNP_CLEAR_DEVICE_STATUS:
869             DPRINT1("FIXME: Clear status data!\n");
870             break;
871     }
872 
873     ObDereferenceObject(DeviceObject);
874 
875     if (Operation == PNP_GET_DEVICE_STATUS)
876     {
877         _SEH2_TRY
878         {
879             StatusData->DeviceStatus = DeviceStatus;
880             StatusData->DeviceProblem = DeviceProblem;
881         }
882         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
883         {
884             Status = _SEH2_GetExceptionCode();
885         }
886         _SEH2_END;
887     }
888 
889     return Status;
890 }
891 
892 static
893 NTSTATUS
894 IopGetDeviceRelations(PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA RelationsData)
895 {
896     UNICODE_STRING DeviceInstance;
897     PDEVICE_OBJECT DeviceObject = NULL;
898     IO_STACK_LOCATION Stack;
899     IO_STATUS_BLOCK IoStatusBlock;
900     PDEVICE_RELATIONS DeviceRelations = NULL;
901     PDEVICE_OBJECT ChildDeviceObject;
902     PDEVICE_NODE ChildDeviceNode;
903     ULONG i;
904     ULONG Relations;
905     ULONG BufferSize, RequiredSize;
906     ULONG BufferLeft;
907     PWCHAR Buffer, Ptr;
908     NTSTATUS Status = STATUS_SUCCESS;
909 
910     DPRINT("IopGetDeviceRelations() called\n");
911     DPRINT("Device name: %wZ\n", &RelationsData->DeviceInstance);
912     DPRINT("Relations: %lu\n", RelationsData->Relations);
913     DPRINT("BufferSize: %lu\n", RelationsData->BufferSize);
914     DPRINT("Buffer: %p\n", RelationsData->Buffer);
915 
916     _SEH2_TRY
917     {
918         Relations = RelationsData->Relations;
919         BufferSize = RelationsData->BufferSize;
920         Buffer = RelationsData->Buffer;
921 
922         ProbeForWrite(Buffer, BufferSize, sizeof(CHAR));
923     }
924     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
925     {
926         _SEH2_YIELD(return _SEH2_GetExceptionCode());
927     }
928     _SEH2_END;
929 
930     Status = IopCaptureUnicodeString(&DeviceInstance, &RelationsData->DeviceInstance);
931     if (!NT_SUCCESS(Status))
932     {
933         DPRINT1("IopCaptureUnicodeString() failed (Status 0x%08lx)\n", Status);
934         return Status;
935     }
936 
937     /* Get the device object */
938     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
939     if (DeviceObject == NULL)
940     {
941         DPRINT1("IopGetDeviceObjectFromDeviceInstance() returned NULL\n");
942         Status = STATUS_NO_SUCH_DEVICE;
943         goto done;
944     }
945 
946     switch (Relations)
947     {
948         case PNP_EJECT_RELATIONS:
949             Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
950             break;
951 
952         case PNP_REMOVAL_RELATIONS:
953             Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
954             break;
955 
956         case PNP_POWER_RELATIONS:
957             Stack.Parameters.QueryDeviceRelations.Type = PowerRelations;
958             break;
959 
960         case PNP_BUS_RELATIONS:
961             Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
962             break;
963 
964         default:
965             Status = STATUS_INVALID_PARAMETER;
966             goto done;
967     }
968 
969     Status = IopInitiatePnpIrp(DeviceObject,
970                                &IoStatusBlock,
971                                IRP_MN_QUERY_DEVICE_RELATIONS,
972                                &Stack);
973     if (!NT_SUCCESS(Status))
974     {
975         DPRINT1("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
976         goto done;
977     }
978 
979     DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
980 
981     DPRINT("Found %d device relations\n", DeviceRelations->Count);
982 
983     _SEH2_TRY
984     {
985         RequiredSize = 0;
986         BufferLeft = BufferSize;
987         Ptr = Buffer;
988 
989         for (i = 0; i < DeviceRelations->Count; i++)
990         {
991             ChildDeviceObject = DeviceRelations->Objects[i];
992 
993             ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
994             if (ChildDeviceNode)
995             {
996                 DPRINT("Device instance: %wZ\n", &ChildDeviceNode->InstancePath);
997                 DPRINT("RequiredSize: %hu\n", ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
998 
999                 if (Ptr != NULL)
1000                 {
1001                     if (BufferLeft < ChildDeviceNode->InstancePath.Length + 2 * sizeof(WCHAR))
1002                     {
1003                         Status = STATUS_BUFFER_TOO_SMALL;
1004                         break;
1005                     }
1006 
1007                     RtlCopyMemory(Ptr,
1008                                   ChildDeviceNode->InstancePath.Buffer,
1009                                   ChildDeviceNode->InstancePath.Length);
1010                     Ptr = (PWCHAR)((ULONG_PTR)Ptr + ChildDeviceNode->InstancePath.Length);
1011                     *Ptr = UNICODE_NULL;
1012                     Ptr = (PWCHAR)((ULONG_PTR)Ptr + sizeof(WCHAR));
1013 
1014                     BufferLeft -= (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
1015                 }
1016 
1017                 RequiredSize += (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
1018             }
1019         }
1020 
1021         if (Ptr != NULL && BufferLeft >= sizeof(WCHAR))
1022             *Ptr = UNICODE_NULL;
1023 
1024         if (RequiredSize > 0)
1025             RequiredSize += sizeof(WCHAR);
1026 
1027         DPRINT("BufferSize: %lu  RequiredSize: %lu\n", RelationsData->BufferSize, RequiredSize);
1028 
1029         RelationsData->BufferSize = RequiredSize;
1030     }
1031     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1032     {
1033         Status = _SEH2_GetExceptionCode();
1034     }
1035     _SEH2_END;
1036 
1037 done:
1038     if (DeviceRelations != NULL)
1039         ExFreePool(DeviceRelations);
1040 
1041     if (DeviceObject != NULL)
1042         ObDereferenceObject(DeviceObject);
1043 
1044     if (DeviceInstance.Buffer != NULL)
1045         ExFreePool(DeviceInstance.Buffer);
1046 
1047     return Status;
1048 }
1049 
1050 static NTSTATUS
1051 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
1052 {
1053     PDEVICE_OBJECT DeviceObject;
1054     PDEVICE_NODE DeviceNode;
1055     UNICODE_STRING DeviceInstance;
1056     NTSTATUS Status = STATUS_SUCCESS;
1057 
1058     DPRINT("IopGetDeviceDepth() called\n");
1059     DPRINT("Device name: %wZ\n", &DepthData->DeviceInstance);
1060 
1061     Status = IopCaptureUnicodeString(&DeviceInstance, &DepthData->DeviceInstance);
1062     if (!NT_SUCCESS(Status))
1063     {
1064         return Status;
1065     }
1066 
1067     /* Get the device object */
1068     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1069     if (DeviceInstance.Buffer != NULL)
1070     {
1071         ExFreePool(DeviceInstance.Buffer);
1072     }
1073     if (DeviceObject == NULL)
1074     {
1075         return STATUS_NO_SUCH_DEVICE;
1076     }
1077 
1078     DeviceNode = IopGetDeviceNode(DeviceObject);
1079 
1080     _SEH2_TRY
1081     {
1082         DepthData->Depth = DeviceNode->Level;
1083     }
1084     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1085     {
1086         Status = _SEH2_GetExceptionCode();
1087     }
1088     _SEH2_END;
1089 
1090     ObDereferenceObject(DeviceObject);
1091 
1092     return Status;
1093 }
1094 
1095 static
1096 NTSTATUS
1097 PiControlSyncDeviceAction(
1098     _In_ PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA DeviceData,
1099     _In_ PLUGPLAY_CONTROL_CLASS ControlClass)
1100 {
1101     PDEVICE_OBJECT DeviceObject;
1102     NTSTATUS Status;
1103     UNICODE_STRING DeviceInstance;
1104 
1105     ASSERT(ControlClass == PlugPlayControlEnumerateDevice ||
1106            ControlClass == PlugPlayControlStartDevice ||
1107            ControlClass == PlugPlayControlResetDevice);
1108 
1109     Status = IopCaptureUnicodeString(&DeviceInstance, &DeviceData->DeviceInstance);
1110     if (!NT_SUCCESS(Status))
1111     {
1112         return Status;
1113     }
1114 
1115     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1116     if (DeviceInstance.Buffer != NULL)
1117     {
1118         ExFreePool(DeviceInstance.Buffer);
1119     }
1120     if (DeviceObject == NULL)
1121     {
1122         return STATUS_NO_SUCH_DEVICE;
1123     }
1124 
1125     DEVICE_ACTION Action;
1126 
1127     switch (ControlClass)
1128     {
1129         case PlugPlayControlEnumerateDevice:
1130             Action = PiActionEnumDeviceTree;
1131             break;
1132         case PlugPlayControlStartDevice:
1133             Action = PiActionStartDevice;
1134             break;
1135         case PlugPlayControlResetDevice:
1136             Action = PiActionResetDevice;
1137             break;
1138         default:
1139             UNREACHABLE;
1140             break;
1141     }
1142 
1143     Status = PiPerformSyncDeviceAction(DeviceObject, Action);
1144 
1145     ObDereferenceObject(DeviceObject);
1146 
1147     return Status;
1148 }
1149 
1150 static
1151 NTSTATUS
1152 PiControlQueryRemoveDevice(
1153     _In_ PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA ControlData)
1154 {
1155     PDEVICE_OBJECT DeviceObject;
1156     NTSTATUS Status;
1157     UNICODE_STRING DeviceInstance;
1158 
1159     Status = IopCaptureUnicodeString(&DeviceInstance, &ControlData->DeviceInstance);
1160     if (!NT_SUCCESS(Status))
1161     {
1162         return Status;
1163     }
1164 
1165     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1166     if (DeviceInstance.Buffer != NULL)
1167     {
1168         ExFreePool(DeviceInstance.Buffer);
1169     }
1170     if (DeviceObject == NULL)
1171     {
1172         return STATUS_NO_SUCH_DEVICE;
1173     }
1174 
1175     UNIMPLEMENTED;
1176     Status = STATUS_NOT_IMPLEMENTED;
1177 
1178     ObDereferenceObject(DeviceObject);
1179 
1180     return Status;
1181 }
1182 
1183 /* PUBLIC FUNCTIONS **********************************************************/
1184 
1185 /*
1186  * Plug and Play event structure used by NtGetPlugPlayEvent.
1187  *
1188  * EventGuid
1189  *    Can be one of the following values:
1190  *       GUID_HWPROFILE_QUERY_CHANGE
1191  *       GUID_HWPROFILE_CHANGE_CANCELLED
1192  *       GUID_HWPROFILE_CHANGE_COMPLETE
1193  *       GUID_TARGET_DEVICE_QUERY_REMOVE
1194  *       GUID_TARGET_DEVICE_REMOVE_CANCELLED
1195  *       GUID_TARGET_DEVICE_REMOVE_COMPLETE
1196  *       GUID_PNP_CUSTOM_NOTIFICATION
1197  *       GUID_PNP_POWER_NOTIFICATION
1198  *       GUID_DEVICE_* (see above)
1199  *
1200  * EventCategory
1201  *    Type of the event that happened.
1202  *
1203  * Result
1204  *    ?
1205  *
1206  * Flags
1207  *    ?
1208  *
1209  * TotalSize
1210  *    Size of the event block including the device IDs and other
1211  *    per category specific fields.
1212  */
1213 
1214 /*
1215  * NtGetPlugPlayEvent
1216  *
1217  * Returns one Plug & Play event from a global queue.
1218  *
1219  * Parameters
1220  *    Reserved1
1221  *    Reserved2
1222  *       Always set to zero.
1223  *
1224  *    Buffer
1225  *       The buffer that will be filled with the event information on
1226  *       successful return from the function.
1227  *
1228  *    BufferSize
1229  *       Size of the buffer pointed by the Buffer parameter. If the
1230  *       buffer size is not large enough to hold the whole event
1231  *       information, error STATUS_BUFFER_TOO_SMALL is returned and
1232  *       the buffer remains untouched.
1233  *
1234  * Return Values
1235  *    STATUS_PRIVILEGE_NOT_HELD
1236  *    STATUS_BUFFER_TOO_SMALL
1237  *    STATUS_SUCCESS
1238  *
1239  * Remarks
1240  *    This function isn't multi-thread safe!
1241  *
1242  * @implemented
1243  */
1244 NTSTATUS
1245 NTAPI
1246 NtGetPlugPlayEvent(IN ULONG Reserved1,
1247                    IN ULONG Reserved2,
1248                    OUT PPLUGPLAY_EVENT_BLOCK Buffer,
1249                    IN ULONG BufferSize)
1250 {
1251     PPNP_EVENT_ENTRY Entry;
1252     NTSTATUS Status;
1253 
1254     DPRINT("NtGetPlugPlayEvent() called\n");
1255 
1256     /* Function can only be called from user-mode */
1257     if (KeGetPreviousMode() == KernelMode)
1258     {
1259         DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
1260         return STATUS_ACCESS_DENIED;
1261     }
1262 
1263     /* Check for Tcb privilege */
1264     if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
1265                                 UserMode))
1266     {
1267         DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
1268         return STATUS_PRIVILEGE_NOT_HELD;
1269     }
1270 
1271     /* Wait for a PnP event */
1272     DPRINT("Waiting for pnp notification event\n");
1273     Status = KeWaitForSingleObject(&IopPnpNotifyEvent,
1274                                    UserRequest,
1275                                    UserMode,
1276                                    FALSE,
1277                                    NULL);
1278     if (!NT_SUCCESS(Status) || Status == STATUS_USER_APC)
1279     {
1280         DPRINT("KeWaitForSingleObject() failed (Status %lx)\n", Status);
1281         ASSERT(Status == STATUS_USER_APC);
1282         return Status;
1283     }
1284 
1285     /* Get entry from the tail of the queue */
1286     Entry = CONTAINING_RECORD(IopPnpEventQueueHead.Blink,
1287                               PNP_EVENT_ENTRY,
1288                               ListEntry);
1289 
1290     /* Check the buffer size */
1291     if (BufferSize < Entry->Event.TotalSize)
1292     {
1293         DPRINT1("Buffer is too small for the pnp-event\n");
1294         return STATUS_BUFFER_TOO_SMALL;
1295     }
1296 
1297     /* Copy event data to the user buffer */
1298     _SEH2_TRY
1299     {
1300         ProbeForWrite(Buffer,
1301                       Entry->Event.TotalSize,
1302                       sizeof(UCHAR));
1303         RtlCopyMemory(Buffer,
1304                       &Entry->Event,
1305                       Entry->Event.TotalSize);
1306     }
1307     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1308     {
1309         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1310     }
1311     _SEH2_END;
1312 
1313     DPRINT("NtGetPlugPlayEvent() done\n");
1314 
1315     return STATUS_SUCCESS;
1316 }
1317 
1318 /*
1319  * NtPlugPlayControl
1320  *
1321  * A function for doing various Plug & Play operations from user mode.
1322  *
1323  * Parameters
1324  *    PlugPlayControlClass
1325  *       0x00   Reenumerate device tree
1326  *
1327  *              Buffer points to UNICODE_STRING decribing the instance
1328  *              path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For
1329  *              more information about instance paths see !devnode command
1330  *              in kernel debugger or look at "Inside Windows 2000" book,
1331  *              chapter "Driver Loading, Initialization, and Installation".
1332  *
1333  *       0x01   Register new device
1334  *       0x02   Deregister device
1335  *       0x03   Initialize device
1336  *       0x04   Start device
1337  *       0x06   Query and remove device
1338  *       0x07   User response
1339  *
1340  *              Called after processing the message from NtGetPlugPlayEvent.
1341  *
1342  *       0x08   Generate legacy device
1343  *       0x09   Get interface device list
1344  *       0x0A   Get property data
1345  *       0x0B   Device class association (Registration)
1346  *       0x0C   Get related device
1347  *       0x0D   Get device interface alias
1348  *       0x0E   Get/set/clear device status
1349  *       0x0F   Get device depth
1350  *       0x10   Query device relations
1351  *       0x11   Query target device relation
1352  *       0x12   Query conflict list
1353  *       0x13   Retrieve dock data
1354  *       0x14   Reset device
1355  *       0x15   Halt device
1356  *       0x16   Get blocked driver data
1357  *
1358  *    Buffer
1359  *       The buffer contains information that is specific to each control
1360  *       code. The buffer is read-only.
1361  *
1362  *    BufferSize
1363  *       Size of the buffer pointed by the Buffer parameter. If the
1364  *       buffer size specifies incorrect value for specified control
1365  *       code, error ??? is returned.
1366  *
1367  * Return Values
1368  *    STATUS_PRIVILEGE_NOT_HELD
1369  *    STATUS_SUCCESS
1370  *    ...
1371  *
1372  * @unimplemented
1373  */
1374 NTSTATUS
1375 NTAPI
1376 NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
1377                   IN OUT PVOID Buffer,
1378                   IN ULONG BufferLength)
1379 {
1380     DPRINT("NtPlugPlayControl(%d %p %lu) called\n",
1381            PlugPlayControlClass, Buffer, BufferLength);
1382 
1383     /* Function can only be called from user-mode */
1384     if (KeGetPreviousMode() == KernelMode)
1385     {
1386         DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
1387         return STATUS_ACCESS_DENIED;
1388     }
1389 
1390     /* Check for Tcb privilege */
1391     if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
1392                                 UserMode))
1393     {
1394         DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
1395         return STATUS_PRIVILEGE_NOT_HELD;
1396     }
1397 
1398     /* Probe the buffer */
1399     _SEH2_TRY
1400     {
1401         ProbeForWrite(Buffer,
1402                       BufferLength,
1403                       sizeof(ULONG));
1404     }
1405     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1406     {
1407         _SEH2_YIELD(return _SEH2_GetExceptionCode());
1408     }
1409     _SEH2_END;
1410 
1411     switch (PlugPlayControlClass)
1412     {
1413         case PlugPlayControlEnumerateDevice:
1414             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_ENUMERATE_DEVICE_DATA))
1415                 return STATUS_INVALID_PARAMETER;
1416             // the Flags field is not used anyway
1417             return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer,
1418                                              PlugPlayControlClass);
1419 
1420 //        case PlugPlayControlRegisterNewDevice:
1421 //        case PlugPlayControlDeregisterDevice:
1422 
1423         case PlugPlayControlInitializeDevice:
1424             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA))
1425                 return STATUS_INVALID_PARAMETER;
1426             return IopInitializeDevice((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer);
1427 
1428         case PlugPlayControlStartDevice:
1429         case PlugPlayControlResetDevice:
1430             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_CONTROL_DATA))
1431                 return STATUS_INVALID_PARAMETER;
1432             return PiControlSyncDeviceAction((PPLUGPLAY_CONTROL_DEVICE_CONTROL_DATA)Buffer,
1433                                              PlugPlayControlClass);
1434 
1435 //        case PlugPlayControlUnlockDevice:
1436         case PlugPlayControlQueryAndRemoveDevice:
1437               if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_QUERY_REMOVE_DATA))
1438                   return STATUS_INVALID_PARAMETER;
1439               return PiControlQueryRemoveDevice((PPLUGPLAY_CONTROL_QUERY_REMOVE_DATA)Buffer);
1440 
1441         case PlugPlayControlUserResponse:
1442             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA))
1443                 return STATUS_INVALID_PARAMETER;
1444             return IopRemovePlugPlayEvent((PPLUGPLAY_CONTROL_USER_RESPONSE_DATA)Buffer);
1445 
1446 //        case PlugPlayControlGenerateLegacyDevice:
1447 
1448         case PlugPlayControlGetInterfaceDeviceList:
1449             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA))
1450                 return STATUS_INVALID_PARAMETER;
1451             return IopGetInterfaceDeviceList((PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA)Buffer);
1452 
1453         case PlugPlayControlProperty:
1454             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
1455                 return STATUS_INVALID_PARAMETER;
1456             return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer);
1457 
1458 //        case PlugPlayControlDeviceClassAssociation:
1459 
1460         case PlugPlayControlGetRelatedDevice:
1461             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
1462                 return STATUS_INVALID_PARAMETER;
1463             return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer);
1464 
1465 //        case PlugPlayControlGetInterfaceDeviceAlias:
1466 
1467         case PlugPlayControlDeviceStatus:
1468             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
1469                 return STATUS_INVALID_PARAMETER;
1470             return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer);
1471 
1472         case PlugPlayControlGetDeviceDepth:
1473             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA))
1474                 return STATUS_INVALID_PARAMETER;
1475             return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer);
1476 
1477         case PlugPlayControlQueryDeviceRelations:
1478             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA))
1479                 return STATUS_INVALID_PARAMETER;
1480             return IopGetDeviceRelations((PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA)Buffer);
1481 
1482 //        case PlugPlayControlTargetDeviceRelation:
1483 //        case PlugPlayControlQueryConflictList:
1484 //        case PlugPlayControlRetrieveDock:
1485 //        case PlugPlayControlHaltDevice:
1486 //        case PlugPlayControlGetBlockedDriverList:
1487 
1488         default:
1489             return STATUS_NOT_IMPLEMENTED;
1490     }
1491 
1492     return STATUS_NOT_IMPLEMENTED;
1493 }
1494