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