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