f25da19ba91f5e4c33776e7207d6a38f1486fe25
[reactos.git] / reactos / ntoskrnl / io / plugplay.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/io/plugplay.c
5 * PURPOSE: Plug-and-play interface routines
6 *
7 * PROGRAMMERS: Eric Kohl <eric.kohl@t-online.de>
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include <ntoskrnl.h>
13
14 #define NDEBUG
15 #include <internal/debug.h>
16
17
18 typedef struct _PNP_EVENT_ENTRY
19 {
20 LIST_ENTRY ListEntry;
21 PLUGPLAY_EVENT_BLOCK Event;
22 } PNP_EVENT_ENTRY, *PPNP_EVENT_ENTRY;
23
24
25 /* GLOBALS *******************************************************************/
26
27 static LIST_ENTRY IopPnpEventQueueHead;
28 static KEVENT IopPnpNotifyEvent;
29
30 /* FUNCTIONS *****************************************************************/
31
32 NTSTATUS INIT_FUNCTION
33 IopInitPlugPlayEvents(VOID)
34 {
35 InitializeListHead(&IopPnpEventQueueHead);
36
37 KeInitializeEvent(&IopPnpNotifyEvent,
38 SynchronizationEvent,
39 FALSE);
40
41 return STATUS_SUCCESS;
42 }
43
44
45 NTSTATUS
46 IopQueueTargetDeviceEvent(const GUID *Guid,
47 PUNICODE_STRING DeviceIds)
48 {
49 PPNP_EVENT_ENTRY EventEntry;
50 DWORD TotalSize;
51
52 TotalSize =
53 FIELD_OFFSET(PLUGPLAY_EVENT_BLOCK, TargetDevice.DeviceIds) +
54 DeviceIds->MaximumLength;
55
56 EventEntry = ExAllocatePool(NonPagedPool,
57 TotalSize + FIELD_OFFSET(PNP_EVENT_ENTRY, Event));
58 if (EventEntry == NULL)
59 return STATUS_INSUFFICIENT_RESOURCES;
60
61 memcpy(&EventEntry->Event.EventGuid,
62 Guid,
63 sizeof(GUID));
64 EventEntry->Event.EventCategory = TargetDeviceChangeEvent;
65 EventEntry->Event.TotalSize = TotalSize;
66
67 memcpy(&EventEntry->Event.TargetDevice.DeviceIds,
68 DeviceIds->Buffer,
69 DeviceIds->MaximumLength);
70
71 InsertHeadList(&IopPnpEventQueueHead,
72 &EventEntry->ListEntry);
73 KeSetEvent(&IopPnpNotifyEvent,
74 0,
75 FALSE);
76
77 return STATUS_SUCCESS;
78 }
79
80
81 /*
82 * Remove the current PnP event from the tail of the event queue
83 * and signal IopPnpNotifyEvent if there is yet another event in the queue.
84 */
85 static NTSTATUS
86 IopRemovePlugPlayEvent(VOID)
87 {
88 /* Remove a pnp event entry from the tail of the queue */
89 if (!IsListEmpty(&IopPnpEventQueueHead))
90 {
91 ExFreePool(RemoveTailList(&IopPnpEventQueueHead));
92 }
93
94 /* Signal the next pnp event in the queue */
95 if (!IsListEmpty(&IopPnpEventQueueHead))
96 {
97 KeSetEvent(&IopPnpNotifyEvent,
98 0,
99 FALSE);
100 }
101
102 return STATUS_SUCCESS;
103 }
104
105
106 /*
107 * Plug and Play event structure used by NtGetPlugPlayEvent.
108 *
109 * EventGuid
110 * Can be one of the following values:
111 * GUID_HWPROFILE_QUERY_CHANGE
112 * GUID_HWPROFILE_CHANGE_CANCELLED
113 * GUID_HWPROFILE_CHANGE_COMPLETE
114 * GUID_TARGET_DEVICE_QUERY_REMOVE
115 * GUID_TARGET_DEVICE_REMOVE_CANCELLED
116 * GUID_TARGET_DEVICE_REMOVE_COMPLETE
117 * GUID_PNP_CUSTOM_NOTIFICATION
118 * GUID_PNP_POWER_NOTIFICATION
119 * GUID_DEVICE_* (see above)
120 *
121 * EventCategory
122 * Type of the event that happened.
123 *
124 * Result
125 * ?
126 *
127 * Flags
128 * ?
129 *
130 * TotalSize
131 * Size of the event block including the device IDs and other
132 * per category specific fields.
133 */
134 /*
135 * NtGetPlugPlayEvent
136 *
137 * Returns one Plug & Play event from a global queue.
138 *
139 * Parameters
140 * Reserved1
141 * Reserved2
142 * Always set to zero.
143 *
144 * Buffer
145 * The buffer that will be filled with the event information on
146 * successful return from the function.
147 *
148 * BufferSize
149 * Size of the buffer pointed by the Buffer parameter. If the
150 * buffer size is not large enough to hold the whole event
151 * information, error STATUS_BUFFER_TOO_SMALL is returned and
152 * the buffer remains untouched.
153 *
154 * Return Values
155 * STATUS_PRIVILEGE_NOT_HELD
156 * STATUS_BUFFER_TOO_SMALL
157 * STATUS_SUCCESS
158 *
159 * Remarks
160 * This function isn't multi-thread safe!
161 *
162 * @implemented
163 */
164 NTSTATUS STDCALL
165 NtGetPlugPlayEvent(IN ULONG Reserved1,
166 IN ULONG Reserved2,
167 OUT PPLUGPLAY_EVENT_BLOCK Buffer,
168 IN ULONG BufferSize)
169 {
170 PPNP_EVENT_ENTRY Entry;
171 NTSTATUS Status;
172
173 DPRINT("NtGetPlugPlayEvent() called\n");
174
175 /* Function can only be called from user-mode */
176 if (KeGetPreviousMode() == KernelMode)
177 {
178 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
179 return STATUS_ACCESS_DENIED;
180 }
181
182 /* Check for Tcb privilege */
183 if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
184 UserMode))
185 {
186 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
187 return STATUS_PRIVILEGE_NOT_HELD;
188 }
189
190 /* Wait for a PnP event */
191 DPRINT("Waiting for pnp notification event\n");
192 Status = KeWaitForSingleObject(&IopPnpNotifyEvent,
193 UserRequest,
194 KernelMode,
195 FALSE,
196 NULL);
197 if (!NT_SUCCESS(Status))
198 {
199 DPRINT1("KeWaitForSingleObject() failed (Status %lx)\n", Status);
200 return Status;
201 }
202
203 /* Get entry from the tail of the queue */
204 Entry = CONTAINING_RECORD(IopPnpEventQueueHead.Blink,
205 PNP_EVENT_ENTRY,
206 ListEntry);
207
208 /* Check the buffer size */
209 if (BufferSize < Entry->Event.TotalSize)
210 {
211 DPRINT1("Buffer is too small for the pnp-event\n");
212 return STATUS_BUFFER_TOO_SMALL;
213 }
214
215 /* Copy event data to the user buffer */
216 memcpy(Buffer,
217 &Entry->Event,
218 Entry->Event.TotalSize);
219
220 DPRINT("NtGetPlugPlayEvent() done\n");
221
222 return STATUS_SUCCESS;
223 }
224
225
226 static PDEVICE_OBJECT
227 IopTraverseDeviceNode(PDEVICE_NODE Node, PUNICODE_STRING DeviceInstance)
228 {
229 PDEVICE_OBJECT DeviceObject;
230 PDEVICE_NODE ChildNode;
231
232 if (RtlEqualUnicodeString(&Node->InstancePath,
233 DeviceInstance, TRUE))
234 {
235 ObReferenceObject(Node->PhysicalDeviceObject);
236 return Node->PhysicalDeviceObject;
237 }
238
239 /* Traversal of all children nodes */
240 for (ChildNode = Node->Child;
241 ChildNode != NULL;
242 ChildNode = ChildNode->NextSibling)
243 {
244 DeviceObject = IopTraverseDeviceNode(ChildNode, DeviceInstance);
245 if (DeviceObject != NULL)
246 {
247 return DeviceObject;
248 }
249 }
250
251 return NULL;
252 }
253
254
255 static PDEVICE_OBJECT
256 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)
257 {
258 #if 0
259 OBJECT_ATTRIBUTES ObjectAttributes;
260 UNICODE_STRING KeyName, ValueName;
261 LPWSTR KeyNameBuffer;
262 HANDLE InstanceKeyHandle;
263 HANDLE ControlKeyHandle;
264 NTSTATUS Status;
265 PKEY_VALUE_PARTIAL_INFORMATION ValueInformation;
266 ULONG ValueInformationLength;
267 PDEVICE_OBJECT DeviceObject = NULL;
268
269 DPRINT("IopGetDeviceObjectFromDeviceInstance(%wZ) called\n", DeviceInstance);
270
271 KeyNameBuffer = ExAllocatePool(PagedPool,
272 (49 * sizeof(WCHAR)) + DeviceInstance->Length);
273 if (KeyNameBuffer == NULL)
274 {
275 DPRINT1("Failed to allocate key name buffer!\n");
276 return NULL;
277 }
278
279 wcscpy(KeyNameBuffer, L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\");
280 wcscat(KeyNameBuffer, DeviceInstance->Buffer);
281
282 RtlInitUnicodeString(&KeyName,
283 KeyNameBuffer);
284 InitializeObjectAttributes(&ObjectAttributes,
285 &KeyName,
286 OBJ_CASE_INSENSITIVE,
287 NULL,
288 NULL);
289
290 Status = ZwOpenKey(&InstanceKeyHandle,
291 KEY_READ,
292 &ObjectAttributes);
293 ExFreePool(KeyNameBuffer);
294 if (!NT_SUCCESS(Status))
295 {
296 DPRINT1("Failed to open the instance key (Status %lx)\n", Status);
297 return NULL;
298 }
299
300 /* Open the 'Control' subkey */
301 RtlInitUnicodeString(&KeyName,
302 L"Control");
303 InitializeObjectAttributes(&ObjectAttributes,
304 &KeyName,
305 OBJ_CASE_INSENSITIVE,
306 InstanceKeyHandle,
307 NULL);
308
309 Status = ZwOpenKey(&ControlKeyHandle,
310 KEY_READ,
311 &ObjectAttributes);
312 ZwClose(InstanceKeyHandle);
313 if (!NT_SUCCESS(Status))
314 {
315 DPRINT1("Failed to open the 'Control' key (Status %lx)\n", Status);
316 return NULL;
317 }
318
319 /* Query the 'DeviceReference' value */
320 RtlInitUnicodeString(&ValueName,
321 L"DeviceReference");
322 ValueInformationLength = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,
323 Data[0]) + sizeof(ULONG);
324 ValueInformation = ExAllocatePool(PagedPool, ValueInformationLength);
325 if (ValueInformation == NULL)
326 {
327 DPRINT1("Failed to allocate the name information buffer!\n");
328 ZwClose(ControlKeyHandle);
329 return NULL;
330 }
331
332 Status = ZwQueryValueKey(ControlKeyHandle,
333 &ValueName,
334 KeyValuePartialInformation,
335 ValueInformation,
336 ValueInformationLength,
337 &ValueInformationLength);
338 ZwClose(ControlKeyHandle);
339 if (!NT_SUCCESS(Status))
340 {
341 DPRINT1("Failed to query the 'DeviceReference' value (Status %lx)\n", Status);
342 return NULL;
343 }
344
345 /* Check the device object */
346 RtlCopyMemory(&DeviceObject,
347 ValueInformation->Data,
348 sizeof(PDEVICE_OBJECT));
349
350 DPRINT("DeviceObject: %p\n", DeviceObject);
351
352 if (DeviceObject->Type != IO_TYPE_DEVICE ||
353 DeviceObject->DeviceObjectExtension == NULL ||
354 DeviceObject->DeviceObjectExtension->DeviceNode == NULL ||
355 !RtlEqualUnicodeString(&DeviceObject->DeviceObjectExtension->DeviceNode->InstancePath,
356 DeviceInstance, TRUE))
357 {
358 DPRINT1("Invalid object type!\n");
359 return NULL;
360 }
361
362 DPRINT("Instance path: %wZ\n", &DeviceObject->DeviceObjectExtension->DeviceNode->InstancePath);
363
364 ObReferenceObject(DeviceObject);
365
366 DPRINT("IopGetDeviceObjectFromDeviceInstance() done\n");
367
368 return DeviceObject;
369 #endif
370
371 if (IopRootDeviceNode == NULL)
372 return NULL;
373
374 if (DeviceInstance == NULL ||
375 DeviceInstance->Length == 0
376 )
377 {
378 if (IopRootDeviceNode->PhysicalDeviceObject)
379 {
380 ObReferenceObject(IopRootDeviceNode->PhysicalDeviceObject);
381 return IopRootDeviceNode->PhysicalDeviceObject;
382 }
383 else
384 return NULL;
385 }
386
387 return IopTraverseDeviceNode(IopRootDeviceNode, DeviceInstance);
388
389 }
390
391
392
393 static NTSTATUS
394 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
395 {
396 PDEVICE_OBJECT DeviceObject = NULL;
397 NTSTATUS Status;
398
399 DPRINT("IopGetDeviceProperty() called\n");
400 DPRINT("Device name: %wZ\n", &PropertyData->DeviceInstance);
401
402 /* Get the device object */
403 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&PropertyData->DeviceInstance);
404 if (DeviceObject == NULL)
405 return STATUS_NO_SUCH_DEVICE;
406
407 Status = IoGetDeviceProperty(DeviceObject,
408 PropertyData->Property,
409 PropertyData->BufferSize,
410 PropertyData->Buffer,
411 &PropertyData->BufferSize);
412
413 ObDereferenceObject(DeviceObject);
414
415 return Status;
416 }
417
418
419 static NTSTATUS
420 IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)
421 {
422 UNICODE_STRING RootDeviceName;
423 PDEVICE_OBJECT DeviceObject = NULL;
424 PDEVICE_NODE DeviceNode = NULL;
425 PDEVICE_NODE RelatedDeviceNode;
426
427 DPRINT("IopGetRelatedDevice() called\n");
428 DPRINT("Device name: %wZ\n", &RelatedDeviceData->TargetDeviceInstance);
429
430 RtlInitUnicodeString(&RootDeviceName,
431 L"HTREE\\ROOT\\0");
432 if (RtlEqualUnicodeString(&RelatedDeviceData->TargetDeviceInstance,
433 &RootDeviceName,
434 TRUE))
435 {
436 DeviceNode = IopRootDeviceNode;
437 }
438 else
439 {
440 /* Get the device object */
441 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&RelatedDeviceData->TargetDeviceInstance);
442 if (DeviceObject == NULL)
443 return STATUS_NO_SUCH_DEVICE;
444
445 DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
446 }
447
448 switch (RelatedDeviceData->Relation)
449 {
450 case PNP_GET_PARENT_DEVICE:
451 RelatedDeviceNode = DeviceNode->Parent;
452 break;
453
454 case PNP_GET_CHILD_DEVICE:
455 RelatedDeviceNode = DeviceNode->Child;
456 break;
457
458 case PNP_GET_SIBLING_DEVICE:
459 RelatedDeviceNode = DeviceNode->NextSibling;
460 break;
461
462 default:
463 if (DeviceObject != NULL)
464 {
465 ObDereferenceObject(DeviceObject);
466 }
467
468 return STATUS_INVALID_PARAMETER;
469 }
470
471 if (RelatedDeviceNode == NULL)
472 {
473 if (DeviceObject)
474 {
475 ObDereferenceObject(DeviceObject);
476 }
477
478 return STATUS_NO_SUCH_DEVICE;
479 }
480
481 if (RelatedDeviceNode->InstancePath.Length >
482 RelatedDeviceData->RelatedDeviceInstance.MaximumLength)
483 {
484 if (DeviceObject)
485 {
486 ObDereferenceObject(DeviceObject);
487 }
488
489 return STATUS_BUFFER_TOO_SMALL;
490 }
491
492 /* Copy related device instance name */
493 RtlCopyMemory(RelatedDeviceData->RelatedDeviceInstance.Buffer,
494 RelatedDeviceNode->InstancePath.Buffer,
495 RelatedDeviceNode->InstancePath.Length);
496 RelatedDeviceData->RelatedDeviceInstance.Length =
497 RelatedDeviceNode->InstancePath.Length;
498
499 if (DeviceObject != NULL)
500 {
501 ObDereferenceObject(DeviceObject);
502 }
503
504 DPRINT("IopGetRelatedDevice() done\n");
505
506 return STATUS_SUCCESS;
507 }
508
509
510 static NTSTATUS
511 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
512 {
513 PDEVICE_OBJECT DeviceObject;
514 PDEVICE_NODE DeviceNode;
515
516 DPRINT("IopDeviceStatus() called\n");
517 DPRINT("Device name: %wZ\n", &StatusData->DeviceInstance);
518
519 /* Get the device object */
520 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&StatusData->DeviceInstance);
521 if (DeviceObject == NULL)
522 return STATUS_NO_SUCH_DEVICE;
523
524 DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
525
526 switch (StatusData->Operation)
527 {
528 case PNP_GET_DEVICE_STATUS:
529 DPRINT("Get status data\n");
530 StatusData->DeviceStatus = DeviceNode->Flags;
531 StatusData->DeviceProblem = DeviceNode->Problem;
532 break;
533
534 case PNP_SET_DEVICE_STATUS:
535 DPRINT("Set status data\n");
536 DeviceNode->Flags = StatusData->DeviceStatus;
537 DeviceNode->Problem = StatusData->DeviceProblem;
538 break;
539
540 case PNP_CLEAR_DEVICE_STATUS:
541 DPRINT1("FIXME: Clear status data!\n");
542 break;
543 }
544
545 ObDereferenceObject(DeviceObject);
546
547 return STATUS_SUCCESS;
548 }
549
550
551 static NTSTATUS
552 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
553 {
554 PDEVICE_OBJECT DeviceObject;
555 PDEVICE_NODE DeviceNode;
556
557 DPRINT("IopGetDeviceDepth() called\n");
558 DPRINT("Device name: %wZ\n", &DepthData->DeviceInstance);
559
560 /* Get the device object */
561 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DepthData->DeviceInstance);
562 if (DeviceObject == NULL)
563 return STATUS_NO_SUCH_DEVICE;
564
565 DeviceNode = IopGetDeviceNode(DeviceObject);
566
567 DepthData->Depth = DeviceNode->Level;
568
569 ObDereferenceObject(DeviceObject);
570
571 return STATUS_SUCCESS;
572 }
573
574
575 static NTSTATUS
576 IopResetDevice(PPLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData)
577 {
578 PDEVICE_OBJECT DeviceObject;
579 PDEVICE_NODE DeviceNode;
580 NTSTATUS Status;
581
582 DPRINT("IopResetDevice() called\n");
583 DPRINT("Device name: %wZ\n", &ResetDeviceData->DeviceInstance);
584
585 /* Get the device object */
586 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&ResetDeviceData->DeviceInstance);
587 if (DeviceObject == NULL)
588 return STATUS_NO_SUCH_DEVICE;
589
590 DeviceNode = IopGetDeviceNode(DeviceObject);
591
592 /* FIXME: we should stop the device, before starting it again */
593
594 /* Start the device */
595 IopDeviceNodeClearFlag(DeviceNode, DNF_DISABLED);
596 Status = IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
597
598 if (NT_SUCCESS(Status))
599 Status = IopActionInitChildServices(DeviceNode, DeviceNode->Parent, FALSE);
600
601 ObDereferenceObject(DeviceObject);
602
603 return Status;
604 }
605
606
607 /*
608 * NtPlugPlayControl
609 *
610 * A function for doing various Plug & Play operations from user mode.
611 *
612 * Parameters
613 * PlugPlayControlClass
614 * 0x00 Reenumerate device tree
615 *
616 * Buffer points to UNICODE_STRING decribing the instance
617 * path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For
618 * more information about instance paths see !devnode command
619 * in kernel debugger or look at "Inside Windows 2000" book,
620 * chapter "Driver Loading, Initialization, and Installation".
621 *
622 * 0x01 Register new device
623 * 0x02 Deregister device
624 * 0x03 Initialize device
625 * 0x04 Start device
626 * 0x06 Query and remove device
627 * 0x07 User response
628 *
629 * Called after processing the message from NtGetPlugPlayEvent.
630 *
631 * 0x08 Generate legacy device
632 * 0x09 Get interface device list
633 * 0x0A Get property data
634 * 0x0B Device class association (Registration)
635 * 0x0C Get related device
636 * 0x0D Get device interface alias
637 * 0x0E Get/set/clear device status
638 * 0x0F Get device depth
639 * 0x10 Query device relations
640 * 0x11 Query target device relation
641 * 0x12 Query conflict list
642 * 0x13 Retrieve dock data
643 * 0x14 Reset device
644 * 0x15 Halt device
645 * 0x16 Get blocked driver data
646 *
647 * Buffer
648 * The buffer contains information that is specific to each control
649 * code. The buffer is read-only.
650 *
651 * BufferSize
652 * Size of the buffer pointed by the Buffer parameter. If the
653 * buffer size specifies incorrect value for specified control
654 * code, error ??? is returned.
655 *
656 * Return Values
657 * STATUS_PRIVILEGE_NOT_HELD
658 * STATUS_SUCCESS
659 * ...
660 *
661 * @unimplemented
662 */
663 NTSTATUS STDCALL
664 NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
665 IN OUT PVOID Buffer,
666 IN ULONG BufferLength)
667 {
668 NTSTATUS Status = STATUS_SUCCESS;
669
670 DPRINT("NtPlugPlayControl(%lu %p %lu) called\n",
671 PlugPlayControlClass, Buffer, BufferLength);
672
673 /* Function can only be called from user-mode */
674 if (KeGetPreviousMode() == KernelMode)
675 {
676 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
677 return STATUS_ACCESS_DENIED;
678 }
679
680 /* Check for Tcb privilege */
681 if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
682 UserMode))
683 {
684 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
685 return STATUS_PRIVILEGE_NOT_HELD;
686 }
687
688 /* Probe the buffer */
689 _SEH_TRY
690 {
691 ProbeForWrite(Buffer,
692 BufferLength,
693 sizeof(ULONG));
694 }
695 _SEH_HANDLE
696 {
697 Status = _SEH_GetExceptionCode();
698 }
699 _SEH_END;
700
701 if (!NT_SUCCESS(Status))
702 {
703 return Status;
704 }
705
706 switch (PlugPlayControlClass)
707 {
708 case PlugPlayControlUserResponse:
709 if (Buffer || BufferLength != 0)
710 return STATUS_INVALID_PARAMETER;
711 return IopRemovePlugPlayEvent();
712
713 case PlugPlayControlProperty:
714 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
715 return STATUS_INVALID_PARAMETER;
716 return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer);
717
718 case PlugPlayControlGetRelatedDevice:
719 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
720 return STATUS_INVALID_PARAMETER;
721 return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer);
722
723 case PlugPlayControlDeviceStatus:
724 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
725 return STATUS_INVALID_PARAMETER;
726 return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer);
727
728 case PlugPlayControlGetDeviceDepth:
729 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA))
730 return STATUS_INVALID_PARAMETER;
731 return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer);
732
733 case PlugPlayControlResetDevice:
734 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA))
735 return STATUS_INVALID_PARAMETER;
736 return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA)Buffer);
737
738 default:
739 return STATUS_NOT_IMPLEMENTED;
740 }
741
742 return STATUS_NOT_IMPLEMENTED;
743 }
744
745 /* EOF */