- merge audio headers
[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 if (SyncEvent)
136 {
137 ASSERT(SyncStatus);
138 *SyncStatus = STATUS_PENDING;
139 }
140
141 /* That call is totally wrong but notifications handler must be fixed first */
142 IopNotifyPlugPlayNotification(DeviceObject,
143 EventCategoryTargetDeviceChange,
144 &GUID_PNP_CUSTOM_NOTIFICATION,
145 NotificationStructure,
146 NULL);
147
148 if (SyncEvent)
149 {
150 KeSetEvent(SyncEvent, IO_NO_INCREMENT, FALSE);
151 *SyncStatus = STATUS_SUCCESS;
152 }
153
154 return STATUS_SUCCESS;
155 }
156
157 /* PUBLIC FUNCTIONS **********************************************************/
158
159 /*
160 * @implemented
161 */
162 NTSTATUS
163 NTAPI
164 IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
165 IN INTERFACE_TYPE LegacyBusType,
166 IN ULONG BusNumber,
167 IN ULONG SlotNumber,
168 IN PCM_RESOURCE_LIST ResourceList,
169 IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL,
170 IN BOOLEAN ResourceAssigned,
171 IN OUT PDEVICE_OBJECT *DeviceObject OPTIONAL)
172 {
173 PDEVICE_NODE DeviceNode;
174 PDEVICE_OBJECT Pdo;
175 NTSTATUS Status;
176 HANDLE InstanceKey;
177 ULONG RequiredLength;
178 UNICODE_STRING ValueName, ServiceName;
179 WCHAR HardwareId[256];
180 PWCHAR IfString;
181 ULONG IdLength;
182
183 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
184 DeviceObject, DeviceObject ? *DeviceObject : NULL);
185
186 /* Create the service name (eg. ACPI_HAL) */
187 ServiceName.Buffer = DriverObject->DriverName.Buffer +
188 sizeof(DRIVER_ROOT_NAME) / sizeof(WCHAR) - 1;
189 ServiceName.Length = DriverObject->DriverName.Length -
190 sizeof(DRIVER_ROOT_NAME) + sizeof(WCHAR);
191 ServiceName.MaximumLength = ServiceName.Length;
192
193 /* If the interface type is unknown, treat it as internal */
194 if (LegacyBusType == InterfaceTypeUndefined)
195 LegacyBusType = Internal;
196
197 /* Get the string equivalent of the interface type */
198 IfString = IopGetInterfaceTypeString(LegacyBusType);
199
200 /* If NULL is returned then it's a bad type */
201 if (!IfString)
202 return STATUS_INVALID_PARAMETER;
203
204 /* We use the caller's PDO if they supplied one */
205 if (DeviceObject && *DeviceObject)
206 {
207 Pdo = *DeviceObject;
208 DeviceNode = IopGetDeviceNode(*DeviceObject);
209 }
210 else
211 {
212 /* Create the PDO */
213 Status = PnpRootCreateDevice(&ServiceName,
214 &Pdo,
215 NULL);
216 if (!NT_SUCCESS(Status))
217 {
218 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status);
219 return Status;
220 }
221
222 /* Create the device node for the new PDO */
223 Status = IopCreateDeviceNode(IopRootDeviceNode,
224 Pdo,
225 NULL,
226 &DeviceNode);
227
228 if (!NT_SUCCESS(Status))
229 {
230 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
231 return Status;
232 }
233 }
234
235 /* We don't call AddDevice for devices reported this way */
236 IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
237
238 /* We don't send IRP_MN_START_DEVICE */
239 IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
240
241 /* We need to get device IDs */
242 #if 0
243 IopDeviceNodeSetFlag(DeviceNode, DNF_NEED_QUERY_IDS);
244 #endif
245
246 /* This is a legacy driver for this device */
247 IopDeviceNodeSetFlag(DeviceNode, DNF_LEGACY_DRIVER);
248
249 /* Perform a manual configuration of our device */
250 IopActionInterrogateDeviceStack(DeviceNode, DeviceNode->Parent);
251 IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
252
253 /* Open a handle to the instance path key */
254 Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, 0, &InstanceKey);
255 if (!NT_SUCCESS(Status))
256 return Status;
257
258 /* Add DETECTEDInterfaceType\DriverName */
259 IdLength = 0;
260 IdLength += swprintf(&HardwareId[IdLength],
261 L"DETECTED%ls\\%wZ",
262 IfString,
263 &ServiceName);
264 HardwareId[IdLength++] = UNICODE_NULL;
265
266 /* Add DETECTED\DriverName */
267 IdLength += swprintf(&HardwareId[IdLength],
268 L"DETECTED\\%wZ",
269 &ServiceName);
270 HardwareId[IdLength++] = UNICODE_NULL;
271
272 /* Terminate the string with another null */
273 HardwareId[IdLength] = UNICODE_NULL;
274
275 /* Store the value for CompatibleIDs */
276 RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
277 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
278 if (!NT_SUCCESS(Status))
279 {
280 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status);
281 ZwClose(InstanceKey);
282 return Status;
283 }
284
285 /* Add a hardware ID if the driver didn't report one */
286 RtlInitUnicodeString(&ValueName, L"HardwareID");
287 if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
288 {
289 /* Just use our most specific compatible ID */
290 IdLength = 0;
291 IdLength += swprintf(&HardwareId[IdLength],
292 L"DETECTED%ls\\%wZ",
293 IfString,
294 &ServiceName);
295 HardwareId[++IdLength] = UNICODE_NULL;
296
297 /* Write the value to the registry */
298 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, HardwareId, IdLength * sizeof(WCHAR));
299 if (!NT_SUCCESS(Status))
300 {
301 DPRINT("Failed to write the hardware ID: 0x%x\n", Status);
302 ZwClose(InstanceKey);
303 return Status;
304 }
305 }
306
307 /* Assign the resources to the device node */
308 DeviceNode->BootResources = ResourceList;
309 DeviceNode->ResourceRequirements = ResourceRequirements;
310
311 /* Set appropriate flags */
312 if (DeviceNode->BootResources)
313 IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
314
315 if (!DeviceNode->ResourceRequirements && !DeviceNode->BootResources)
316 IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED);
317
318 /* Write the resource information to the registry */
319 IopSetDeviceInstanceData(InstanceKey, DeviceNode);
320
321 /* If the caller didn't get the resources assigned for us, do it now */
322 if (!ResourceAssigned)
323 {
324 Status = IopAssignDeviceResources(DeviceNode);
325
326 /* See if we failed */
327 if (!NT_SUCCESS(Status))
328 {
329 DPRINT("Assigning resources failed: 0x%x\n", Status);
330 ZwClose(InstanceKey);
331 return Status;
332 }
333 }
334
335 /* Close the instance key handle */
336 ZwClose(InstanceKey);
337
338 /* Report the device's enumeration to umpnpmgr */
339 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
340 &DeviceNode->InstancePath);
341
342 /* Report the device's arrival to umpnpmgr */
343 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
344 &DeviceNode->InstancePath);
345
346 DPRINT1("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath);
347
348 /* Return the PDO */
349 if (DeviceObject) *DeviceObject = Pdo;
350
351 return STATUS_SUCCESS;
352 }
353
354 /*
355 * @halfplemented
356 */
357 NTSTATUS
358 NTAPI
359 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject,
360 IN PCM_RESOURCE_LIST DriverList OPTIONAL,
361 IN ULONG DriverListSize OPTIONAL,
362 IN PDEVICE_OBJECT DeviceObject OPTIONAL,
363 IN PCM_RESOURCE_LIST DeviceList OPTIONAL,
364 IN ULONG DeviceListSize OPTIONAL,
365 OUT PBOOLEAN ConflictDetected)
366 {
367 PCM_RESOURCE_LIST ResourceList;
368 NTSTATUS Status;
369
370 *ConflictDetected = FALSE;
371
372 if (!DriverList && !DeviceList)
373 return STATUS_INVALID_PARAMETER;
374
375 /* Find the real list */
376 if (!DriverList)
377 ResourceList = DeviceList;
378 else
379 ResourceList = DriverList;
380
381 /* Look for a resource conflict */
382 Status = IopDetectResourceConflict(ResourceList);
383 if (Status == STATUS_CONFLICTING_ADDRESSES)
384 {
385 /* Oh noes */
386 *ConflictDetected = TRUE;
387 }
388 else if (NT_SUCCESS(Status))
389 {
390 /* Looks like we're good to go */
391
392 /* TODO: Claim the resources in the ResourceMap */
393 }
394
395 return Status;
396 }
397
398 VOID
399 NTAPI
400 IopSetEvent(IN PVOID Context)
401 {
402 PKEVENT Event = Context;
403
404 /* Set the event */
405 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
406 }
407
408 /*
409 * @implemented
410 */
411 NTSTATUS
412 NTAPI
413 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject,
414 IN PVOID NotificationStructure)
415 {
416 KEVENT NotifyEvent;
417 NTSTATUS Status, NotifyStatus;
418 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
419
420 ASSERT(notifyStruct);
421
422 /* Check for valid PDO */
423 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
424 {
425 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG)PhysicalDeviceObject, 0, 0);
426 }
427
428 /* FileObject must be null. PnP will fill in it */
429 ASSERT(notifyStruct->FileObject == NULL);
430
431 /* Do not handle system PnP events */
432 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
433 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
434 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
435 {
436 return STATUS_INVALID_DEVICE_REQUEST;
437 }
438
439 if (notifyStruct->Version != 1)
440 {
441 return STATUS_INVALID_DEVICE_REQUEST;
442 }
443
444 /* Initialize even that will let us know when PnP will have finished notify */
445 KeInitializeEvent(&NotifyEvent, NotificationEvent, FALSE);
446
447 Status = PpSetCustomTargetEvent(PhysicalDeviceObject, &NotifyEvent, &NotifyStatus, NULL, NULL, notifyStruct);
448 /* If no error, wait for the notify to end and return the status of the notify and not of the event */
449 if (NT_SUCCESS(Status))
450 {
451 KeWaitForSingleObject(&NotifyEvent, Executive, KernelMode, FALSE, NULL);
452 Status = NotifyStatus;
453 }
454
455 return Status;
456 }
457
458 /*
459 * @implemented
460 */
461 NTSTATUS
462 NTAPI
463 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject,
464 IN PVOID NotificationStructure,
465 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
466 IN PVOID Context OPTIONAL)
467 {
468 PINTERNAL_WORK_QUEUE_ITEM Item = NULL;
469 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
470
471 ASSERT(notifyStruct);
472
473 /* Check for valid PDO */
474 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
475 {
476 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG)PhysicalDeviceObject, 0, 0);
477 }
478
479 /* FileObject must be null. PnP will fill in it */
480 ASSERT(notifyStruct->FileObject == NULL);
481
482 /* Do not handle system PnP events */
483 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
484 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
485 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
486 {
487 return STATUS_INVALID_DEVICE_REQUEST;
488 }
489
490 if (notifyStruct->Version != 1)
491 {
492 return STATUS_INVALID_DEVICE_REQUEST;
493 }
494
495 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */
496 Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(INTERNAL_WORK_QUEUE_ITEM), ' pP');
497 if (!Item) return STATUS_INSUFFICIENT_RESOURCES;
498
499 /* Initialize all stuff */
500 ObReferenceObject(PhysicalDeviceObject);
501 Item->NotificationStructure = notifyStruct;
502 Item->PhysicalDeviceObject = PhysicalDeviceObject;
503 Item->Callback = Callback;
504 Item->Context = Context;
505 ExInitializeWorkItem(&(Item->WorkItem), (PWORKER_THREAD_ROUTINE)IopReportTargetDeviceChangeAsyncWorker, Item);
506
507 /* Finally, queue the item, our work here is done */
508 ExQueueWorkItem(&(Item->WorkItem), DelayedWorkQueue);
509
510 return STATUS_PENDING;
511 }