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