948da46a25872b2fc7c496e22b8b69a8f62ba957
[reactos.git] / reactos / ntoskrnl / io / pnpmgr / plugplay.c
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 return Status;
80
81 InsertHeadList(&IopPnpEventQueueHead,
82 &EventEntry->ListEntry);
83 KeSetEvent(&IopPnpNotifyEvent,
84 0,
85 FALSE);
86
87 return STATUS_SUCCESS;
88 }
89
90
91 /*
92 * Remove the current PnP event from the tail of the event queue
93 * and signal IopPnpNotifyEvent if there is yet another event in the queue.
94 */
95 static NTSTATUS
96 IopRemovePlugPlayEvent(VOID)
97 {
98 /* Remove a pnp event entry from the tail of the queue */
99 if (!IsListEmpty(&IopPnpEventQueueHead))
100 {
101 ExFreePool(RemoveTailList(&IopPnpEventQueueHead));
102 }
103
104 /* Signal the next pnp event in the queue */
105 if (!IsListEmpty(&IopPnpEventQueueHead))
106 {
107 KeSetEvent(&IopPnpNotifyEvent,
108 0,
109 FALSE);
110 }
111
112 return STATUS_SUCCESS;
113 }
114
115 static PDEVICE_OBJECT
116 IopTraverseDeviceNode(PDEVICE_NODE Node, PUNICODE_STRING DeviceInstance)
117 {
118 PDEVICE_OBJECT DeviceObject;
119 PDEVICE_NODE ChildNode;
120
121 if (RtlEqualUnicodeString(&Node->InstancePath,
122 DeviceInstance, TRUE))
123 {
124 ObReferenceObject(Node->PhysicalDeviceObject);
125 return Node->PhysicalDeviceObject;
126 }
127
128 /* Traversal of all children nodes */
129 for (ChildNode = Node->Child;
130 ChildNode != NULL;
131 ChildNode = ChildNode->Sibling)
132 {
133 DeviceObject = IopTraverseDeviceNode(ChildNode, DeviceInstance);
134 if (DeviceObject != NULL)
135 {
136 return DeviceObject;
137 }
138 }
139
140 return NULL;
141 }
142
143
144 static PDEVICE_OBJECT
145 IopGetDeviceObjectFromDeviceInstance(PUNICODE_STRING DeviceInstance)
146 {
147 if (IopRootDeviceNode == NULL)
148 return NULL;
149
150 if (DeviceInstance == NULL ||
151 DeviceInstance->Length == 0
152 )
153 {
154 if (IopRootDeviceNode->PhysicalDeviceObject)
155 {
156 ObReferenceObject(IopRootDeviceNode->PhysicalDeviceObject);
157 return IopRootDeviceNode->PhysicalDeviceObject;
158 }
159 else
160 return NULL;
161 }
162
163 return IopTraverseDeviceNode(IopRootDeviceNode, DeviceInstance);
164
165 }
166
167 static NTSTATUS
168 IopCaptureUnicodeString(PUNICODE_STRING DstName, PUNICODE_STRING SrcName)
169 {
170 NTSTATUS Status = STATUS_SUCCESS;
171 UNICODE_STRING Name;
172
173 Name.Buffer = NULL;
174 _SEH2_TRY
175 {
176 Name.Length = SrcName->Length;
177 Name.MaximumLength = SrcName->MaximumLength;
178 if (Name.Length > Name.MaximumLength)
179 {
180 Status = STATUS_INVALID_PARAMETER;
181 _SEH2_LEAVE;
182 }
183 if (Name.MaximumLength)
184 {
185 ProbeForRead(SrcName->Buffer,
186 Name.MaximumLength,
187 sizeof(WCHAR));
188 Name.Buffer = ExAllocatePool(NonPagedPool, Name.MaximumLength);
189 if (Name.Buffer == NULL)
190 {
191 Status = STATUS_INSUFFICIENT_RESOURCES;
192 _SEH2_LEAVE;
193 }
194 memcpy(Name.Buffer, SrcName->Buffer, Name.MaximumLength);
195 }
196 *DstName = Name;
197 }
198 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
199 {
200 Status = _SEH2_GetExceptionCode();
201 }
202 _SEH2_END;
203
204 if (!NT_SUCCESS(Status) && Name.Buffer)
205 {
206 ExFreePool(Name.Buffer);
207 }
208 return Status;
209 }
210
211 static NTSTATUS
212 IopGetDeviceProperty(PPLUGPLAY_CONTROL_PROPERTY_DATA PropertyData)
213 {
214 PDEVICE_OBJECT DeviceObject = NULL;
215 NTSTATUS Status = STATUS_SUCCESS;
216 UNICODE_STRING DeviceInstance;
217 ULONG BufferSize;
218 ULONG Property = 0;
219 PVOID Buffer;
220
221 DPRINT("IopGetDeviceProperty() called\n");
222 DPRINT("Device name: %wZ\n", &PropertyData->DeviceInstance);
223
224 Status = IopCaptureUnicodeString(&DeviceInstance, &PropertyData->DeviceInstance);
225 if (!NT_SUCCESS(Status))
226 {
227 return Status;
228 }
229
230 _SEH2_TRY
231 {
232 Property = PropertyData->Property;
233 BufferSize = PropertyData->BufferSize;
234 ProbeForWrite(PropertyData->Buffer,
235 BufferSize,
236 sizeof(UCHAR));
237 }
238 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
239 {
240 Status = _SEH2_GetExceptionCode();
241 }
242 _SEH2_END;
243
244 if (!NT_SUCCESS(Status))
245 {
246 ExFreePool(DeviceInstance.Buffer);
247 return Status;
248 }
249
250 /* Get the device object */
251 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
252 ExFreePool(DeviceInstance.Buffer);
253 if (DeviceObject == NULL)
254 {
255 return STATUS_NO_SUCH_DEVICE;
256 }
257
258 Buffer = ExAllocatePool(NonPagedPool, BufferSize);
259 if (Buffer == NULL)
260 {
261 return STATUS_INSUFFICIENT_RESOURCES;
262 }
263
264
265 Status = IoGetDeviceProperty(DeviceObject,
266 Property,
267 BufferSize,
268 Buffer,
269 &BufferSize);
270
271 ObDereferenceObject(DeviceObject);
272
273 if (NT_SUCCESS(Status))
274 {
275 _SEH2_TRY
276 {
277 memcpy(Buffer, PropertyData->Buffer, BufferSize);
278 PropertyData->BufferSize = BufferSize;
279 }
280 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
281 {
282 Status = _SEH2_GetExceptionCode();
283 }
284 _SEH2_END;
285 }
286 ExFreePool(Buffer);
287 return Status;
288 }
289
290
291 static NTSTATUS
292 IopGetRelatedDevice(PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA RelatedDeviceData)
293 {
294 UNICODE_STRING RootDeviceName;
295 PDEVICE_OBJECT DeviceObject = NULL;
296 PDEVICE_NODE DeviceNode = NULL;
297 PDEVICE_NODE RelatedDeviceNode;
298 UNICODE_STRING TargetDeviceInstance;
299 NTSTATUS Status = STATUS_SUCCESS;
300 ULONG Relation = 0;
301 ULONG MaximumLength = 0;
302
303 DPRINT("IopGetRelatedDevice() called\n");
304 DPRINT("Device name: %wZ\n", &RelatedDeviceData->TargetDeviceInstance);
305
306 Status = IopCaptureUnicodeString(&TargetDeviceInstance, &RelatedDeviceData->TargetDeviceInstance);
307 if (!NT_SUCCESS(Status))
308 {
309 return Status;
310 }
311
312 _SEH2_TRY
313 {
314 Relation = RelatedDeviceData->Relation;
315 MaximumLength = RelatedDeviceData->RelatedDeviceInstanceLength;
316 ProbeForWrite(RelatedDeviceData->RelatedDeviceInstance,
317 MaximumLength,
318 sizeof(WCHAR));
319 }
320 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
321 {
322 Status = _SEH2_GetExceptionCode();
323 }
324 _SEH2_END;
325
326 if (!NT_SUCCESS(Status))
327 {
328 ExFreePool(TargetDeviceInstance.Buffer);
329 return Status;
330 }
331
332 RtlInitUnicodeString(&RootDeviceName,
333 L"HTREE\\ROOT\\0");
334 if (RtlEqualUnicodeString(&TargetDeviceInstance,
335 &RootDeviceName,
336 TRUE))
337 {
338 DeviceNode = IopRootDeviceNode;
339 ExFreePool(TargetDeviceInstance.Buffer);
340 }
341 else
342 {
343 /* Get the device object */
344 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&TargetDeviceInstance);
345 ExFreePool(TargetDeviceInstance.Buffer);
346 if (DeviceObject == NULL)
347 return STATUS_NO_SUCH_DEVICE;
348
349 DeviceNode = ((PEXTENDED_DEVOBJ_EXTENSION)DeviceObject->DeviceObjectExtension)->DeviceNode;
350 }
351
352 switch (Relation)
353 {
354 case PNP_GET_PARENT_DEVICE:
355 RelatedDeviceNode = DeviceNode->Parent;
356 break;
357
358 case PNP_GET_CHILD_DEVICE:
359 RelatedDeviceNode = DeviceNode->Child;
360 break;
361
362 case PNP_GET_SIBLING_DEVICE:
363 RelatedDeviceNode = DeviceNode->Sibling;
364 break;
365
366 default:
367 if (DeviceObject != NULL)
368 {
369 ObDereferenceObject(DeviceObject);
370 }
371
372 return STATUS_INVALID_PARAMETER;
373 }
374
375 if (RelatedDeviceNode == NULL)
376 {
377 if (DeviceObject)
378 {
379 ObDereferenceObject(DeviceObject);
380 }
381
382 return STATUS_NO_SUCH_DEVICE;
383 }
384
385 if (RelatedDeviceNode->InstancePath.Length > MaximumLength)
386 {
387 if (DeviceObject)
388 {
389 ObDereferenceObject(DeviceObject);
390 }
391
392 return STATUS_BUFFER_TOO_SMALL;
393 }
394
395 /* Copy related device instance name */
396 _SEH2_TRY
397 {
398 RtlCopyMemory(RelatedDeviceData->RelatedDeviceInstance,
399 RelatedDeviceNode->InstancePath.Buffer,
400 RelatedDeviceNode->InstancePath.Length);
401 RelatedDeviceData->RelatedDeviceInstanceLength = RelatedDeviceNode->InstancePath.Length;
402 }
403 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
404 {
405 Status = _SEH2_GetExceptionCode();
406 }
407 _SEH2_END;
408
409 if (DeviceObject != NULL)
410 {
411 ObDereferenceObject(DeviceObject);
412 }
413
414 DPRINT("IopGetRelatedDevice() done\n");
415
416 return Status;
417 }
418
419
420 static NTSTATUS
421 IopDeviceStatus(PPLUGPLAY_CONTROL_STATUS_DATA StatusData)
422 {
423 PDEVICE_OBJECT DeviceObject;
424 PDEVICE_NODE DeviceNode;
425 ULONG Operation = 0;
426 ULONG DeviceStatus = 0;
427 ULONG DeviceProblem = 0;
428 UNICODE_STRING DeviceInstance;
429 NTSTATUS Status = STATUS_SUCCESS;
430
431 DPRINT("IopDeviceStatus() called\n");
432
433 Status = IopCaptureUnicodeString(&DeviceInstance, &StatusData->DeviceInstance);
434 if (!NT_SUCCESS(Status))
435 return Status;
436 DPRINT("Device name: '%wZ'\n", &DeviceInstance);
437
438 _SEH2_TRY
439 {
440 Operation = StatusData->Operation;
441 if (Operation == PNP_SET_DEVICE_STATUS)
442 {
443 DeviceStatus = StatusData->DeviceStatus;
444 DeviceProblem = StatusData->DeviceProblem;
445 }
446 }
447 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
448 {
449 Status = _SEH2_GetExceptionCode();
450 }
451 _SEH2_END;
452
453 if (!NT_SUCCESS(Status))
454 {
455 if (DeviceInstance.Buffer)
456 ExFreePool(DeviceInstance.Buffer);
457 return Status;
458 }
459
460 /* Get the device object */
461 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
462 ExFreePool(DeviceInstance.Buffer);
463 if (DeviceObject == NULL)
464 return STATUS_NO_SUCH_DEVICE;
465
466 DeviceNode = IopGetDeviceNode(DeviceObject);
467
468 switch (Operation)
469 {
470 case PNP_GET_DEVICE_STATUS:
471 DPRINT("Get status data\n");
472 DeviceStatus = DeviceNode->Flags;
473 DeviceProblem = DeviceNode->Problem;
474 break;
475
476 case PNP_SET_DEVICE_STATUS:
477 DPRINT("Set status data\n");
478 DeviceNode->Flags = DeviceStatus;
479 DeviceNode->Problem = DeviceProblem;
480 break;
481
482 case PNP_CLEAR_DEVICE_STATUS:
483 DPRINT1("FIXME: Clear status data!\n");
484 break;
485 }
486
487 ObDereferenceObject(DeviceObject);
488
489 if (Operation == PNP_GET_DEVICE_STATUS)
490 {
491 _SEH2_TRY
492 {
493 StatusData->DeviceStatus = DeviceStatus;
494 StatusData->DeviceProblem = DeviceProblem;
495 }
496 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
497 {
498 Status = _SEH2_GetExceptionCode();
499 }
500 _SEH2_END;
501 }
502
503 return Status;
504 }
505
506
507 static NTSTATUS
508 IopGetDeviceDepth(PPLUGPLAY_CONTROL_DEPTH_DATA DepthData)
509 {
510 PDEVICE_OBJECT DeviceObject;
511 PDEVICE_NODE DeviceNode;
512 UNICODE_STRING DeviceInstance;
513 NTSTATUS Status = STATUS_SUCCESS;
514
515 DPRINT("IopGetDeviceDepth() called\n");
516 DPRINT("Device name: %wZ\n", &DepthData->DeviceInstance);
517
518 Status = IopCaptureUnicodeString(&DeviceInstance, &DepthData->DeviceInstance);
519 if (!NT_SUCCESS(Status))
520 {
521 return Status;
522 }
523
524 /* Get the device object */
525 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
526 ExFreePool(DeviceInstance.Buffer);
527 if (DeviceObject == NULL)
528 return STATUS_NO_SUCH_DEVICE;
529
530 DeviceNode = IopGetDeviceNode(DeviceObject);
531
532 _SEH2_TRY
533 {
534 DepthData->Depth = DeviceNode->Level;
535 }
536 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
537 {
538 Status = _SEH2_GetExceptionCode();
539 }
540 _SEH2_END;
541
542 ObDereferenceObject(DeviceObject);
543
544 return Status;
545 }
546
547
548 static NTSTATUS
549 IopResetDevice(PPLUGPLAY_CONTROL_RESET_DEVICE_DATA ResetDeviceData)
550 {
551 PDEVICE_OBJECT DeviceObject;
552 PDEVICE_NODE DeviceNode;
553 NTSTATUS Status = STATUS_SUCCESS;
554 UNICODE_STRING DeviceInstance;
555
556 Status = IopCaptureUnicodeString(&DeviceInstance, &ResetDeviceData->DeviceInstance);
557 if (!NT_SUCCESS(Status))
558 return Status;
559
560 DPRINT("IopResetDevice(%wZ)\n", &DeviceInstance);
561
562 /* Get the device object */
563 DeviceObject = IopGetDeviceObjectFromDeviceInstance(&DeviceInstance);
564 ExFreePool(DeviceInstance.Buffer);
565 if (DeviceObject == NULL)
566 return STATUS_NO_SUCH_DEVICE;
567
568 DeviceNode = IopGetDeviceNode(DeviceObject);
569
570 /* FIXME: we should stop the device, before starting it again */
571
572 /* Start the device */
573 IopDeviceNodeClearFlag(DeviceNode, DNF_DISABLED);
574 Status = IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
575
576 if (NT_SUCCESS(Status))
577 Status = IopActionInitChildServices(DeviceNode, DeviceNode->Parent);
578
579 ObDereferenceObject(DeviceObject);
580
581 return Status;
582 }
583
584 /* PUBLIC FUNCTIONS **********************************************************/
585
586 /*
587 * Plug and Play event structure used by NtGetPlugPlayEvent.
588 *
589 * EventGuid
590 * Can be one of the following values:
591 * GUID_HWPROFILE_QUERY_CHANGE
592 * GUID_HWPROFILE_CHANGE_CANCELLED
593 * GUID_HWPROFILE_CHANGE_COMPLETE
594 * GUID_TARGET_DEVICE_QUERY_REMOVE
595 * GUID_TARGET_DEVICE_REMOVE_CANCELLED
596 * GUID_TARGET_DEVICE_REMOVE_COMPLETE
597 * GUID_PNP_CUSTOM_NOTIFICATION
598 * GUID_PNP_POWER_NOTIFICATION
599 * GUID_DEVICE_* (see above)
600 *
601 * EventCategory
602 * Type of the event that happened.
603 *
604 * Result
605 * ?
606 *
607 * Flags
608 * ?
609 *
610 * TotalSize
611 * Size of the event block including the device IDs and other
612 * per category specific fields.
613 */
614
615 /*
616 * NtGetPlugPlayEvent
617 *
618 * Returns one Plug & Play event from a global queue.
619 *
620 * Parameters
621 * Reserved1
622 * Reserved2
623 * Always set to zero.
624 *
625 * Buffer
626 * The buffer that will be filled with the event information on
627 * successful return from the function.
628 *
629 * BufferSize
630 * Size of the buffer pointed by the Buffer parameter. If the
631 * buffer size is not large enough to hold the whole event
632 * information, error STATUS_BUFFER_TOO_SMALL is returned and
633 * the buffer remains untouched.
634 *
635 * Return Values
636 * STATUS_PRIVILEGE_NOT_HELD
637 * STATUS_BUFFER_TOO_SMALL
638 * STATUS_SUCCESS
639 *
640 * Remarks
641 * This function isn't multi-thread safe!
642 *
643 * @implemented
644 */
645 NTSTATUS
646 NTAPI
647 NtGetPlugPlayEvent(IN ULONG Reserved1,
648 IN ULONG Reserved2,
649 OUT PPLUGPLAY_EVENT_BLOCK Buffer,
650 IN ULONG BufferSize)
651 {
652 PPNP_EVENT_ENTRY Entry;
653 NTSTATUS Status;
654
655 DPRINT("NtGetPlugPlayEvent() called\n");
656
657 /* Function can only be called from user-mode */
658 if (KeGetPreviousMode() == KernelMode)
659 {
660 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
661 return STATUS_ACCESS_DENIED;
662 }
663
664 /* Check for Tcb privilege */
665 if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
666 UserMode))
667 {
668 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
669 return STATUS_PRIVILEGE_NOT_HELD;
670 }
671
672 /* Wait for a PnP event */
673 DPRINT("Waiting for pnp notification event\n");
674 Status = KeWaitForSingleObject(&IopPnpNotifyEvent,
675 UserRequest,
676 KernelMode,
677 FALSE,
678 NULL);
679 if (!NT_SUCCESS(Status))
680 {
681 DPRINT1("KeWaitForSingleObject() failed (Status %lx)\n", Status);
682 return Status;
683 }
684
685 /* Get entry from the tail of the queue */
686 Entry = CONTAINING_RECORD(IopPnpEventQueueHead.Blink,
687 PNP_EVENT_ENTRY,
688 ListEntry);
689
690 /* Check the buffer size */
691 if (BufferSize < Entry->Event.TotalSize)
692 {
693 DPRINT1("Buffer is too small for the pnp-event\n");
694 return STATUS_BUFFER_TOO_SMALL;
695 }
696
697 /* Copy event data to the user buffer */
698 memcpy(Buffer,
699 &Entry->Event,
700 Entry->Event.TotalSize);
701
702 DPRINT("NtGetPlugPlayEvent() done\n");
703
704 return STATUS_SUCCESS;
705 }
706
707 /*
708 * NtPlugPlayControl
709 *
710 * A function for doing various Plug & Play operations from user mode.
711 *
712 * Parameters
713 * PlugPlayControlClass
714 * 0x00 Reenumerate device tree
715 *
716 * Buffer points to UNICODE_STRING decribing the instance
717 * path (like "HTREE\ROOT\0" or "Root\ACPI_HAL\0000"). For
718 * more information about instance paths see !devnode command
719 * in kernel debugger or look at "Inside Windows 2000" book,
720 * chapter "Driver Loading, Initialization, and Installation".
721 *
722 * 0x01 Register new device
723 * 0x02 Deregister device
724 * 0x03 Initialize device
725 * 0x04 Start device
726 * 0x06 Query and remove device
727 * 0x07 User response
728 *
729 * Called after processing the message from NtGetPlugPlayEvent.
730 *
731 * 0x08 Generate legacy device
732 * 0x09 Get interface device list
733 * 0x0A Get property data
734 * 0x0B Device class association (Registration)
735 * 0x0C Get related device
736 * 0x0D Get device interface alias
737 * 0x0E Get/set/clear device status
738 * 0x0F Get device depth
739 * 0x10 Query device relations
740 * 0x11 Query target device relation
741 * 0x12 Query conflict list
742 * 0x13 Retrieve dock data
743 * 0x14 Reset device
744 * 0x15 Halt device
745 * 0x16 Get blocked driver data
746 *
747 * Buffer
748 * The buffer contains information that is specific to each control
749 * code. The buffer is read-only.
750 *
751 * BufferSize
752 * Size of the buffer pointed by the Buffer parameter. If the
753 * buffer size specifies incorrect value for specified control
754 * code, error ??? is returned.
755 *
756 * Return Values
757 * STATUS_PRIVILEGE_NOT_HELD
758 * STATUS_SUCCESS
759 * ...
760 *
761 * @unimplemented
762 */
763 NTSTATUS
764 NTAPI
765 NtPlugPlayControl(IN PLUGPLAY_CONTROL_CLASS PlugPlayControlClass,
766 IN OUT PVOID Buffer,
767 IN ULONG BufferLength)
768 {
769 NTSTATUS Status = STATUS_SUCCESS;
770
771 DPRINT("NtPlugPlayControl(%lu %p %lu) called\n",
772 PlugPlayControlClass, Buffer, BufferLength);
773
774 /* Function can only be called from user-mode */
775 if (KeGetPreviousMode() == KernelMode)
776 {
777 DPRINT1("NtGetPlugPlayEvent cannot be called from kernel mode!\n");
778 return STATUS_ACCESS_DENIED;
779 }
780
781 /* Check for Tcb privilege */
782 if (!SeSinglePrivilegeCheck(SeTcbPrivilege,
783 UserMode))
784 {
785 DPRINT1("NtGetPlugPlayEvent: Caller does not hold the SeTcbPrivilege privilege!\n");
786 return STATUS_PRIVILEGE_NOT_HELD;
787 }
788
789 /* Probe the buffer */
790 _SEH2_TRY
791 {
792 ProbeForWrite(Buffer,
793 BufferLength,
794 sizeof(ULONG));
795 }
796 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
797 {
798 Status = _SEH2_GetExceptionCode();
799 }
800 _SEH2_END;
801
802 if (!NT_SUCCESS(Status))
803 {
804 return Status;
805 }
806
807 switch (PlugPlayControlClass)
808 {
809 case PlugPlayControlUserResponse:
810 if (Buffer || BufferLength != 0)
811 return STATUS_INVALID_PARAMETER;
812 return IopRemovePlugPlayEvent();
813
814 case PlugPlayControlProperty:
815 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_PROPERTY_DATA))
816 return STATUS_INVALID_PARAMETER;
817 return IopGetDeviceProperty((PPLUGPLAY_CONTROL_PROPERTY_DATA)Buffer);
818
819 case PlugPlayControlGetRelatedDevice:
820 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RELATED_DEVICE_DATA))
821 return STATUS_INVALID_PARAMETER;
822 return IopGetRelatedDevice((PPLUGPLAY_CONTROL_RELATED_DEVICE_DATA)Buffer);
823
824 case PlugPlayControlDeviceStatus:
825 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_STATUS_DATA))
826 return STATUS_INVALID_PARAMETER;
827 return IopDeviceStatus((PPLUGPLAY_CONTROL_STATUS_DATA)Buffer);
828
829 case PlugPlayControlGetDeviceDepth:
830 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_DEPTH_DATA))
831 return STATUS_INVALID_PARAMETER;
832 return IopGetDeviceDepth((PPLUGPLAY_CONTROL_DEPTH_DATA)Buffer);
833
834 case PlugPlayControlResetDevice:
835 if (!Buffer || BufferLength < sizeof(PLUGPLAY_CONTROL_RESET_DEVICE_DATA))
836 return STATUS_INVALID_PARAMETER;
837 return IopResetDevice((PPLUGPLAY_CONTROL_RESET_DEVICE_DATA)Buffer);
838
839 default:
840 return STATUS_NOT_IMPLEMENTED;
841 }
842
843 return STATUS_NOT_IMPLEMENTED;
844 }