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