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