xref: /reactos/ntoskrnl/io/pnpmgr/plugplay.c (revision 8da0f868)
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 ULONG
693 IopGetDeviceNodeStatus(PDEVICE_NODE DeviceNode)
694 {
695     ULONG Output = 0;
696 
697     if (DeviceNode->Parent == IopRootDeviceNode)
698         Output |= DN_ROOT_ENUMERATED;
699 
700     if (DeviceNode->Flags & DNF_ADDED)
701         Output |= DN_DRIVER_LOADED;
702 
703     /* FIXME: DN_ENUM_LOADED */
704 
705     if (DeviceNode->Flags & DNF_STARTED)
706         Output |= DN_STARTED;
707 
708     /* FIXME: Manual */
709 
710     if (!(DeviceNode->Flags & DNF_PROCESSED))
711         Output |= DN_NEED_TO_ENUM;
712 
713     /* DN_NOT_FIRST_TIME is 9x only */
714 
715     /* FIXME: DN_HARDWARE_ENUM */
716 
717     /* DN_LIAR and DN_HAS_MARK are 9x only */
718 
719     if (DeviceNode->Problem != 0)
720         Output |= DN_HAS_PROBLEM;
721 
722     /* FIXME: DN_FILTERED */
723 
724     if (DeviceNode->Flags & DNF_LEGACY_DRIVER)
725         Output |= DN_LEGACY_DRIVER;
726 
727     if (DeviceNode->UserFlags & DNUF_DONT_SHOW_IN_UI)
728         Output |= DN_NO_SHOW_IN_DM;
729 
730     if (!(DeviceNode->UserFlags & DNUF_NOT_DISABLEABLE))
731         Output |= DN_DISABLEABLE;
732 
733     /* FIXME: Implement the rest */
734 
735     Output |= DN_NT_ENUMERATOR | DN_NT_DRIVER;
736 
737     return Output;
738 }
739 
740 static NTSTATUS
741 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
742 {
743     PDEVICE_OBJECT DeviceObject;
744     PDEVICE_NODE DeviceNode;
745     ULONG Operation = 0;
746     ULONG DeviceStatus = 0;
747     ULONG DeviceProblem = 0;
748     UNICODE_STRING DeviceInstance;
749     NTSTATUS Status;
750 
751     DPRINT("IopDeviceStatus() called\n");
752 
753     Status = IopCaptureUnicodeString(&DeviceInstance, &StatusData->DeviceInstance);
754     if (!NT_SUCCESS(Status))
755     {
756         return Status;
757     }
758 
759     DPRINT("Device name: '%wZ'\n", &DeviceInstance);
760 
761     _SEH2_TRY
762     {
763         Operation = StatusData->Operation;
764         if (Operation == PNP_SET_DEVICE_STATUS)
765         {
766             DeviceStatus = StatusData->DeviceStatus;
767             DeviceProblem = StatusData->DeviceProblem;
768         }
769     }
770     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
771     {
772         if (DeviceInstance.Buffer != NULL)
773         {
774             ExFreePool(DeviceInstance.Buffer);
775         }
776         _SEH2_YIELD(return _SEH2_GetExceptionCode());
777     }
778     _SEH2_END;
779 
780     /* Get the device object */
781     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
782     if (DeviceInstance.Buffer != NULL)
783     {
784         ExFreePool(DeviceInstance.Buffer);
785     }
786     if (DeviceObject == NULL)
787     {
788         return STATUS_NO_SUCH_DEVICE;
789     }
790 
791     DeviceNode = IopGetDeviceNode(DeviceObject);
792 
793     switch (Operation)
794     {
795         case PNP_GET_DEVICE_STATUS:
796             DPRINT("Get status data\n");
797             DeviceStatus = IopGetDeviceNodeStatus(DeviceNode);
798             DeviceProblem = DeviceNode->Problem;
799             break;
800 
801         case PNP_SET_DEVICE_STATUS:
802             DPRINT1("Set status data is NOT SUPPORTED\n");
803             break;
804 
805         case PNP_CLEAR_DEVICE_STATUS:
806             DPRINT1("FIXME: Clear status data!\n");
807             break;
808     }
809 
810     ObDereferenceObject(DeviceObject);
811 
812     if (Operation == PNP_GET_DEVICE_STATUS)
813     {
814         _SEH2_TRY
815         {
816             StatusData->DeviceStatus = DeviceStatus;
817             StatusData->DeviceProblem = DeviceProblem;
818         }
819         _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
820         {
821             Status = _SEH2_GetExceptionCode();
822         }
823         _SEH2_END;
824     }
825 
826     return Status;
827 }
828 
829 static
830 NTSTATUS
831 IopGetDeviceRelations(PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA RelationsData)
832 {
833     UNICODE_STRING DeviceInstance;
834     PDEVICE_OBJECT DeviceObject = NULL;
835     IO_STACK_LOCATION Stack;
836     IO_STATUS_BLOCK IoStatusBlock;
837     PDEVICE_RELATIONS DeviceRelations = NULL;
838     PDEVICE_OBJECT ChildDeviceObject;
839     PDEVICE_NODE ChildDeviceNode;
840     ULONG i;
841     ULONG Relations;
842     ULONG BufferSize, RequiredSize;
843     ULONG BufferLeft;
844     PWCHAR Buffer, Ptr;
845     NTSTATUS Status = STATUS_SUCCESS;
846 
847     DPRINT("IopGetDeviceRelations() called\n");
848     DPRINT("Device name: %wZ\n", &RelationsData->DeviceInstance);
849     DPRINT("Relations: %lu\n", RelationsData->Relations);
850     DPRINT("BufferSize: %lu\n", RelationsData->BufferSize);
851     DPRINT("Buffer: %p\n", RelationsData->Buffer);
852 
853     _SEH2_TRY
854     {
855         Relations = RelationsData->Relations;
856         BufferSize = RelationsData->BufferSize;
857         Buffer = RelationsData->Buffer;
858 
859         ProbeForWrite(Buffer, BufferSize, sizeof(CHAR));
860     }
861     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
862     {
863         _SEH2_YIELD(return _SEH2_GetExceptionCode());
864     }
865     _SEH2_END;
866 
867     Status = IopCaptureUnicodeString(&DeviceInstance, &RelationsData->DeviceInstance);
868     if (!NT_SUCCESS(Status))
869     {
870         DPRINT1("IopCaptureUnicodeString() failed (Status 0x%08lx)\n", Status);
871         return Status;
872     }
873 
874     /* Get the device object */
875     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
876     if (DeviceObject == NULL)
877     {
878         DPRINT1("IopGetDeviceObjectFromDeviceInstance() returned NULL\n");
879         Status = STATUS_NO_SUCH_DEVICE;
880         goto done;
881     }
882 
883     switch (Relations)
884     {
885         case PNP_EJECT_RELATIONS:
886             Stack.Parameters.QueryDeviceRelations.Type = EjectionRelations;
887             break;
888 
889         case PNP_REMOVAL_RELATIONS:
890             Stack.Parameters.QueryDeviceRelations.Type = RemovalRelations;
891             break;
892 
893         case PNP_POWER_RELATIONS:
894             Stack.Parameters.QueryDeviceRelations.Type = PowerRelations;
895             break;
896 
897         case PNP_BUS_RELATIONS:
898             Stack.Parameters.QueryDeviceRelations.Type = BusRelations;
899             break;
900 
901         default:
902             Status = STATUS_INVALID_PARAMETER;
903             goto done;
904     }
905 
906     Status = IopInitiatePnpIrp(DeviceObject,
907                                &IoStatusBlock,
908                                IRP_MN_QUERY_DEVICE_RELATIONS,
909                                &Stack);
910     if (!NT_SUCCESS(Status))
911     {
912         DPRINT1("IopInitiatePnpIrp() failed (Status 0x%08lx)\n", Status);
913         goto done;
914     }
915 
916     DeviceRelations = (PDEVICE_RELATIONS)IoStatusBlock.Information;
917 
918     DPRINT("Found %d device relations\n", DeviceRelations->Count);
919 
920     _SEH2_TRY
921     {
922         RequiredSize = 0;
923         BufferLeft = BufferSize;
924         Ptr = Buffer;
925 
926         for (i = 0; i < DeviceRelations->Count; i++)
927         {
928             ChildDeviceObject = DeviceRelations->Objects[i];
929 
930             ChildDeviceNode = IopGetDeviceNode(ChildDeviceObject);
931             if (ChildDeviceNode)
932             {
933                 DPRINT("Device instance: %wZ\n", &ChildDeviceNode->InstancePath);
934                 DPRINT("RequiredSize: %hu\n", ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
935 
936                 if (Ptr != NULL)
937                 {
938                     if (BufferLeft < ChildDeviceNode->InstancePath.Length + 2 * sizeof(WCHAR))
939                     {
940                         Status = STATUS_BUFFER_TOO_SMALL;
941                         break;
942                     }
943 
944                     RtlCopyMemory(Ptr,
945                                   ChildDeviceNode->InstancePath.Buffer,
946                                   ChildDeviceNode->InstancePath.Length);
947                     Ptr = (PWCHAR)((ULONG_PTR)Ptr + ChildDeviceNode->InstancePath.Length);
948                     *Ptr = UNICODE_NULL;
949                     Ptr = (PWCHAR)((ULONG_PTR)Ptr + sizeof(WCHAR));
950 
951                     BufferLeft -= (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
952                 }
953 
954                 RequiredSize += (ChildDeviceNode->InstancePath.Length + sizeof(WCHAR));
955             }
956         }
957 
958         if (Ptr != NULL && BufferLeft >= sizeof(WCHAR))
959             *Ptr = UNICODE_NULL;
960 
961         if (RequiredSize > 0)
962             RequiredSize += sizeof(WCHAR);
963 
964         DPRINT("BufferSize: %lu  RequiredSize: %lu\n", RelationsData->BufferSize, RequiredSize);
965 
966         RelationsData->BufferSize = RequiredSize;
967     }
968     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
969     {
970         Status = _SEH2_GetExceptionCode();
971     }
972     _SEH2_END;
973 
974 done:
975     if (DeviceRelations != NULL)
976         ExFreePool(DeviceRelations);
977 
978     if (DeviceObject != NULL)
979         ObDereferenceObject(DeviceObject);
980 
981     if (DeviceInstance.Buffer != NULL)
982         ExFreePool(DeviceInstance.Buffer);
983 
984     return Status;
985 }
986 
987 static NTSTATUS
988 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
989 {
990     PDEVICE_OBJECT DeviceObject;
991     PDEVICE_NODE DeviceNode;
992     UNICODE_STRING DeviceInstance;
993     NTSTATUS Status = STATUS_SUCCESS;
994 
995     DPRINT("IopGetDeviceDepth() called\n");
996     DPRINT("Device name: %wZ\n", &DepthData->DeviceInstance);
997 
998     Status = IopCaptureUnicodeString(&DeviceInstance, &DepthData->DeviceInstance);
999     if (!NT_SUCCESS(Status))
1000     {
1001         return Status;
1002     }
1003 
1004     /* Get the device object */
1005     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1006     if (DeviceInstance.Buffer != NULL)
1007     {
1008         ExFreePool(DeviceInstance.Buffer);
1009     }
1010     if (DeviceObject == NULL)
1011     {
1012         return STATUS_NO_SUCH_DEVICE;
1013     }
1014 
1015     DeviceNode = IopGetDeviceNode(DeviceObject);
1016 
1017     _SEH2_TRY
1018     {
1019         DepthData->Depth = DeviceNode->Level;
1020     }
1021     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1022     {
1023         Status = _SEH2_GetExceptionCode();
1024     }
1025     _SEH2_END;
1026 
1027     ObDereferenceObject(DeviceObject);
1028 
1029     return Status;
1030 }
1031 
1032 
1033 static NTSTATUS
1034 IopResetDevice(PPLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData)
1035 {
1036     PDEVICE_OBJECT DeviceObject;
1037     NTSTATUS Status;
1038     UNICODE_STRING DeviceInstance;
1039 
1040     Status = IopCaptureUnicodeString(&DeviceInstance, &ResetDeviceData->DeviceInstance);
1041     if (!NT_SUCCESS(Status))
1042     {
1043         return Status;
1044     }
1045 
1046     DPRINT("IopResetDevice(%wZ)\n", &DeviceInstance);
1047 
1048     /* Get the device object */
1049     DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
1050     if (DeviceInstance.Buffer != NULL)
1051     {
1052         ExFreePool(DeviceInstance.Buffer);
1053     }
1054     if (DeviceObject == NULL)
1055     {
1056         return STATUS_NO_SUCH_DEVICE;
1057     }
1058 
1059     Status = PiPerformSyncDeviceAction(DeviceObject, PiActionResetDevice);
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             return IopPnpEnumerateDevice((PPLUGPLAY_CONTROL_ENUMERATE_DEVICE_DATA)Buffer);
1300 
1301 //        case PlugPlayControlRegisterNewDevice:
1302 //        case PlugPlayControlDeregisterDevice:
1303 //        case PlugPlayControlInitializeDevice:
1304 //        case PlugPlayControlStartDevice:
1305 //        case PlugPlayControlUnlockDevice:
1306 //        case PlugPlayControlQueryAndRemoveDevice:
1307 
1308         case PlugPlayControlUserResponse:
1309             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_USER_RESPONSE_DATA))
1310                 return STATUS_INVALID_PARAMETER;
1311             return IopRemovePlugPlayEvent((PPLUGPLAY_CONTROL_USER_RESPONSE_DATA)Buffer);
1312 
1313 //        case PlugPlayControlGenerateLegacyDevice:
1314 
1315         case PlugPlayControlGetInterfaceDeviceList:
1316             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA))
1317                 return STATUS_INVALID_PARAMETER;
1318             return IopGetInterfaceDeviceList((PPLUGPLAY_CONTROL_INTERFACE_DEVICE_LIST_DATA)Buffer);
1319 
1320         case PlugPlayControlProperty:
1321             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
1322                 return STATUS_INVALID_PARAMETER;
1323             return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer);
1324 
1325 //        case PlugPlayControlDeviceClassAssociation:
1326 
1327         case PlugPlayControlGetRelatedDevice:
1328             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
1329                 return STATUS_INVALID_PARAMETER;
1330             return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer);
1331 
1332 //        case PlugPlayControlGetInterfaceDeviceAlias:
1333 
1334         case PlugPlayControlDeviceStatus:
1335             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
1336                 return STATUS_INVALID_PARAMETER;
1337             return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer);
1338 
1339         case PlugPlayControlGetDeviceDepth:
1340             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA))
1341                 return STATUS_INVALID_PARAMETER;
1342             return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer);
1343 
1344         case PlugPlayControlQueryDeviceRelations:
1345             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA))
1346                 return STATUS_INVALID_PARAMETER;
1347             return IopGetDeviceRelations((PPLUGPLAY_CONTROL_DEVICE_RELATIONS_DATA)Buffer);
1348 
1349 //        case PlugPlayControlTargetDeviceRelation:
1350 //        case PlugPlayControlQueryConflictList:
1351 //        case PlugPlayControlRetrieveDock:
1352 
1353         case PlugPlayControlResetDevice:
1354             if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA))
1355                 return STATUS_INVALID_PARAMETER;
1356             return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA)Buffer);
1357 
1358 //        case PlugPlayControlHaltDevice:
1359 //        case PlugPlayControlGetBlockedDriverList:
1360 
1361         default:
1362             return STATUS_NOT_IMPLEMENTED;
1363     }
1364 
1365     return STATUS_NOT_IMPLEMENTED;
1366 }
1367