[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 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14
15 NTSTATUS
16 NTAPI
17 IopCreateDeviceKeyPath(IN PCUNICODE_STRING RegistryPath,
18 IN ULONG CreateOptions,
19 OUT PHANDLE Handle);
20
21 NTSTATUS
22 IopAssignDeviceResources(
23 IN PDEVICE_NODE DeviceNode,
24 OUT ULONG *pRequiredSize);
25
26 NTSTATUS
27 IopSetDeviceInstanceData(HANDLE InstanceKey,
28 PDEVICE_NODE DeviceNode);
29
30 NTSTATUS
31 IopTranslateDeviceResources(
32 IN PDEVICE_NODE DeviceNode,
33 IN ULONG RequiredSize);
34
35 NTSTATUS
36 IopActionInterrogateDeviceStack(PDEVICE_NODE DeviceNode,
37 PVOID Context);
38
39 NTSTATUS
40 IopUpdateResourceMapForPnPDevice(
41 IN PDEVICE_NODE DeviceNode);
42
43 NTSTATUS
44 IopDetectResourceConflict(
45 IN PCM_RESOURCE_LIST ResourceList);
46
47 /* PRIVATE FUNCTIONS *********************************************************/
48
49 PWCHAR
50 IopGetInterfaceTypeString(INTERFACE_TYPE IfType)
51 {
52 switch (IfType)
53 {
54 case Internal:
55 return L"Internal";
56
57 case Isa:
58 return L"Isa";
59
60 case Eisa:
61 return L"Eisa";
62
63 case MicroChannel:
64 return L"MicroChannel";
65
66 case TurboChannel:
67 return L"TurboChannel";
68
69 case PCIBus:
70 return L"PCIBus";
71
72 case VMEBus:
73 return L"VMEBus";
74
75 case NuBus:
76 return L"NuBus";
77
78 case PCMCIABus:
79 return L"PCMCIABus";
80
81 case CBus:
82 return L"CBus";
83
84 case MPIBus:
85 return L"MPIBus";
86
87 case MPSABus:
88 return L"MPSABus";
89
90 case ProcessorInternal:
91 return L"ProcessorInternal";
92
93 case PNPISABus:
94 return L"PNPISABus";
95
96 case PNPBus:
97 return L"PNPBus";
98
99 case Vmcs:
100 return L"Vmcs";
101
102 default:
103 DPRINT1("Invalid bus type: %d\n", IfType);
104 return NULL;
105 }
106 }
107
108 /* PUBLIC FUNCTIONS **********************************************************/
109
110 /*
111 * @implemented
112 */
113 NTSTATUS
114 NTAPI
115 IoReportDetectedDevice(IN PDRIVER_OBJECT DriverObject,
116 IN INTERFACE_TYPE LegacyBusType,
117 IN ULONG BusNumber,
118 IN ULONG SlotNumber,
119 IN PCM_RESOURCE_LIST ResourceList,
120 IN PIO_RESOURCE_REQUIREMENTS_LIST ResourceRequirements OPTIONAL,
121 IN BOOLEAN ResourceAssigned,
122 IN OUT PDEVICE_OBJECT *DeviceObject OPTIONAL)
123 {
124 PDEVICE_NODE DeviceNode;
125 PDEVICE_OBJECT Pdo;
126 NTSTATUS Status;
127 HANDLE InstanceKey;
128 ULONG RequiredLength;
129 UNICODE_STRING ValueName, ServiceName;
130 WCHAR HardwareId[256];
131 PWCHAR IfString;
132 ULONG IdLength;
133
134 DPRINT("IoReportDetectedDevice (DeviceObject %p, *DeviceObject %p)\n",
135 DeviceObject, DeviceObject ? *DeviceObject : NULL);
136
137 /* Create the service name (eg. ACPI_HAL) */
138 ServiceName.Buffer = DriverObject->DriverName.Buffer +
139 sizeof(DRIVER_ROOT_NAME) / sizeof(WCHAR) - 1;
140 ServiceName.Length = DriverObject->DriverName.Length -
141 sizeof(DRIVER_ROOT_NAME) + sizeof(WCHAR);
142 ServiceName.MaximumLength = ServiceName.Length;
143
144 /* If the interface type is unknown, treat it as internal */
145 if (LegacyBusType == InterfaceTypeUndefined)
146 LegacyBusType = Internal;
147
148 /* Get the string equivalent of the interface type */
149 IfString = IopGetInterfaceTypeString(LegacyBusType);
150
151 /* If NULL is returned then it's a bad type */
152 if (!IfString)
153 return STATUS_INVALID_PARAMETER;
154
155 /* We use the caller's PDO if they supplied one */
156 if (DeviceObject && *DeviceObject)
157 {
158 Pdo = *DeviceObject;
159 DeviceNode = IopGetDeviceNode(*DeviceObject);
160 }
161 else
162 {
163 /* Create the PDO */
164 Status = PnpRootCreateDevice(&ServiceName,
165 &Pdo,
166 NULL);
167 if (!NT_SUCCESS(Status))
168 {
169 DPRINT("PnpRootCreateDevice() failed (Status 0x%08lx)\n", Status);
170 return Status;
171 }
172
173 /* Create the device node for the new PDO */
174 Status = IopCreateDeviceNode(IopRootDeviceNode,
175 Pdo,
176 NULL,
177 &DeviceNode);
178
179 if (!NT_SUCCESS(Status))
180 {
181 DPRINT("IopCreateDeviceNode() failed (Status 0x%08lx)\n", Status);
182 return Status;
183 }
184 }
185
186 /* We don't call AddDevice for devices reported this way */
187 IopDeviceNodeSetFlag(DeviceNode, DNF_ADDED);
188
189 /* We don't send IRP_MN_START_DEVICE */
190 IopDeviceNodeSetFlag(DeviceNode, DNF_STARTED);
191
192 /* This is a legacy driver for this device */
193 IopDeviceNodeSetFlag(DeviceNode, DNF_LEGACY_DRIVER);
194
195 /* Perform a manual configuration of our device */
196 IopActionInterrogateDeviceStack(DeviceNode, DeviceNode->Parent);
197 IopActionConfigureChildServices(DeviceNode, DeviceNode->Parent);
198
199 /* Open a handle to the instance path key */
200 Status = IopCreateDeviceKeyPath(&DeviceNode->InstancePath, 0, &InstanceKey);
201 if (!NT_SUCCESS(Status))
202 return Status;
203
204 /* Add DETECTEDInterfaceType\DriverName */
205 IdLength = 0;
206 IdLength += swprintf(&HardwareId[IdLength],
207 L"DETECTED%ls\\%wZ",
208 IfString,
209 &ServiceName);
210 HardwareId[IdLength++] = UNICODE_NULL;
211
212 /* Add DETECTED\DriverName */
213 IdLength += swprintf(&HardwareId[IdLength],
214 L"DETECTED\\%wZ",
215 &ServiceName);
216 HardwareId[IdLength++] = UNICODE_NULL;
217
218 /* Terminate the string with another null */
219 HardwareId[IdLength] = UNICODE_NULL;
220
221 /* Store the value for CompatibleIDs */
222 RtlInitUnicodeString(&ValueName, L"CompatibleIDs");
223 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_MULTI_SZ, HardwareId, IdLength * sizeof(WCHAR));
224 if (!NT_SUCCESS(Status))
225 {
226 DPRINT("Failed to write the compatible IDs: 0x%x\n", Status);
227 ZwClose(InstanceKey);
228 return Status;
229 }
230
231 /* Add a hardware ID if the driver didn't report one */
232 RtlInitUnicodeString(&ValueName, L"HardwareID");
233 if (ZwQueryValueKey(InstanceKey, &ValueName, KeyValueBasicInformation, NULL, 0, &RequiredLength) == STATUS_OBJECT_NAME_NOT_FOUND)
234 {
235 /* Just use our most specific compatible ID */
236 IdLength = 0;
237 IdLength += swprintf(&HardwareId[IdLength],
238 L"DETECTED%ls\\%wZ",
239 IfString,
240 &ServiceName);
241 HardwareId[++IdLength] = UNICODE_NULL;
242
243 /* Write the value to the registry */
244 Status = ZwSetValueKey(InstanceKey, &ValueName, 0, REG_SZ, HardwareId, IdLength * sizeof(WCHAR));
245 if (!NT_SUCCESS(Status))
246 {
247 DPRINT("Failed to write the hardware ID: 0x%x\n", Status);
248 ZwClose(InstanceKey);
249 return Status;
250 }
251 }
252
253 /* Assign the resources to the device node */
254 DeviceNode->BootResources = ResourceList;
255 DeviceNode->ResourceRequirements = ResourceRequirements;
256
257 /* Set appropriate flags */
258 if (DeviceNode->BootResources)
259 IopDeviceNodeSetFlag(DeviceNode, DNF_HAS_BOOT_CONFIG);
260
261 if (DeviceNode->ResourceRequirements)
262 IopDeviceNodeSetFlag(DeviceNode, DNF_RESOURCE_REPORTED);
263 else
264 IopDeviceNodeSetFlag(DeviceNode, DNF_NO_RESOURCE_REQUIRED);
265
266 /* Write the resource information to the registry */
267 IopSetDeviceInstanceData(InstanceKey, DeviceNode);
268
269 /* Close the instance key handle */
270 ZwClose(InstanceKey);
271
272 /* If the caller didn't get the resources assigned for us, do it now */
273 if (!ResourceAssigned)
274 {
275 IopDeviceNodeSetFlag(DeviceNode, DNF_ASSIGNING_RESOURCES);
276 Status = IopAssignDeviceResources(DeviceNode, &RequiredLength);
277 if (NT_SUCCESS(Status))
278 {
279 Status = IopTranslateDeviceResources(DeviceNode, RequiredLength);
280 if (NT_SUCCESS(Status))
281 Status = IopUpdateResourceMapForPnPDevice(DeviceNode);
282 }
283 IopDeviceNodeClearFlag(DeviceNode, DNF_ASSIGNING_RESOURCES);
284
285 /* See if we failed */
286 if (!NT_SUCCESS(Status))
287 {
288 DPRINT("Assigning resources failed: 0x%x\n", Status);
289 return Status;
290 }
291 }
292
293 /* Report the device's enumeration to umpnpmgr */
294 IopQueueTargetDeviceEvent(&GUID_DEVICE_ENUMERATED,
295 &DeviceNode->InstancePath);
296
297 /* Report the device's arrival to umpnpmgr */
298 IopQueueTargetDeviceEvent(&GUID_DEVICE_ARRIVAL,
299 &DeviceNode->InstancePath);
300
301 DPRINT1("Reported device: %S (%wZ)\n", HardwareId, &DeviceNode->InstancePath);
302
303 /* Return the PDO */
304 if (DeviceObject) *DeviceObject = Pdo;
305
306 return STATUS_SUCCESS;
307 }
308
309 /*
310 * @halfplemented
311 */
312 NTSTATUS
313 NTAPI
314 IoReportResourceForDetection(IN PDRIVER_OBJECT DriverObject,
315 IN PCM_RESOURCE_LIST DriverList OPTIONAL,
316 IN ULONG DriverListSize OPTIONAL,
317 IN PDEVICE_OBJECT DeviceObject OPTIONAL,
318 IN PCM_RESOURCE_LIST DeviceList OPTIONAL,
319 IN ULONG DeviceListSize OPTIONAL,
320 OUT PBOOLEAN ConflictDetected)
321 {
322 PCM_RESOURCE_LIST ResourceList;
323 NTSTATUS Status;
324
325 *ConflictDetected = FALSE;
326
327 if (!DriverList && !DeviceList)
328 return STATUS_INVALID_PARAMETER;
329
330 /* Find the real list */
331 if (!DriverList)
332 ResourceList = DeviceList;
333 else
334 ResourceList = DriverList;
335
336 /* Look for a resource conflict */
337 Status = IopDetectResourceConflict(ResourceList);
338 if (Status == STATUS_CONFLICTING_ADDRESSES)
339 {
340 /* Oh noes */
341 *ConflictDetected = TRUE;
342 }
343 else if (NT_SUCCESS(Status))
344 {
345 /* Looks like we're good to go */
346
347 /* TODO: Claim the resources in the ResourceMap */
348 }
349
350 return Status;
351 }
352
353 VOID
354 NTAPI
355 IopSetEvent(IN PVOID Context)
356 {
357 PKEVENT Event = Context;
358
359 /* Set the event */
360 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
361 }
362
363 /*
364 * @unimplemented
365 */
366 NTSTATUS
367 NTAPI
368 IoReportTargetDeviceChange(IN PDEVICE_OBJECT PhysicalDeviceObject,
369 IN PVOID NotificationStructure)
370 {
371 UNIMPLEMENTED;
372 return STATUS_NOT_IMPLEMENTED;
373 }
374
375 /*
376 * @unimplemented
377 */
378 NTSTATUS
379 NTAPI
380 IoReportTargetDeviceChangeAsynchronous(IN PDEVICE_OBJECT PhysicalDeviceObject,
381 IN PVOID NotificationStructure,
382 IN PDEVICE_CHANGE_COMPLETE_CALLBACK Callback OPTIONAL,
383 IN PVOID Context OPTIONAL)
384 {
385 UNIMPLEMENTED;
386 return STATUS_NOT_IMPLEMENTED;
387 }