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