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