00b3547ccefdd646f279936f1ac57fa346a78dd1
[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 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, ServiceLongName, ServiceName;
177 WCHAR HardwareId[256];
178 PWCHAR IfString;
179 ULONG IdLength;
180 ULONG LegacyValue;
181
182 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
183 DeviceObject, DeviceObject ? *DeviceObject : NULL);
184
185 ServiceLongName = DriverObject->DriverExtension->ServiceKeyName;
186 ServiceName = ServiceLongName;
187
188 /* If the interface type is unknown, treat it as internal */
189 if (LegacyBusType == InterfaceTypeUndefined)
190 LegacyBusType = Internal;
191
192 /* Get the string equivalent of the interface type */
193 IfString = IopGetInterfaceTypeString(LegacyBusType);
194
195 /* If NULL is returned then it's a bad type */
196 if (!IfString)
197 return STATUS_INVALID_PARAMETER;
198
199 /*
200 * Drivers that have been created via a direct IoCreateDriver() call
201 * have their ServiceKeyName set to \Driver\DriverName. We need to
202 * strip everything up to the last path separator and keep what remains.
203 */
204 if (DriverObject->Flags & DRVO_BUILTIN_DRIVER)
205 {
206 /*
207 * Find the last path separator.
208 * NOTE: Since ServiceName is not necessarily NULL-terminated,
209 * we cannot use wcsrchr().
210 */
211 if (ServiceName.Buffer && ServiceName.Length >= sizeof(WCHAR))
212 {
213 ValueName.Length = 1;
214 ValueName.Buffer = ServiceName.Buffer + (ServiceName.Length / sizeof(WCHAR)) - 1;
215
216 while ((ValueName.Buffer > ServiceName.Buffer) && (*ValueName.Buffer != L'\\'))
217 {
218 --ValueName.Buffer;
219 ++ValueName.Length;
220 }
221 if (*ValueName.Buffer == L'\\')
222 {
223 ++ValueName.Buffer;
224 --ValueName.Length;
225 }
226 ValueName.Length *= sizeof(WCHAR);
227
228 /* Shorten the string */
229 ServiceName.MaximumLength -= (ServiceName.Length - ValueName.Length);
230 ServiceName.Length = ValueName.Length;
231 ServiceName.Buffer = ValueName.Buffer;
232 }
233 }
234
235 /* We use the caller's PDO if they supplied one */
236 if (DeviceObject && *DeviceObject)
237 {
238 Pdo = *DeviceObject;
239 }
240 else
241 {
242 /* Create the PDO */
243 Status = PnpRootCreateDevice(&ServiceName,
244 NULL,
245 &Pdo,
246 NULL);
247 if (!NT_SUCCESS(Status))
248 {
249 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status);
250 return Status;
251 }
252 }
253
254 /* Create the device node for the new PDO */
255 Status = IopCreateDeviceNode(IopRootDeviceNode,
256 Pdo,
257 NULL,
258 &DeviceNode);
259 if (!NT_SUCCESS(Status))
260 {
261 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
262 return Status;
263 }
264
265 /* We're enumerated already */
266 IopDeviceNodeSetFlag(DeviceNode, DNF_ENUMERATED);
267
268 /* We don't call AddDevice for devices reported this way */
269 IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
270
271 /* We don't send IRP_MN_START_DEVICE */
272 IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
273
274 /* We need to get device IDs */
275 #if 0
276 IopDeviceNodeSetFlag(DeviceNode, DNF_NEED_QUERY_IDS);
277 #endif
278
279 /* This is a legacy driver for this device */
280 IopDeviceNodeSetFlag(DeviceNode, DNF_LEGACY_DRIVER);
281
282 /* Perform a manual configuration of our device */
283 IopActionInterrogateDeviceStack(DeviceNode, DeviceNode->Parent);
284 IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
285
286 /* Open a handle to the instance path key */
287 /* REG_OPTION_VOLATILE is a HACK!!! */
288 Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, REG_OPTION_VOLATILE, &InstanceKey);
289 if (!NT_SUCCESS(Status))
290 return Status;
291
292 /* Save the driver name */
293 RtlInitUnicodeString(&ValueName, L"Service");
294 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, ServiceLongName.Buffer, ServiceLongName.Length + sizeof(UNICODE_NULL));
295 if (!NT_SUCCESS(Status))
296 {
297 DPRINT("Failed to write the Service name value: 0x%x\n", Status);
298 }
299
300 /* Report as non-legacy driver */
301 RtlInitUnicodeString(&ValueName, L"Legacy");
302 LegacyValue = 0;
303 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_DWORD, &LegacyValue, sizeof(LegacyValue));
304 if (!NT_SUCCESS(Status))
305 {
306 DPRINT("Failed to write the Legacy value: 0x%x\n", Status);
307 }
308
309 /* Add DETECTEDInterfaceType\DriverName */
310 IdLength = 0;
311 IdLength += swprintf(&HardwareId[IdLength],
312 L"DETECTED%ls\\%wZ",
313 IfString,
314 &ServiceName);
315 IdLength++;
316
317 /* Add DETECTED\DriverName */
318 IdLength += swprintf(&HardwareId[IdLength],
319 L"DETECTED\\%wZ",
320 &ServiceName);
321 IdLength++;
322
323 /* Terminate the string with another null */
324 HardwareId[IdLength++] = UNICODE_NULL;
325
326 /* Store the value for CompatibleIDs */
327 RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
328 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
329 if (!NT_SUCCESS(Status))
330 {
331 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status);
332 ZwClose(InstanceKey);
333 return Status;
334 }
335
336 /* Add a hardware ID if the driver didn't report one */
337 RtlInitUnicodeString(&ValueName, L"HardwareID");
338 if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
339 {
340 /* Just use our most specific compatible ID */
341 IdLength = 0;
342 IdLength += swprintf(&HardwareId[IdLength],
343 L"DETECTED%ls\\%wZ",
344 IfString,
345 &ServiceName);
346 IdLength++;
347
348 HardwareId[IdLength++] = UNICODE_NULL;
349
350 /* Write the value to the registry */
351 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
352 if (!NT_SUCCESS(Status))
353 {
354 DPRINT("Failed to write the hardware ID: 0x%x\n", Status);
355 ZwClose(InstanceKey);
356 return Status;
357 }
358 }
359
360 /* Assign the resources to the device node */
361 DeviceNode->BootResources = ResourceList;
362 DeviceNode->ResourceRequirements = ResourceRequirements;
363
364 /* Set appropriate flags */
365 if (DeviceNode->BootResources)
366 IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
367
368 if (!DeviceNode->ResourceRequirements && !DeviceNode->BootResources)
369 IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED);
370
371 /* Write the resource information to the registry */
372 IopSetDeviceInstanceData(InstanceKey, DeviceNode);
373
374 /* If the caller didn't get the resources assigned for us, do it now */
375 if (!ResourceAssigned)
376 {
377 Status = IopAssignDeviceResources(DeviceNode);
378
379 /* See if we failed */
380 if (!NT_SUCCESS(Status))
381 {
382 DPRINT("Assigning resources failed: 0x%x\n", Status);
383 ZwClose(InstanceKey);
384 return Status;
385 }
386 }
387
388 /* Close the instance key handle */
389 ZwClose(InstanceKey);
390
391 /* Register the given DO with PnP root if required */
392 if (DeviceObject && *DeviceObject)
393 PnpRootRegisterDevice(*DeviceObject);
394
395 /* Report the device's enumeration to umpnpmgr */
396 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
397 &DeviceNode->InstancePath);
398
399 /* Report the device's arrival to umpnpmgr */
400 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
401 &DeviceNode->InstancePath);
402
403 DPRINT("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath);
404
405 /* Return the PDO */
406 if (DeviceObject) *DeviceObject = Pdo;
407
408 return STATUS_SUCCESS;
409 }
410
411 /*
412 * @halfplemented
413 */
414 NTSTATUS
415 NTAPI
416 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject,
417 IN PCM_RESOURCE_LIST DriverList OPTIONAL,
418 IN ULONG DriverListSize OPTIONAL,
419 IN PDEVICE_OBJECT DeviceObject OPTIONAL,
420 IN PCM_RESOURCE_LIST DeviceList OPTIONAL,
421 IN ULONG DeviceListSize OPTIONAL,
422 OUT PBOOLEAN ConflictDetected)
423 {
424 PCM_RESOURCE_LIST ResourceList;
425 NTSTATUS Status;
426
427 *ConflictDetected = FALSE;
428
429 if (!DriverList && !DeviceList)
430 return STATUS_INVALID_PARAMETER;
431
432 /* Find the real list */
433 if (!DriverList)
434 ResourceList = DeviceList;
435 else
436 ResourceList = DriverList;
437
438 /* Look for a resource conflict */
439 Status = IopDetectResourceConflict(ResourceList, FALSE, NULL);
440 if (Status == STATUS_CONFLICTING_ADDRESSES)
441 {
442 /* Oh noes */
443 *ConflictDetected = TRUE;
444 }
445 else if (NT_SUCCESS(Status))
446 {
447 /* Looks like we're good to go */
448
449 /* TODO: Claim the resources in the ResourceMap */
450 }
451
452 return Status;
453 }
454
455 VOID
456 NTAPI
457 IopSetEvent(IN PVOID Context)
458 {
459 PKEVENT Event = Context;
460
461 /* Set the event */
462 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
463 }
464
465 /*
466 * @implemented
467 */
468 NTSTATUS
469 NTAPI
470 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject,
471 IN PVOID NotificationStructure)
472 {
473 KEVENT NotifyEvent;
474 NTSTATUS Status, NotifyStatus;
475 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
476
477 ASSERT(notifyStruct);
478
479 /* Check for valid PDO */
480 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
481 {
482 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0);
483 }
484
485 /* FileObject must be null. PnP will fill in it */
486 ASSERT(notifyStruct->FileObject == NULL);
487
488 /* Do not handle system PnP events */
489 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
490 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
491 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
492 {
493 return STATUS_INVALID_DEVICE_REQUEST;
494 }
495
496 if (notifyStruct->Version != 1)
497 {
498 return STATUS_INVALID_DEVICE_REQUEST;
499 }
500
501 /* Initialize even that will let us know when PnP will have finished notify */
502 KeInitializeEvent(&NotifyEvent, NotificationEvent, FALSE);
503
504 Status = PpSetCustomTargetEvent(PhysicalDeviceObject, &NotifyEvent, &NotifyStatus, NULL, NULL, notifyStruct);
505 /* If no error, wait for the notify to end and return the status of the notify and not of the event */
506 if (NT_SUCCESS(Status))
507 {
508 KeWaitForSingleObject(&NotifyEvent, Executive, KernelMode, FALSE, NULL);
509 Status = NotifyStatus;
510 }
511
512 return Status;
513 }
514
515 /*
516 * @implemented
517 */
518 NTSTATUS
519 NTAPI
520 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject,
521 IN PVOID NotificationStructure,
522 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
523 IN PVOID Context OPTIONAL)
524 {
525 PINTERNAL_WORK_QUEUE_ITEM Item = NULL;
526 PTARGET_DEVICE_CUSTOM_NOTIFICATION notifyStruct = (PTARGET_DEVICE_CUSTOM_NOTIFICATION)NotificationStructure;
527
528 ASSERT(notifyStruct);
529
530 /* Check for valid PDO */
531 if (!IopIsValidPhysicalDeviceObject(PhysicalDeviceObject))
532 {
533 KeBugCheckEx(PNP_DETECTED_FATAL_ERROR, 0x2, (ULONG_PTR)PhysicalDeviceObject, 0, 0);
534 }
535
536 /* FileObject must be null. PnP will fill in it */
537 ASSERT(notifyStruct->FileObject == NULL);
538
539 /* Do not handle system PnP events */
540 if ((RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_QUERY_REMOVE), sizeof(GUID)) != sizeof(GUID)) ||
541 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_CANCELLED), sizeof(GUID)) != sizeof(GUID)) ||
542 (RtlCompareMemory(&(notifyStruct->Event), &(GUID_TARGET_DEVICE_REMOVE_COMPLETE), sizeof(GUID)) != sizeof(GUID)))
543 {
544 return STATUS_INVALID_DEVICE_REQUEST;
545 }
546
547 if (notifyStruct->Version != 1)
548 {
549 return STATUS_INVALID_DEVICE_REQUEST;
550 }
551
552 /* We need to store all the data given by the caller with the WorkItem, so use our own struct */
553 Item = ExAllocatePoolWithTag(NonPagedPool, sizeof(INTERNAL_WORK_QUEUE_ITEM), ' pP');
554 if (!Item) return STATUS_INSUFFICIENT_RESOURCES;
555
556 /* Initialize all stuff */
557 ObReferenceObject(PhysicalDeviceObject);
558 Item->NotificationStructure = notifyStruct;
559 Item->PhysicalDeviceObject = PhysicalDeviceObject;
560 Item->Callback = Callback;
561 Item->Context = Context;
562 ExInitializeWorkItem(&(Item->WorkItem), IopReportTargetDeviceChangeAsyncWorker, Item);
563
564 /* Finally, queue the item, our work here is done */
565 ExQueueWorkItem(&(Item->WorkItem), DelayedWorkQueue);
566
567 return STATUS_PENDING;
568 }