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