[CMBATT]: Implement CmBattPowerDispatch for power requests.
[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(IN PDEVICE_OBJECT DeviceObject,
163 IN PIRP Irp)
164 {
165 PIO_STACK_LOCATION IoStackLocation;
166 PCMBATT_DEVICE_EXTENSION DeviceExtension;
167 NTSTATUS Status;
168 if (CmBattDebug & 0x210) DbgPrint("CmBattPowerDispatch\n");
169
170 /* Get stack location and device extension */
171 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
172 DeviceExtension = DeviceObject->DeviceExtension;
173 switch (IoStackLocation->MinorFunction)
174 {
175 case IRP_MN_WAIT_WAKE:
176 if (CmBattDebug & 0x10)
177 DbgPrint("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n");
178 break;
179
180 case IRP_MN_POWER_SEQUENCE:
181 if (CmBattDebug & 0x10)
182 DbgPrint("CmBattPowerDispatch: IRP_MN_POWER_SEQUENCE\n");
183 break;
184
185 case IRP_MN_QUERY_POWER:
186 if (CmBattDebug & 0x10)
187 DbgPrint("CmBattPowerDispatch: IRP_MN_WAIT_WAKE\n");
188 break;
189
190 case IRP_MN_SET_POWER:
191 if (CmBattDebug & 0x10)
192 DbgPrint("CmBattPowerDispatch: IRP_MN_SET_POWER type: %d, State: %d \n",
193 IoStackLocation->Parameters.Power.Type,
194 IoStackLocation->Parameters.Power.State);
195 break;
196
197 default:
198
199 if (CmBattDebug & 1)
200 DbgPrint("CmBattPowerDispatch: minor %d\n", IoStackLocation->MinorFunction);
201 break;
202 }
203
204 /* Start the next IRP and see if we're attached */
205 PoStartNextPowerIrp(Irp);
206 if (DeviceExtension->AttachedDevice)
207 {
208 /* Call ACPI */
209 IoSkipCurrentIrpStackLocation(Irp);
210 Status = PoCallDriver(DeviceExtension->AttachedDevice, Irp);
211 }
212 else
213 {
214 /* Complete the request here */
215 Status = Irp->IoStatus.Status;
216 IofCompleteRequest(Irp, IO_NO_INCREMENT);
217 }
218
219 /* Return status */
220 return Status;
221 }
222
223 NTSTATUS
224 NTAPI
225 CmBattPnpDispatch(PDEVICE_OBJECT DeviceObject,
226 PIRP Irp)
227 {
228 UNIMPLEMENTED;
229 return STATUS_NOT_IMPLEMENTED;
230 }
231
232 NTSTATUS
233 NTAPI
234 CmBattCreateFdo(IN PDRIVER_OBJECT DriverObject,
235 IN PDEVICE_OBJECT DeviceObject,
236 IN ULONG DeviceExtensionSize,
237 IN PDEVICE_OBJECT *NewDeviceObject)
238 {
239 PDEVICE_OBJECT FdoDeviceObject;
240 HANDLE KeyHandle;
241 PCMBATT_DEVICE_EXTENSION FdoExtension;
242 UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
243 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
244 NTSTATUS Status;
245 UNICODE_STRING KeyString;
246 ULONG UniqueId;
247 ULONG ResultLength;
248 PAGED_CODE();
249 if (CmBattDebug & 0x220) DbgPrint("CmBattCreateFdo: Entered\n");
250
251 /* Get unique ID */
252 Status = CmBattGetUniqueId(DeviceObject, &UniqueId);
253 if (!NT_SUCCESS(Status))
254 {
255 /* Assume 0 */
256 UniqueId = 0;
257 if (CmBattDebug & 2)
258 DbgPrint("CmBattCreateFdo: Error %x from _UID, assuming unit #0\n", Status);
259 }
260
261 /* Create the FDO */
262 Status = IoCreateDevice(DriverObject,
263 DeviceExtensionSize,
264 0,
265 FILE_DEVICE_BATTERY,
266 FILE_DEVICE_SECURE_OPEN,
267 0,
268 &FdoDeviceObject);
269 if (!NT_SUCCESS(Status))
270 {
271 /* Fail */
272 if (CmBattDebug & 0xC)
273 DbgPrint("CmBattCreateFdo: error (0x%x) creating device object\n", Status);
274 return Status;
275 }
276
277 /* Set FDO flags */
278 FdoDeviceObject->Flags |= DO_BUFFERED_IO;
279 FdoDeviceObject->Flags |= DO_MAP_IO_BUFFER;
280 FdoDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;
281
282 /* Initialize the extension */
283 FdoExtension = FdoDeviceObject->DeviceExtension;
284 RtlZeroMemory(FdoExtension, DeviceExtensionSize);
285 FdoExtension->DeviceObject = FdoDeviceObject;
286 FdoExtension->FdoDeviceObject = FdoDeviceObject;
287 FdoExtension->PdoDeviceObject = DeviceObject;
288
289 /* Attach to ACPI */
290 FdoExtension->AttachedDevice = IoAttachDeviceToDeviceStack(FdoDeviceObject,
291 DeviceObject);
292 if (!FdoExtension->AttachedDevice)
293 {
294 /* Destroy and fail */
295 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
296 if (CmBattDebug & 0xC)
297 DbgPrint("CmBattCreateFdo: IoAttachDeviceToDeviceStack failed.\n");
298 return STATUS_UNSUCCESSFUL;
299 }
300
301 /* Get ACPI interface for EVAL */
302 Status = CmBattGetAcpiInterfaces(FdoExtension->AttachedDevice,
303 &FdoExtension->AcpiInterface);
304 if (!FdoExtension->AttachedDevice)
305 {
306 /* Detach, destroy, and fail */
307 IoDetachDevice(FdoExtension->AttachedDevice);
308 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
309 if (CmBattDebug & 0xC)
310 DbgPrint("CmBattCreateFdo: Could not get ACPI interfaces: %x\n", Status);
311 return STATUS_UNSUCCESSFUL;
312 }
313
314 /* Setup the rest of the extension */
315 ExInitializeFastMutex(&FdoExtension->FastMutex);
316 IoInitializeRemoveLock(&FdoExtension->RemoveLock, 0, 0, 0);
317 FdoExtension->HandleCount = 0;
318 FdoExtension->WaitWakeEnable = FALSE;
319 FdoExtension->DeviceId = UniqueId;
320 FdoExtension->DeviceName = NULL;
321 FdoExtension->DelayNotification = FALSE;
322 FdoExtension->ArFlag = 0;
323
324 /* Open the device key */
325 Status = IoOpenDeviceRegistryKey(DeviceObject,
326 PLUGPLAY_REGKEY_DEVICE,
327 KEY_READ,
328 &KeyHandle);
329 if (NT_SUCCESS(Status))
330 {
331 /* Read wait wake value */
332 RtlInitUnicodeString(&KeyString, L"WaitWakeEnabled");
333 Status = ZwQueryValueKey(KeyHandle,
334 &KeyString,
335 KeyValuePartialInformation,
336 PartialInfo,
337 sizeof(Buffer),
338 &ResultLength);
339 if (NT_SUCCESS(Status))
340 {
341 /* Set value */
342 FdoExtension->WaitWakeEnable = *(PULONG)PartialInfo->Data;
343 }
344
345 /* Close the handle */
346 ZwClose(KeyHandle);
347 }
348
349 /* Return success and the new FDO */
350 *NewDeviceObject = FdoDeviceObject;
351 if (CmBattDebug & 0x220)
352 DbgPrint("CmBattCreateFdo: Created FDO %x\n", FdoDeviceObject);
353 return STATUS_SUCCESS;
354 }
355
356 NTSTATUS
357 NTAPI
358 CmBattAddBattery(IN PDRIVER_OBJECT DriverObject,
359 IN PDEVICE_OBJECT DeviceObject)
360 {
361 BATTERY_MINIPORT_INFO MiniportInfo;
362 NTSTATUS Status;
363 PDEVICE_OBJECT FdoDeviceObject;
364 PCMBATT_DEVICE_EXTENSION FdoExtension;
365 PAGED_CODE();
366 if (CmBattDebug & 0x220)
367 DbgPrint("CmBattAddBattery: pdo %x\n", DeviceObject);
368
369 /* Create the FDO */
370 Status = CmBattCreateFdo(DriverObject,
371 DeviceObject,
372 sizeof(CMBATT_DEVICE_EXTENSION),
373 &FdoDeviceObject);
374 if (!NT_SUCCESS(Status))
375 {
376 if (CmBattDebug & 0xC)
377 DbgPrint("CmBattAddBattery: error (0x%x) creating Fdo\n", Status);
378 return Status;
379 }
380
381 /* Build the FDO extensio, check if we support trip points */
382 FdoExtension = FdoDeviceObject->DeviceExtension;
383 FdoExtension->FdoType = CmBattBattery;
384 FdoExtension->Started = 0;
385 FdoExtension->NotifySent = TRUE;
386 InterlockedExchange(&FdoExtension->ArLockValue, 0);
387 FdoExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
388 FdoExtension->Tag = 0;
389 FdoExtension->InterruptTime = KeQueryInterruptTime();
390 FdoExtension->TripPointSet = CmBattSetTripPpoint(FdoExtension, 0) !=
391 STATUS_OBJECT_NAME_NOT_FOUND;
392
393 /* Setup the battery miniport information structure */
394 RtlZeroMemory(&MiniportInfo, sizeof(MiniportInfo));
395 MiniportInfo.Pdo = DeviceObject;
396 MiniportInfo.MajorVersion = BATTERY_CLASS_MAJOR_VERSION;
397 MiniportInfo.MinorVersion = BATTERY_CLASS_MINOR_VERSION;
398 MiniportInfo.Context = FdoExtension;
399 MiniportInfo.QueryTag = (PVOID)CmBattQueryTag;
400 MiniportInfo.QueryInformation = (PVOID)CmBattQueryInformation;
401 MiniportInfo.SetInformation = NULL;
402 MiniportInfo.QueryStatus = (PVOID)CmBattQueryStatus;
403 MiniportInfo.SetStatusNotify = (PVOID)CmBattSetStatusNotify;
404 MiniportInfo.DisableStatusNotify = (PVOID)CmBattDisableStatusNotify;
405 MiniportInfo.DeviceName = FdoExtension->DeviceName;
406
407 /* Register with the class driver */
408 Status = BatteryClassInitializeDevice(&MiniportInfo, &FdoExtension->ClassData);
409 if (!NT_SUCCESS(Status))
410 {
411 IoDetachDevice(FdoExtension->AttachedDevice);
412 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
413 if (CmBattDebug & 0xC)
414 DbgPrint("CmBattAddBattery: error (0x%x) registering with class\n", Status);
415 return Status;
416 }
417
418 /* Register WMI */
419 Status = CmBattWmiRegistration(FdoExtension);
420 if (!NT_SUCCESS(Status))
421 {
422 if (CmBattDebug & 0xC)
423 DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
424 return Status;
425 }
426
427 /* Register ACPI */
428 Status = FdoExtension->AcpiInterface.RegisterForDeviceNotifications(FdoExtension->AcpiInterface.Context,
429 (PVOID)CmBattNotifyHandler,
430 FdoExtension);
431 if (!NT_SUCCESS(Status))
432 {
433 CmBattWmiDeRegistration(FdoExtension);
434 BatteryClassUnload(FdoExtension->ClassData);
435 IoDetachDevice(FdoExtension->AttachedDevice);
436 CmBattDestroyFdo(FdoExtension->FdoDeviceObject);
437 if (CmBattDebug & 0xC)
438 DbgPrint("CmBattAddBattery: Could not register for battery notify, status = %Lx\n", Status);
439 }
440
441 /* Return status */
442 return Status;
443 }
444
445 NTSTATUS
446 NTAPI
447 CmBattAddAcAdapter(IN PDRIVER_OBJECT DriverObject,
448 IN PDEVICE_OBJECT PdoDeviceObject)
449 {
450 PDEVICE_OBJECT FdoDeviceObject;
451 NTSTATUS Status;
452 PCMBATT_DEVICE_EXTENSION DeviceExtension;
453 PAGED_CODE();
454 if (CmBattDebug & 0x220)
455 DbgPrint("CmBattAddAcAdapter: pdo %x\n", PdoDeviceObject);
456
457 /* Check if we already have an AC adapter */
458 if (AcAdapterPdo)
459 {
460 /* Don't do anything */
461 if (CmBattDebug & 0xC)
462 DbgPrint("CmBatt: Second AC adapter found. Current version of driver only supports 1 aadapter.\n");
463 }
464 else
465 {
466 /* Set this as the AC adapter's PDO */
467 AcAdapterPdo = PdoDeviceObject;
468 }
469
470 /* Create the FDO for the adapter */
471 Status = CmBattCreateFdo(DriverObject,
472 PdoDeviceObject,
473 sizeof(CMBATT_DEVICE_EXTENSION),
474 &FdoDeviceObject);
475 if (!NT_SUCCESS(Status))
476 {
477 /* Fail */
478 if (CmBattDebug & 0xC)
479 DbgPrint("CmBattAddAcAdapter: error (0x%x) creating Fdo\n", Status);
480 return Status;
481 }
482
483 /* Set the type and do WMI registration */
484 DeviceExtension = FdoDeviceObject->DeviceExtension;
485 DeviceExtension->FdoType = CmBattAcAdapter;
486 Status = CmBattWmiRegistration(DeviceExtension);
487 if (!NT_SUCCESS(Status))
488 {
489 /* We can go on without WMI */
490 if (CmBattDebug & 0xC)
491 DbgPrint("CmBattAddBattery: Could not register as a WMI provider, status = %Lx\n", Status);
492 }
493
494 /* Register with ACPI */
495 Status = DeviceExtension->AcpiInterface.RegisterForDeviceNotifications(DeviceExtension->AcpiInterface.Context,
496 (PVOID)CmBattNotifyHandler,
497 DeviceExtension);
498 if (!(NT_SUCCESS(Status)) && (CmBattDebug & 0xC))
499 DbgPrint("CmBattAddAcAdapter: Could not register for power notify, status = %Lx\n", Status);
500
501 /* Send the first manual notification */
502 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
503 return STATUS_SUCCESS;
504 }
505
506 NTSTATUS
507 NTAPI
508 CmBattAddDevice(IN PDRIVER_OBJECT DriverObject,
509 IN PDEVICE_OBJECT PdoDeviceObject)
510 {
511 NTSTATUS Status;
512 HANDLE KeyHandle;
513 ULONG ResultLength;
514 UNICODE_STRING KeyString;
515 UCHAR Buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
516 PKEY_VALUE_PARTIAL_INFORMATION PartialInfo = (PVOID)Buffer;
517 ULONG PowerSourceType;
518 PAGED_CODE();
519 if (CmBattDebug & 0x220)
520 DbgPrint("CmBattAddDevice: Entered with pdo %x\n", PdoDeviceObject);
521
522 /* Make sure we have a PDO */
523 if (!PdoDeviceObject)
524 {
525 /* Should not be having as one */
526 if (CmBattDebug & 0x24) DbgPrint("CmBattAddDevice: Asked to do detection\n");
527 return STATUS_NO_MORE_ENTRIES;
528 }
529
530 /* Open the driver key */
531 Status = IoOpenDeviceRegistryKey(PdoDeviceObject,
532 PLUGPLAY_REGKEY_DRIVER,
533 KEY_READ,
534 &KeyHandle);
535 if (!NT_SUCCESS(Status))
536 {
537 if (CmBattDebug & 0xC)
538 DbgPrint("CmBattAddDevice: Could not get the software branch: %x\n", Status);
539 return Status;
540 }
541
542 /* Read the power source type */
543 RtlInitUnicodeString(&KeyString, L"PowerSourceType");
544 Status = ZwQueryValueKey(KeyHandle,
545 &KeyString,
546 KeyValuePartialInformation,
547 PartialInfo,
548 sizeof(Buffer),
549 &ResultLength);
550 ZwClose(KeyHandle);
551 if (!NT_SUCCESS(Status))
552 {
553 /* We need the data, fail without it */
554 if (CmBattDebug & 0xC)
555 DbgPrint("CmBattAddDevice: Could not read the power type identifier: %x\n", Status);
556 return Status;
557 }
558
559 /* Check what kind of power source this is */
560 PowerSourceType = *(PULONG)PartialInfo->Data;
561 if (PowerSourceType == 1)
562 {
563 /* Create an AC adapter */
564 Status = CmBattAddAcAdapter(DriverObject, PdoDeviceObject);
565 }
566 else if (PowerSourceType == 0)
567 {
568 /* Create a battery */
569 Status = CmBattAddBattery(DriverObject, PdoDeviceObject);
570 }
571 else
572 {
573 /* Unknown type, fail */
574 if (CmBattDebug & 0xC)
575 DbgPrint("CmBattAddDevice: Invalid POWER_SOURCE_TYPE == %d \n", PowerSourceType);
576 return STATUS_UNSUCCESSFUL;
577 }
578
579 /* Return whatever the FDO creation routine did */
580 return Status;
581 }
582
583 /* EOF */