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