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