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