Sync with trunk (r48414)
[reactos.git] / ntoskrnl / io / pnpmgr / pnpreport.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * COPYRIGHT: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/io/pnpmgr/pnpreport.c
5 * PURPOSE: Device Changes Reporting Functions
6 * PROGRAMMERS: Cameron Gutman (cameron.gutman@reactos.org)
7 * Pierre Schweitzer
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* TYPES *******************************************************************/
17
18 typedef struct _INTERNAL_WORK_QUEUE_ITEM
19 {
20 WORK_QUEUE_ITEM WorkItem;
21 PDEVICE_OBJECT PhysicalDeviceObject;
22 PDEVICE_CHANGE_COMPLETE_CALLBACK Callback;
23 PVOID Context;
24 PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure;
25 } INTERNAL_WORK_QUEUE_ITEM, *PINTERNAL_WORK_QUEUE_ITEM;
26
27 NTSTATUS
28 NTAPI
29 IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath,
30 IN ULONG CreateOptions,
31 OUT PHANDLE Handle);
32
33 NTSTATUS
34 IopSetDeviceInstanceData(HANDLE InstanceKey,
35 PDEVICE_NODE DeviceNode);
36
37 NTSTATUS
38 IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
39 PVOID Context);
40
41 NTSTATUS
42 IopDetectResourceConflict(IN PCM_RESOURCE_LIST ResourceList);
43
44 NTSTATUS
45 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject,
46 IN OUT PKEVENT SyncEvent OPTIONAL,
47 IN OUT PNTSTATUS SyncStatus OPTIONAL,
48 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
49 IN PVOID Context OPTIONAL,
50 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure);
51
52 /* PRIVATE FUNCTIONS *********************************************************/
53
54 PWCHAR
55 IopGetInterfaceTypeString(INTERFACE_TYPE IfType)
56 {
57 switch (IfType)
58 {
59 case Internal:
60 return L"Internal";
61
62 case Isa:
63 return L"Isa";
64
65 case Eisa:
66 return L"Eisa";
67
68 case MicroChannel:
69 return L"MicroChannel";
70
71 case TurboChannel:
72 return L"TurboChannel";
73
74 case PCIBus:
75 return L"PCIBus";
76
77 case VMEBus:
78 return L"VMEBus";
79
80 case NuBus:
81 return L"NuBus";
82
83 case PCMCIABus:
84 return L"PCMCIABus";
85
86 case CBus:
87 return L"CBus";
88
89 case MPIBus:
90 return L"MPIBus";
91
92 case MPSABus:
93 return L"MPSABus";
94
95 case ProcessorInternal:
96 return L"ProcessorInternal";
97
98 case PNPISABus:
99 return L"PNPISABus";
100
101 case PNPBus:
102 return L"PNPBus";
103
104 case Vmcs:
105 return L"Vmcs";
106
107 default:
108 DPRINT1("Invalid bus type: %d\n", IfType);
109 return NULL;
110 }
111 }
112
113 VOID
114 IopReportTargetDeviceChangeAsyncWorker(PVOID Context)
115 {
116 PINTERNAL_WORK_QUEUE_ITEM Item;
117
118 Item = (PINTERNAL_WORK_QUEUE_ITEM)Context;
119 PpSetCustomTargetEvent(Item->PhysicalDeviceObject, NULL, NULL, Item->Callback, Item->Context, Item->NotificationStructure);
120 ObDereferenceObject(Item->PhysicalDeviceObject);
121 ExFreePoolWithTag(Context, ' pP');
122 }
123
124 NTSTATUS
125 PpSetCustomTargetEvent(IN PDEVICE_OBJECT DeviceObject,
126 IN OUT PKEVENT SyncEvent OPTIONAL,
127 IN OUT PNTSTATUS SyncStatus OPTIONAL,
128 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
129 IN PVOID Context OPTIONAL,
130 IN PTARGET_DEVICE_CUSTOM_NOTIFICATION NotificationStructure)
131 {
132 ASSERT(NotificationStructure != NULL);
133 ASSERT(DeviceObject != NULL);
134
135 /* That call is totally wrong but notifications handler must be fixed first */
136 IopNotifyPlugPlayNotification(DeviceObject,
137 EventCategoryTargetDeviceChange,
138 &GUID_PNP_CUSTOM_NOTIFICATION,
139 NotificationStructure,
140 NULL);
141 return STATUS_SUCCESS;
142 }
143
144 /* PUBLIC FUNCTIONS **********************************************************/
145
146 /*
147 * @implemented
148 */
149 NTSTATUS
150 NTAPI
151 IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
152 IN INTERFACE_TYPE LegacyBusType,
153 IN ULONG BusNumber,
154 IN ULONG SlotNumber,
155 IN PCM_RESOURCE_LIST ResourceList,
156 IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL,
157 IN BOOLEAN ResourceAssigned,
158 IN OUT PDEVICE_OBJECT *DeviceObject OPTIONAL)
159 {
160 PDEVICE_NODE DeviceNode;
161 PDEVICE_OBJECT Pdo;
162 NTSTATUS Status;
163 HANDLE InstanceKey;
164 ULONG RequiredLength;
165 UNICODE_STRING ValueName, ServiceName;
166 WCHAR HardwareId[256];
167 PWCHAR IfString;
168 ULONG IdLength;
169
170 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
171 DeviceObject, DeviceObject ? *DeviceObject : NULL);
172
173 /* Create the service name (eg. ACPI_HAL) */
174 ServiceName.Buffer = DriverObject->DriverName.Buffer +
175 sizeof(DRIVER_ROOT_NAME) / sizeof(WCHAR) - 1;
176 ServiceName.Length = DriverObject->DriverName.Length -
177 sizeof(DRIVER_ROOT_NAME) + sizeof(WCHAR);
178 ServiceName.MaximumLength = ServiceName.Length;
179
180 /* If the interface type is unknown, treat it as internal */
181 if (LegacyBusType == InterfaceTypeUndefined)
182 LegacyBusType = Internal;
183
184 /* Get the string equivalent of the interface type */
185 IfString = IopGetInterfaceTypeString(LegacyBusType);
186
187 /* If NULL is returned then it's a bad type */
188 if (!IfString)
189 return STATUS_INVALID_PARAMETER;
190
191 /* We use the caller's PDO if they supplied one */
192 if (DeviceObject && *DeviceObject)
193 {
194 Pdo = *DeviceObject;
195 DeviceNode = IopGetDeviceNode(*DeviceObject);
196 }
197 else
198 {
199 /* Create the PDO */
200 Status = PnpRootCreateDevice(&ServiceName,
201 &Pdo,
202 NULL);
203 if (!NT_SUCCESS(Status))
204 {
205 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status);
206 return Status;
207 }
208
209 /* Create the device node for the new PDO */
210 Status = IopCreateDeviceNode(IopRootDeviceNode,
211 Pdo,
212 NULL,
213 &DeviceNode);
214
215 if (!NT_SUCCESS(Status))
216 {
217 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
218 return Status;
219 }
220 }
221
222 /* We don't call AddDevice for devices reported this way */
223 IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
224
225 /* We don't send IRP_MN_START_DEVICE */
226 IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
227
228 /* We need to get device IDs */
229 #if 0
230 IopDeviceNodeSetFlag(DeviceNode, DNF_NEED_QUERY_IDS);
231 #endif
232
233 /* This is a legacy driver for this device */
234 IopDeviceNodeSetFlag(DeviceNode, DNF_LEGACY_DRIVER);
235
236 /* Perform a manual configuration of our device */
237 IopActionInterrogateDeviceStack(DeviceNode, DeviceNode->Parent);
238 IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
239
240 /* Open a handle to the instance path key */
241 Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, 0, &InstanceKey);
242 if (!NT_SUCCESS(Status))
243 return Status;
244
245 /* Add DETECTEDInterfaceType\DriverName */
246 IdLength = 0;
247 IdLength += swprintf(&HardwareId[IdLength],
248 L"DETECTED%ls\\%wZ",
249 IfString,
250 &ServiceName);
251 HardwareId[IdLength++] = UNICODE_NULL;
252
253 /* Add DETECTED\DriverName */
254 IdLength += swprintf(&HardwareId[IdLength],
255 L"DETECTED\\%wZ",
256 &ServiceName);
257 HardwareId[IdLength++] = UNICODE_NULL;
258
259 /* Terminate the string with another null */
260 HardwareId[IdLength] = UNICODE_NULL;
261
262 /* Store the value for CompatibleIDs */
263 RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
264 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
265 if (!NT_SUCCESS(Status))
266 {
267 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status);
268 ZwClose(InstanceKey);
269 return Status;
270 }
271
272 /* Add a hardware ID if the driver didn't report one */
273 RtlInitUnicodeString(&ValueName, L"HardwareID");
274 if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
275 {
276 /* Just use our most specific compatible ID */
277 IdLength = 0;
278 IdLength += swprintf(&HardwareId[IdLength],
279 L"DETECTED%ls\\%wZ",
280 IfString,
281 &ServiceName);
282 HardwareId[++IdLength] = UNICODE_NULL;
283
284 /* Write the value to the registry */
285 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, HardwareId, IdLength * sizeof(WCHAR));
286 if (!NT_SUCCESS(Status))
287 {
288 DPRINT("Failed to write the hardware ID: 0x%x\n", Status);
289 ZwClose(InstanceKey);
290 return Status;
291 }
292 }
293
294 /* Assign the resources to the device node */
295 DeviceNode->BootResources = ResourceList;
296 DeviceNode->ResourceRequirements = ResourceRequirements;
297
298 /* Set appropriate flags */
299 if (DeviceNode->BootResources)
300 IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
301
302 if (!DeviceNode->ResourceRequirements && !DeviceNode->BootResources)
303 IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED);
304
305 /* Write the resource information to the registry */
306 IopSetDeviceInstanceData(InstanceKey, DeviceNode);
307
308 /* If the caller didn't get the resources assigned for us, do it now */
309 if (!ResourceAssigned)
310 {
311 Status = IopAssignDeviceResources(DeviceNode);
312
313 /* See if we failed */
314 if (!NT_SUCCESS(Status))
315 {
316 DPRINT("Assigning resources failed: 0x%x\n", Status);
317 ZwClose(InstanceKey);
318 return Status;
319 }
320 }
321
322 /* Close the instance key handle */
323 ZwClose(InstanceKey);
324
325 /* Report the device's enumeration to umpnpmgr */
326 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
327 &DeviceNode->InstancePath);
328
329 /* Report the device's arrival to umpnpmgr */
330 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
331 &DeviceNode->InstancePath);
332
333 DPRINT1("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath);
334
335 /* Return the PDO */
336 if (DeviceObject) *DeviceObject = Pdo;
337
338 return STATUS_SUCCESS;
339 }
340
341 /*
342 * @halfplemented
343 */
344 NTSTATUS
345 NTAPI
346 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject,
347 IN PCM_RESOURCE_LIST DriverList OPTIONAL,
348 IN ULONG DriverListSize OPTIONAL,
349 IN PDEVICE_OBJECT DeviceObject OPTIONAL,
350 IN PCM_RESOURCE_LIST DeviceList OPTIONAL,
351 IN ULONG DeviceListSize OPTIONAL,
352 OUT PBOOLEAN ConflictDetected)
353 {
354 PCM_RESOURCE_LIST ResourceList;
355 NTSTATUS Status;
356
357 *ConflictDetected = FALSE;
358
359 if (!DriverList && !DeviceList)
360 return STATUS_INVALID_PARAMETER;
361
362 /* Find the real list */
363 if (!DriverList)
364 ResourceList = DeviceList;
365 else
366 ResourceList = DriverList;
367
368 /* Look for a resource conflict */
369 Status = IopDetectResourceConflict(ResourceList);
370 if (Status == STATUS_CONFLICTING_ADDRESSES)
371 {
372 /* Oh noes */
373 *ConflictDetected = TRUE;
374 }
375 else if (NT_SUCCESS(Status))
376 {
377 /* Looks like we're good to go */
378
379 /* TODO: Claim the resources in the ResourceMap */
380 }
381
382 return Status;
383 }
384
385 VOID
386 NTAPI
387 IopSetEvent(IN PVOID Context)
388 {
389 PKEVENT Event = Context;
390
391 /* Set the event */
392 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
393 }
394
395 /*
396 * @implemented
397 */
398 NTSTATUS
399 NTAPI
400 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject,
401 IN PVOID NotificationStructure)
402 {
403 KEVENT NotifyEvent;
404 NTSTATUS Status, NotifyStatus;
405 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
406
407 ASSERT(notifyStruct);
408
409 /* Check for valid PDO */
410 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
411 {
412 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG)PhysicalDeviceObject, 0, 0);
413 }
414
415 /* FileObject must be null. PnP will fill in it */
416 ASSERT(notifyStruct->FileObject == NULL);
417
418 /* Do not handle system PnP events */
419 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
420 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
421 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
422 {
423 return STATUS_INVALID_DEVICE_REQUEST;
424 }
425
426 if (notifyStruct->Version != 1)
427 {
428 return STATUS_INVALID_DEVICE_REQUEST;
429 }
430
431 /* Initialize even that will let us know when PnP will have finished notify */
432 KeInitializeEvent(&NotifyEvent, NotificationEvent, FALSE);
433
434 Status = PpSetCustomTargetEvent(PhysicalDeviceObject, &NotifyEvent, &NotifyStatus, NULL, NULL, notifyStruct);
435 /* If no error, wait for the notify to end and return the status of the notify and not of the event */
436 if (NT_SUCCESS(Status))
437 {
438 KeWaitForSingleObject(&NotifyEvent, Executive, KernelMode, FALSE, NULL);
439 Status = NotifyStatus;
440 }
441
442 return Status;
443 }
444
445 /*
446 * @implemented
447 */
448 NTSTATUS
449 NTAPI
450 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject,
451 IN PVOID NotificationStructure,
452 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
453 IN PVOID Context OPTIONAL)
454 {
455 PINTERNAL_WORK_QUEUE_ITEM Item = NULL;
456 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
457
458 ASSERT(notifyStruct);
459
460 /* Check for valid PDO */
461 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
462 {
463 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG)PhysicalDeviceObject, 0, 0);
464 }
465
466 /* FileObject must be null. PnP will fill in it */
467 ASSERT(notifyStruct->FileObject == NULL);
468
469 /* Do not handle system PnP events */
470 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
471 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
472 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
473 {
474 return STATUS_INVALID_DEVICE_REQUEST;
475 }
476
477 if (notifyStruct->Version != 1)
478 {
479 return STATUS_INVALID_DEVICE_REQUEST;
480 }
481
482 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */
483 Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(INTERNAL_WORK_QUEUE_ITEM), ' pP');
484 if (!Item) return STATUS_INSUFFICIENT_RESOURCES;
485
486 /* Initialize all stuff */
487 ObReferenceObject(PhysicalDeviceObject);
488 Item->NotificationStructure = notifyStruct;
489 Item->PhysicalDeviceObject = PhysicalDeviceObject;
490 Item->Callback = Callback;
491 Item->Context = Context;
492 ExInitializeWorkItem(&(Item->WorkItem), (PWORKER_THREAD_ROUTINE)IopReportTargetDeviceChangeAsyncWorker, Item);
493
494 /* Finally, queue the item, our work here is done */
495 ExQueueWorkItem(&(Item->WorkItem), DelayedWorkQueue);
496
497 return STATUS_PENDING;
498 }