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