410812e9adb99327ee68b80d49fa0cd4b8bac650
[reactos.git] / reactos / drivers / bus / acpi / cmbatt / cmbpnp.c
1 /*
2 * PROJECT: ReactOS ACPI-Compliant Control Method Battery
3 * LICENSE: BSD - See COPYING.ARM in the top level directory
4 * FILE: boot/drivers/bus/acpi/cmbatt/cmbpnp.c
5 * PURPOSE: Plug-and-Play IOCTL/IRP Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "cmbatt.h"
12
13 /* FUNCTIONS ******************************************************************/
14
15 NTSTATUS
16 NTAPI
17 CmBattIoCompletion(IN PDEVICE_OBJECT DeviceObject,
18 IN PIRP Irp,
19 IN PKEVENT Event)
20 {
21 if (CmBattDebug & 2) DbgPrint("CmBattIoCompletion: Event (%x)\n", Event);
22
23 /* Set the completion event */
24 KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
25 return STATUS_MORE_PROCESSING_REQUIRED;
26 }
27
28 NTSTATUS
29 NTAPI
30 CmBattGetAcpiInterfaces(IN PDEVICE_OBJECT DeviceObject,
31 IN OUT PACPI_INTERFACE_STANDARD AcpiInterface)
32 {
33 PIRP Irp;
34 NTSTATUS Status;
35 PIO_STACK_LOCATION IoStackLocation;
36 KEVENT Event;
37
38 /* Allocate the IRP */
39 Irp = IoAllocateIrp(DeviceObject->StackSize, 0);
40 if (!Irp)
41 {
42 /* Fail */
43 if (CmBattDebug & 0xC)
44 DbgPrint("CmBattGetAcpiInterfaces: Failed to allocate Irp\n");
45 return STATUS_INSUFFICIENT_RESOURCES;
46 }
47
48 /* Set default error code */
49 Irp->IoStatus.Status = STATUS_NOT_SUPPORTED;
50
51 /* Build the query */
52 IoStackLocation = IoGetNextIrpStackLocation(Irp);
53 IoStackLocation->MinorFunction = IRP_MN_QUERY_INTERFACE;
54 IoStackLocation->Parameters.QueryInterface.InterfaceType = &GUID_ACPI_INTERFACE_STANDARD;
55 IoStackLocation->Parameters.QueryInterface.Size = sizeof(ACPI_INTERFACE_STANDARD);
56 IoStackLocation->Parameters.QueryInterface.Version = 1;
57 IoStackLocation->Parameters.QueryInterface.Interface = (PINTERFACE)AcpiInterface;
58 IoStackLocation->Parameters.QueryInterface.InterfaceSpecificData = NULL;
59
60 /* Set default ACPI interface data */
61 AcpiInterface->Size = sizeof(ACPI_INTERFACE_STANDARD);
62 AcpiInterface->Version = 1;
63
64 /* Initialize our wait event */
65 KeInitializeEvent(&Event, SynchronizationEvent, 0);
66
67 /* Set the completion routine */
68 IoCopyCurrentIrpStackLocationToNext(Irp);
69 IoSetCompletionRoutine(Irp,
70 (PVOID)CmBattIoCompletion,
71 &Event,
72 TRUE,
73 TRUE,
74 TRUE);
75
76 /* Now call ACPI */
77 Status = IoCallDriver(DeviceObject, Irp);
78 if (Status == STATUS_PENDING)
79 {
80 /* Wait for completion */
81 KeWaitForSingleObject(&Event,
82 Executive,
83 KernelMode,
84 FALSE,
85 NULL);
86 Status = Irp->IoStatus.Status;
87 }
88
89 /* Free the IRP */
90 IoFreeIrp(Irp);
91
92 /* Return status */
93 if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0xC))
94 DbgPrint("CmBattGetAcpiInterfaces: Could not get ACPI driver interfaces, status = %x\n", Status);
95 return Status;
96 }
97
98 VOID
99 NTAPI
100 CmBattDestroyFdo(IN PDEVICE_OBJECT DeviceObject)
101 {
102 PAGED_CODE();
103 if (CmBattDebug & 0x220) DbgPrint("CmBattDestroyFdo, Battery.\n");
104
105 /* Delete the device */
106 IoDeleteDevice(DeviceObject);
107 if (CmBattDebug & 0x220) DbgPrint("CmBattDestroyFdo: done.\n");
108 }
109
110 NTSTATUS
111 NTAPI
112 CmBattRemoveDevice(IN PDEVICE_OBJECT DeviceObject,
113 IN PIRP Irp)
114 {
115 PCMBATT_DEVICE_EXTENSION DeviceExtension;
116 PVOID Context;
117 DeviceExtension = DeviceObject->DeviceExtension;
118 if (CmBattDebug & 2)
119 DbgPrint("CmBattRemoveDevice: CmBatt (%x), Type %d, _UID %d\n",
120 DeviceExtension,
121 DeviceExtension->FdoType,
122 DeviceExtension->DeviceId);
123
124 /* Make sure it's safe to go ahead */
125 IoReleaseRemoveLockAndWait(&DeviceExtension->RemoveLock, 0);
126
127 /* Check for pending power IRP */
128 if (DeviceExtension->PowerIrp)
129 {
130 /* Cancel and clear */
131 IoCancelIrp(DeviceExtension->PowerIrp);
132 DeviceExtension->PowerIrp = NULL;
133 }
134
135 /* Check what type of FDO is being removed */
136 Context = DeviceExtension->AcpiInterface.Context;
137 if (DeviceExtension->FdoType == CmBattBattery)
138 {
139 /* Unregister battery FDO */
140 DeviceExtension->AcpiInterface.UnregisterForDeviceNotifications(Context,
141 (PVOID)CmBattNotifyHandler);
142 CmBattWmiDeRegistration(DeviceExtension);
143 if (!NT_SUCCESS(BatteryClassUnload(DeviceExtension->ClassData))) ASSERT(FALSE);
144 }
145 else
146 {
147 /* Unregister AC adapter FDO */
148 DeviceExtension->AcpiInterface.UnregisterForDeviceNotifications(Context,
149 (PVOID)CmBattNotifyHandler);
150 CmBattWmiDeRegistration(DeviceExtension);
151 AcAdapterPdo = NULL;
152 }
153
154 /* Detach and delete */
155 IoDetachDevice(DeviceExtension->AttachedDevice);
156 IoDeleteDevice(DeviceExtension->DeviceObject);
157 return STATUS_SUCCESS;
158 }
159
160 NTSTATUS
161 NTAPI
162 CmBattPowerDispatch(PDEVICE_OBJECT DeviceObject,
163 PIRP Irp)
164 {
165 UNIMPLEMENTED;
166 return STATUS_NOT_IMPLEMENTED;
167 }
168
169 NTSTATUS
170 NTAPI
171 CmBattPnpDispatch(PDEVICE_OBJECT DeviceObject,
172 PIRP Irp)
173 {
174 UNIMPLEMENTED;
175 return STATUS_NOT_IMPLEMENTED;
176 }
177
178 NTSTATUS
179 NTAPI
180 CmBattCreateFdo(IN PDRIVER_OBJECT DriverObject,
181 IN PDEVICE_OBJECT DeviceObject,
182 IN ULONG DeviceExtensionSize,
183 IN PDEVICE_OBJECT *NewDeviceObject)
184 {
185 PDEVICE_OBJECT FdoDeviceObject;
186 HANDLE KeyHandle;
187 PCMBATT_DEVICE_EXTENSION FdoExtension;
188 UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
189 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
190 NTSTATUS Status;
191 UNICODE_STRING KeyString;
192 ULONG UniqueId;
193 ULONG ResultLength;
194 PAGED_CODE();
195 if (CmBattDebug & 0x220) DbgPrint("CmBattCreateFdo: Entered\n");
196
197 /* Get unique ID */
198 Status = CmBattGetUniqueId(DeviceObject, &UniqueId);
199 if (!NT_SUCCESS(Status))
200 {
201 /* Assume 0 */
202 UniqueId = 0;
203 if (CmBattDebug & 2)
204 DbgPrint("CmBattCreateFdo: Error %x from _UID, assuming unit #0\n", Status);
205 }
206
207 /* Create the FDO */
208 Status = IoCreateDevice(DriverObject,
209 DeviceExtensionSize,
210 0,
211 FILE_DEVICE_BATTERY,
212 FILE_DEVICE_SECURE_OPEN,
213 0,
214 &FdoDeviceObject);
215 if (!NT_SUCCESS(Status))
216 {
217 /* Fail */
218 if (CmBattDebug & 0xC)
219 DbgPrint("CmBattCreateFdo: error (0x%x) creating device object\n", Status);
220 return Status;
221 }
222
223 /* Set FDO flags */
224 FdoDeviceObject->Flags |= DO_BUFFERED_IO;
225 FdoDeviceObject->Flags |= DO_MAP_IO_BUFFER;
226 FdoDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
227
228 /* Initialize the extension */
229 FdoExtension = FdoDeviceObject->DeviceExtension;
230 RtlZeroMemory(FdoExtension, DeviceExtensionSize);
231 FdoExtension->DeviceObject = FdoDeviceObject;
232 FdoExtension->FdoDeviceObject = FdoDeviceObject;
233 FdoExtension->PdoDeviceObject = DeviceObject;
234
235 /* Attach to ACPI */
236 FdoExtension->AttachedDevice = IoAttachDeviceToDeviceStack(FdoDeviceObject,
237 DeviceObject);
238 if (!FdoExtension->AttachedDevice)
239 {
240 /* Destroy and fail */
241 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
242 if (CmBattDebug & 0xC)
243 DbgPrint("CmBattCreateFdo: IoAttachDeviceToDeviceStack failed.\n");
244 return STATUS_UNSUCCESSFUL;
245 }
246
247 /* Get ACPI interface for EVAL */
248 Status = CmBattGetAcpiInterfaces(FdoExtension->AttachedDevice,
249 &FdoExtension->AcpiInterface);
250 if (!FdoExtension->AttachedDevice)
251 {
252 /* Detach, destroy, and fail */
253 IoDetachDevice(FdoExtension->AttachedDevice);
254 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
255 if (CmBattDebug & 0xC)
256 DbgPrint("CmBattCreateFdo: Could not get ACPI interfaces: %x\n", Status);
257 return STATUS_UNSUCCESSFUL;
258 }
259
260 /* Setup the rest of the extension */
261 ExInitializeFastMutex(&FdoExtension->FastMutex);
262 IoInitializeRemoveLock(&FdoExtension->RemoveLock, 0, 0, 0);
263 FdoExtension->HandleCount = 0;
264 FdoExtension->WaitWakeEnable = FALSE;
265 FdoExtension->DeviceId = UniqueId;
266 FdoExtension->DeviceName = NULL;
267 FdoExtension->DelayNotification = FALSE;
268 FdoExtension->ArFlag = 0;
269
270 /* Open the device key */
271 Status = IoOpenDeviceRegistryKey(DeviceObject,
272 PLUGPLAY_REGKEY_DEVICE,
273 KEY_READ,
274 &KeyHandle);
275 if (NT_SUCCESS(Status))
276 {
277 /* Read wait wake value */
278 RtlInitUnicodeString(&KeyString, L"WaitWakeEnabled");
279 Status = ZwQueryValueKey(KeyHandle,
280 &KeyString,
281 KeyValuePartialInformation,
282 PartialInfo,
283 sizeof(Buffer),
284 &ResultLength);
285 if (NT_SUCCESS(Status))
286 {
287 /* Set value */
288 FdoExtension->WaitWakeEnable = *(PULONG)PartialInfo->Data;
289 }
290
291 /* Close the handle */
292 ZwClose(KeyHandle);
293 }
294
295 /* Return success and the new FDO */
296 *NewDeviceObject = FdoDeviceObject;
297 if (CmBattDebug & 0x220)
298 DbgPrint("CmBattCreateFdo: Created FDO %x\n", FdoDeviceObject);
299 return STATUS_SUCCESS;
300 }
301
302 NTSTATUS
303 NTAPI
304 CmBattAddBattery(IN PDRIVER_OBJECT DriverObject,
305 IN PDEVICE_OBJECT DeviceObject)
306 {
307 BATTERY_MINIPORT_INFO MiniportInfo;
308 NTSTATUS Status;
309 PDEVICE_OBJECT FdoDeviceObject;
310 PCMBATT_DEVICE_EXTENSION FdoExtension;
311 PAGED_CODE();
312 if (CmBattDebug & 0x220)
313 DbgPrint("CmBattAddBattery: pdo %x\n", DeviceObject);
314
315 /* Create the FDO */
316 Status = CmBattCreateFdo(DriverObject,
317 DeviceObject,
318 sizeof(CMBATT_DEVICE_EXTENSION),
319 &FdoDeviceObject);
320 if (!NT_SUCCESS(Status))
321 {
322 if (CmBattDebug & 0xC)
323 DbgPrint("CmBattAddBattery: error (0x%x) creating Fdo\n", Status);
324 return Status;
325 }
326
327 /* Build the FDO extensio, check if we support trip points */
328 FdoExtension = FdoDeviceObject->DeviceExtension;
329 FdoExtension->FdoType = CmBattBattery;
330 FdoExtension->Started = 0;
331 FdoExtension->NotifySent = TRUE;
332 InterlockedExchange(&FdoExtension->ArLockValue, 0);
333 FdoExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
334 FdoExtension->Tag = 0;
335 FdoExtension->InterruptTime = KeQueryInterruptTime();
336 FdoExtension->TripPointSet = CmBattSetTripPpoint(FdoExtension, 0) !=
337 STATUS_OBJECT_NAME_NOT_FOUND;
338
339 /* Setup the battery miniport information structure */
340 RtlZeroMemory(&MiniportInfo, sizeof(MiniportInfo));
341 MiniportInfo.Pdo = DeviceObject;
342 MiniportInfo.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
343 MiniportInfo.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
344 MiniportInfo.Context = FdoExtension;
345 MiniportInfo.QueryTag = (PVOID)CmBattQueryTag;
346 MiniportInfo.QueryInformation = (PVOID)CmBattQueryInformation;
347 MiniportInfo.SetInformation = NULL;
348 MiniportInfo.QueryStatus = (PVOID)CmBattQueryStatus;
349 MiniportInfo.SetStatusNotify = (PVOID)CmBattSetStatusNotify;
350 MiniportInfo.DisableStatusNotify = (PVOID)CmBattDisableStatusNotify;
351 MiniportInfo.DeviceName = FdoExtension->DeviceName;
352
353 /* Register with the class driver */
354 Status = BatteryClassInitializeDevice(&MiniportInfo, &FdoExtension->ClassData);
355 if (!NT_SUCCESS(Status))
356 {
357 IoDetachDevice(FdoExtension->AttachedDevice);
358 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
359 if (CmBattDebug & 0xC)
360 DbgPrint("CmBattAddBattery: error (0x%x) registering with class\n", Status);
361 return Status;
362 }
363
364 /* Register WMI */
365 Status = CmBattWmiRegistration(FdoExtension);
366 if (!NT_SUCCESS(Status))
367 {
368 if (CmBattDebug & 0xC)
369 DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
370 return Status;
371 }
372
373 /* Register ACPI */
374 Status = FdoExtension->AcpiInterface.RegisterForDeviceNotifications(FdoExtension->AcpiInterface.Context,
375 (PVOID)CmBattNotifyHandler,
376 FdoExtension);
377 if (!NT_SUCCESS(Status))
378 {
379 CmBattWmiDeRegistration(FdoExtension);
380 BatteryClassUnload(FdoExtension->ClassData);
381 IoDetachDevice(FdoExtension->AttachedDevice);
382 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
383 if (CmBattDebug & 0xC)
384 DbgPrint("CmBattAddBattery: Could not register for battery notify, status = %Lx\n", Status);
385 }
386
387 /* Return status */
388 return Status;
389 }
390
391 NTSTATUS
392 NTAPI
393 CmBattAddAcAdapter(IN PDRIVER_OBJECT DriverObject,
394 IN PDEVICE_OBJECT PdoDeviceObject)
395 {
396 PDEVICE_OBJECT FdoDeviceObject;
397 NTSTATUS Status;
398 PCMBATT_DEVICE_EXTENSION DeviceExtension;
399 PAGED_CODE();
400 if (CmBattDebug & 0x220)
401 DbgPrint("CmBattAddAcAdapter: pdo %x\n", PdoDeviceObject);
402
403 /* Check if we already have an AC adapter */
404 if (AcAdapterPdo)
405 {
406 /* Don't do anything */
407 if (CmBattDebug & 0xC)
408 DbgPrint("CmBatt: Second AC adapter found. Current version of driver only supports 1 aadapter.\n");
409 }
410 else
411 {
412 /* Set this as the AC adapter's PDO */
413 AcAdapterPdo = PdoDeviceObject;
414 }
415
416 /* Create the FDO for the adapter */
417 Status = CmBattCreateFdo(DriverObject,
418 PdoDeviceObject,
419 sizeof(CMBATT_DEVICE_EXTENSION),
420 &FdoDeviceObject);
421 if (!NT_SUCCESS(Status))
422 {
423 /* Fail */
424 if (CmBattDebug & 0xC)
425 DbgPrint("CmBattAddAcAdapter: error (0x%x) creating Fdo\n", Status);
426 return Status;
427 }
428
429 /* Set the type and do WMI registration */
430 DeviceExtension = FdoDeviceObject->DeviceExtension;
431 DeviceExtension->FdoType = CmBattAcAdapter;
432 Status = CmBattWmiRegistration(DeviceExtension);
433 if (!NT_SUCCESS(Status))
434 {
435 /* We can go on without WMI */
436 if (CmBattDebug & 0xC)
437 DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
438 }
439
440 /* Register with ACPI */
441 Status = DeviceExtension->AcpiInterface.RegisterForDeviceNotifications(DeviceExtension->AcpiInterface.Context,
442 (PVOID)CmBattNotifyHandler,
443 DeviceExtension);
444 if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0xC))
445 DbgPrint("CmBattAddAcAdapter: Could not register for power notify, status = %Lx\n", Status);
446
447 /* Send the first manual notification */
448 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
449 return STATUS_SUCCESS;
450 }
451
452 NTSTATUS
453 NTAPI
454 CmBattAddDevice(IN PDRIVER_OBJECT DriverObject,
455 IN PDEVICE_OBJECT PdoDeviceObject)
456 {
457 NTSTATUS Status;
458 HANDLE KeyHandle;
459 ULONG ResultLength;
460 UNICODE_STRING KeyString;
461 UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
462 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
463 ULONG PowerSourceType;
464 PAGED_CODE();
465 if (CmBattDebug & 0x220)
466 DbgPrint("CmBattAddDevice: Entered with pdo %x\n", PdoDeviceObject);
467
468 /* Make sure we have a PDO */
469 if (!PdoDeviceObject)
470 {
471 /* Should not be having as one */
472 if (CmBattDebug & 0x24) DbgPrint("CmBattAddDevice: Asked to do detection\n");
473 return STATUS_NO_MORE_ENTRIES;
474 }
475
476 /* Open the driver key */
477 Status = IoOpenDeviceRegistryKey(PdoDeviceObject,
478 PLUGPLAY_REGKEY_DRIVER,
479 KEY_READ,
480 &KeyHandle);
481 if (!NT_SUCCESS(Status))
482 {
483 if (CmBattDebug & 0xC)
484 DbgPrint("CmBattAddDevice: Could not get the software branch: %x\n", Status);
485 return Status;
486 }
487
488 /* Read the power source type */
489 RtlInitUnicodeString(&KeyString, L"PowerSourceType");
490 Status = ZwQueryValueKey(KeyHandle,
491 &KeyString,
492 KeyValuePartialInformation,
493 PartialInfo,
494 sizeof(Buffer),
495 &ResultLength);
496 ZwClose(KeyHandle);
497 if (!NT_SUCCESS(Status))
498 {
499 /* We need the data, fail without it */
500 if (CmBattDebug & 0xC)
501 DbgPrint("CmBattAddDevice: Could not read the power type identifier: %x\n", Status);
502 return Status;
503 }
504
505 /* Check what kind of power source this is */
506 PowerSourceType = *(PULONG)PartialInfo->Data;
507 if (PowerSourceType == 1)
508 {
509 /* Create an AC adapter */
510 Status = CmBattAddAcAdapter(DriverObject, PdoDeviceObject);
511 }
512 else if (PowerSourceType == 0)
513 {
514 /* Create a battery */
515 Status = CmBattAddBattery(DriverObject, PdoDeviceObject);
516 }
517 else
518 {
519 /* Unknown type, fail */
520 if (CmBattDebug & 0xC)
521 DbgPrint("CmBattAddDevice: Invalid POWER_SOURCE_TYPE == %d \n", PowerSourceType);
522 return STATUS_UNSUCCESSFUL;
523 }
524
525 /* Return whatever the FDO creation routine did */
526 return Status;
527 }
528
529 /* EOF */