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