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