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/cmbatt.c
5 * PURPOSE: Main Initialization Code and IRP Handling
6 * PROGRAMMERS: ReactOS Portable Systems Group
7 */
8
9 /* INCLUDES *******************************************************************/
10
11 #include "cmbatt.h"
12
13 #include <debug.h>
14
15 /* GLOBALS ********************************************************************/
16
17 ULONG CmBattDebug;
18 PCALLBACK_OBJECT CmBattPowerCallBackObject;
19 PVOID CmBattPowerCallBackRegistration;
20 UNICODE_STRING GlobalRegistryPath;
21 KTIMER CmBattWakeDpcTimerObject;
22 KDPC CmBattWakeDpcObject;
23 PDEVICE_OBJECT AcAdapterPdo;
24 LARGE_INTEGER CmBattWakeDpcDelay;
25
26 /* FUNCTIONS ******************************************************************/
27
28 VOID
29 NTAPI
CmBattPowerCallBack(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Action,IN ULONG Value)30 CmBattPowerCallBack(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
31 IN ULONG Action,
32 IN ULONG Value)
33 {
34 BOOLEAN Cancelled;
35 PDEVICE_OBJECT DeviceObject;
36 if (CmBattDebug & 0x10)
37 DbgPrint("CmBattPowerCallBack: action: %d, value: %d \n", Action, Value);
38
39 /* Check if a transition is going to happen */
40 if (Action == PO_CB_SYSTEM_STATE_LOCK)
41 {
42 /* We have just re-entered S0: call the wake DPC in 10 seconds */
43 if (Value == 1)
44 {
45 if (CmBattDebug & 0x10)
46 DbgPrint("CmBattPowerCallBack: Calling CmBattWakeDpc after 10 seconds.\n");
47 Cancelled = KeSetTimer(&CmBattWakeDpcTimerObject, CmBattWakeDpcDelay, &CmBattWakeDpcObject);
48 if (CmBattDebug & 0x10)
49 DbgPrint("CmBattPowerCallBack: timerCanceled = %d.\n", Cancelled);
50 }
51 else if (Value == 0)
52 {
53 /* We are exiting the S0 state: loop all devices to set the delay flag */
54 if (CmBattDebug & 0x10)
55 DbgPrint("CmBattPowerCallBack: Delaying Notifications\n");
56 for (DeviceObject = DeviceExtension->DeviceObject;
57 DeviceObject;
58 DeviceObject = DeviceObject->NextDevice)
59 {
60 /* Set the delay flag */
61 DeviceExtension = DeviceObject->DeviceExtension;
62 DeviceExtension->DelayNotification = TRUE;
63 }
64 }
65 else if (CmBattDebug & 0x10)
66 {
67 /* Unknown value */
68 DbgPrint("CmBattPowerCallBack: unknown argument2 = %08x\n", Value);
69 }
70 }
71 }
72
73 VOID
74 NTAPI
CmBattWakeDpc(IN PKDPC Dpc,IN PCMBATT_DEVICE_EXTENSION FdoExtension,IN PVOID SystemArgument1,IN PVOID SystemArgument2)75 CmBattWakeDpc(IN PKDPC Dpc,
76 IN PCMBATT_DEVICE_EXTENSION FdoExtension,
77 IN PVOID SystemArgument1,
78 IN PVOID SystemArgument2)
79 {
80 PDEVICE_OBJECT CurrentObject;
81 BOOLEAN AcNotify = FALSE;
82 PCMBATT_DEVICE_EXTENSION DeviceExtension;
83 ULONG ArFlag;
84 if (CmBattDebug & 2) DbgPrint("CmBattWakeDpc: Entered.\n");
85
86 /* Loop all device objects */
87 for (CurrentObject = FdoExtension->DeviceObject;
88 CurrentObject;
89 CurrentObject = CurrentObject->NextDevice)
90 {
91 /* Turn delay flag off, we're back in S0 */
92 DeviceExtension = CurrentObject->DeviceExtension;
93 DeviceExtension->DelayNotification = 0;
94
95 /* Check if this is an AC adapter */
96 if (DeviceExtension->FdoType == CmBattAcAdapter)
97 {
98 /* Was there a pending notify? */
99 if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
100 {
101 /* We'll send a notify on the next pass */
102 AcNotify = TRUE;
103 DeviceExtension->ArFlag = 0;
104 if (CmBattDebug & 0x20)
105 DbgPrint("CmBattWakeDpc: AC adapter notified\n");
106 }
107 }
108 }
109
110 /* Loop the device objects again */
111 for (CurrentObject = FdoExtension->DeviceObject;
112 CurrentObject;
113 CurrentObject = CurrentObject->NextDevice)
114 {
115 /* Check if this is a battery */
116 DeviceExtension = CurrentObject->DeviceExtension;
117 if (DeviceExtension->FdoType == CmBattBattery)
118 {
119 /* Check what ARs are pending */
120 ArFlag = DeviceExtension->ArFlag;
121 if (CmBattDebug & 0x20)
122 DbgPrint("CmBattWakeDpc: Performing delayed ARs: %01x\n", ArFlag);
123
124 /* Insert notification, clear the lock value */
125 if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
126
127 /* Removal, clear the battery tag */
128 if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
129
130 /* Notification (or AC/DC adapter change from first pass above) */
131 if ((ArFlag & CMBATT_AR_NOTIFY) || (AcNotify))
132 {
133 /* Notify the class driver */
134 BatteryClassStatusNotify(DeviceExtension->ClassData);
135 }
136 }
137 }
138 }
139
140 VOID
141 NTAPI
CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG NotifyValue)142 CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
143 IN ULONG NotifyValue)
144 {
145 ULONG ArFlag;
146 PCMBATT_DEVICE_EXTENSION FdoExtension;
147 PDEVICE_OBJECT DeviceObject;
148
149 if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_PNP_INFO))
150 DbgPrint("CmBattNotifyHandler: CmBatt 0x%08x Type %d Number %d Notify Value: %x\n",
151 DeviceExtension,
152 DeviceExtension->FdoType,
153 DeviceExtension->DeviceId,
154 NotifyValue);
155
156 /* Check what kind of notification was received */
157 switch (NotifyValue)
158 {
159 /* ACPI Specification says is sends a "Bus Check" when power source changes */
160 case ACPI_BUS_CHECK:
161
162 /* We treat it as possible physical change */
163 DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY | CMBATT_AR_INSERT);
164 if ((DeviceExtension->Tag) &&
165 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
166 DbgPrint("CmBattNotifyHandler: Received battery #%x insertion, but tag was not invalid.\n",
167 DeviceExtension->DeviceId);
168 break;
169
170 /* Status of the battery has changed */
171 case ACPI_BATT_NOTIFY_STATUS:
172
173 /* All we'll do is notify the class driver */
174 DeviceExtension->ArFlag |= CMBATT_AR_NOTIFY;
175 break;
176
177 /* Information on the battery has changed, such as physical presence */
178 case ACPI_DEVICE_CHECK:
179 case ACPI_BATT_NOTIFY_INFO:
180
181 /* Reset all state and let the class driver re-evaluate it all */
182 DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY |
183 CMBATT_AR_INSERT |
184 CMBATT_AR_REMOVE);
185 break;
186
187 default:
188
189 if (CmBattDebug & CMBATT_PNP_INFO)
190 DbgPrint("CmBattNotifyHandler: Unknown Notify Value: %x\n", NotifyValue);
191 }
192
193 /* Check if we're supposed to delay the notification till later */
194 if (DeviceExtension->DelayNotification)
195 {
196 /* We'll handle this when we get a status query later on */
197 if (CmBattDebug & CMBATT_PNP_INFO)
198 DbgPrint("CmBattNotifyHandler: Notification delayed: ARs = %01x\n",
199 DeviceExtension->ArFlag);
200 return;
201 }
202
203 /* We're going to handle this now */
204 if (CmBattDebug & CMBATT_PNP_INFO)
205 DbgPrint("CmBattNotifyHandler: Performing ARs: %01x\n", DeviceExtension->ArFlag);
206
207 /* Check if this is a battery or AC adapter notification */
208 if (DeviceExtension->FdoType == CmBattBattery)
209 {
210 /* Reset the current trip point */
211 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
212
213 /* Check what ARs have to be done */
214 ArFlag = DeviceExtension->ArFlag;
215
216 /* New battery inserted, reset lock value */
217 if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
218
219 /* Check if the battery may have been removed */
220 if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
221
222 /* Check if there's been any sort of change to the battery */
223 if (ArFlag & CMBATT_AR_NOTIFY)
224 {
225 /* We'll probably end up re-evaluating _BIF and _BST */
226 DeviceExtension->NotifySent = TRUE;
227 BatteryClassStatusNotify(DeviceExtension->ClassData);
228 }
229 }
230 else if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
231 {
232 /* The only known notification is AC/DC change. Loop device objects. */
233 for (DeviceObject = DeviceExtension->FdoDeviceObject->DriverObject->DeviceObject;
234 DeviceObject;
235 DeviceObject = DeviceObject->NextDevice)
236 {
237 /* Is this a battery? */
238 FdoExtension = DeviceObject->DeviceExtension;
239 if (FdoExtension->FdoType == CmBattBattery)
240 {
241 /* Send a notification to the class driver */
242 FdoExtension->NotifySent = TRUE;
243 BatteryClassStatusNotify(FdoExtension->ClassData);
244 }
245 }
246 }
247
248 /* ARs have been processed */
249 DeviceExtension->ArFlag = 0;
250 }
251
252 VOID
253 NTAPI
CmBattUnload(IN PDRIVER_OBJECT DriverObject)254 CmBattUnload(IN PDRIVER_OBJECT DriverObject)
255 {
256 if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattUnload: \n");
257
258 /* Check if we have a registered power callback */
259 if (CmBattPowerCallBackObject)
260 {
261 /* Get rid of it */
262 ExUnregisterCallback(CmBattPowerCallBackRegistration);
263 ObDereferenceObject(CmBattPowerCallBackObject);
264 }
265
266 /* Free the registry buffer if it exists */
267 if (GlobalRegistryPath.Buffer) ExFreePool(GlobalRegistryPath.Buffer);
268
269 /* Make sure we don't still have references to the DO */
270 if ((DriverObject->DeviceObject) && (CmBattDebug & CMBATT_GENERIC_WARNING))
271 {
272 DbgPrint("Unload called before all devices removed.\n");
273 }
274 }
275
276 /**
277 * @brief
278 * Retrieves the static information of the battery.
279 *
280 * @param[in] DeviceExtension
281 * A pointer to a Control Method (CM) battery device extension.
282 * It is used for debugging purposes.
283 *
284 * @param[out] UseBix
285 * A pointer to a boolean value, returned to caller. This can return
286 * TRUE if this machine supports the _BIX method, FALSE otherwise.
287 *
288 * @param[out] BattInfo
289 * A pointer to a structure that contains the static info of the
290 * battery. ONLY ONE type of information is filled. See Remarks.
291 *
292 * @return
293 * Returns STATUS_INSUFFICIENT_RESOURCES if there is no enough
294 * memory to allocate for the static info buffer. Returns
295 * STATUS_SUCCESS if the operation has succeeded. Otherwise an
296 * error NTSTATUS code is returned.
297 *
298 * @remarks
299 * It is important to note that a machine can only support one method,
300 * _BIX or _BIF. Starting with ACPI 4.0, _BIF has become deprecated.
301 * The caller MUST INSPECT the boolean value, ExtendedData, in order
302 * to determine if the machine has returned the extended information
303 * data or not.
304 */
305 static
306 NTSTATUS
CmBattGetBattStaticInfo(_In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_Out_ PBOOLEAN UseBix,_Outptr_ PACPI_BATT_STATIC_INFO * BattInfo)307 CmBattGetBattStaticInfo(
308 _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
309 _Out_ PBOOLEAN UseBix,
310 _Outptr_ PACPI_BATT_STATIC_INFO *BattInfo)
311 {
312 NTSTATUS Status;
313 ACPI_BIF_DATA BifData;
314 ACPI_BIX_DATA BixData;
315 PACPI_BATT_STATIC_INFO Info;
316
317 /* Allocate pool space for the static information */
318 Info = ExAllocatePoolZero(PagedPool,
319 sizeof(*Info),
320 CMBATT_BATT_STATIC_INFO_TAG);
321 if (Info == NULL)
322 {
323 return STATUS_INSUFFICIENT_RESOURCES;
324 }
325
326 /* Assume this machine supports the _BIX method */
327 *UseBix = TRUE;
328
329 /* Retrieve extended battery static info from _BIX method */
330 Status = CmBattGetBixData(DeviceExtension, &BixData);
331 if (!NT_SUCCESS(Status))
332 {
333 /*
334 * It failed. This can be expected because not every machine supports
335 * _BIX, especially the older machines which do not support ACPI 4.0.
336 * Fallback to _BIF at this point.
337 */
338 Status = CmBattGetBifData(DeviceExtension, &BifData);
339 if (!NT_SUCCESS(Status))
340 {
341 /* That failed too, time to punt */
342 ExFreePoolWithTag(Info, CMBATT_BATT_STATIC_INFO_TAG);
343 return Status;
344 }
345
346 /* Acknowledge the caller it will be going to use _BIF */
347 *UseBix = FALSE;
348 Info->BifData = BifData;
349 }
350 else
351 {
352 Info->BixData = BixData;
353 }
354
355 /* Return the battery static info to caller */
356 Info->ExtendedData = *UseBix;
357 *BattInfo = Info;
358 return Status;
359 }
360
361 /**
362 * @brief
363 * Verifies the extended battery information (_BIX) and translates
364 * such data to the BATTERY_INFORMATION structure.
365 *
366 * @param[in] DeviceExtension
367 * A pointer to a Control Method (CM) battery device extension.
368 * It is used to gather _BIX data.
369 *
370 * @param[in,out] Info
371 * A pointer to a structure of which this function fills in
372 * battery information that can be by other battery miniport
373 * drivers, such as the Composite Battery driver.
374 */
375 static
376 VOID
CmBattVerifyBixData(_In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_Inout_ PBATTERY_INFORMATION Info)377 CmBattVerifyBixData(
378 _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
379 _Inout_ PBATTERY_INFORMATION Info)
380 {
381 ULONG DesignVoltage;
382 ACPI_BIX_DATA BixData = DeviceExtension->BattInfo.BixData;
383
384 /* Copy the battery info data */
385 Info->Technology = BixData.BatteryTechnology;
386 Info->CycleCount = BixData.CycleCount;
387 RtlCopyMemory(Info->Chemistry, BixData.BatteryType, 4);
388
389 /* Check if the power stats are reported in ampere or watts */
390 if (BixData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
391 {
392 /*
393 * We have got power stats in milli-ampere but ReactOS expects the values
394 * to be reported in milli-watts, so we have to convert them.
395 * In order to do so we must expect the design voltage of the battery
396 * is not unknown.
397 */
398 DesignVoltage = BixData.DesignVoltage;
399 if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage != 0))
400 {
401 /* Convert the design capacity */
402 Info->DesignedCapacity = CONVERT_BATT_INFO(BixData.DesignCapacity, DesignVoltage);
403
404 /* Convert the full charged capacity */
405 Info->FullChargedCapacity = CONVERT_BATT_INFO(BixData.LastFullCapacity, DesignVoltage);
406
407 /* Convert the low capacity alarm (DefaultAlert1) */
408 Info->DefaultAlert1 = CONVERT_BATT_INFO(BixData.DesignCapacityLow, DesignVoltage);
409
410 /* Convert the designed capacity warning alarm (DefaultAlert2) */
411 Info->DefaultAlert2 = CONVERT_BATT_INFO(BixData.DesignCapacityWarning, DesignVoltage);
412 }
413 else
414 {
415 /*
416 * Without knowing the nominal designed voltage of the battery
417 * we cannot determine the power consumption of this battery.
418 */
419 Info->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
420 Info->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
421 Info->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
422 Info->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
423 }
424 }
425 else
426 {
427 /* The stats are in milli-watts, use them directly */
428 Info->DesignedCapacity = BixData.DesignCapacity;
429 Info->FullChargedCapacity = BixData.LastFullCapacity;
430 Info->DefaultAlert1 = BixData.DesignCapacityLow;
431 Info->DefaultAlert2 = BixData.DesignCapacityWarning;
432 }
433 }
434
435 /**
436 * @brief
437 * Verifies the battery information (_BIF) and translates
438 * such data to the BATTERY_INFORMATION structure.
439 *
440 * @param[in] DeviceExtension
441 * A pointer to a Control Method (CM) battery device extension.
442 * It is used to gather _BIF data.
443 *
444 * @param[in,out] Info
445 * A pointer to a structure of which this function fills in
446 * battery information that can be by other battery miniport
447 * drivers, such as the Composite Battery driver.
448 */
449 static
450 VOID
CmBattVerifyBifData(_In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_Inout_ PBATTERY_INFORMATION Info)451 CmBattVerifyBifData(
452 _In_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
453 _Inout_ PBATTERY_INFORMATION Info)
454 {
455 ULONG DesignVoltage;
456 ACPI_BIF_DATA BifData = DeviceExtension->BattInfo.BifData;
457
458 /* Copy the battery info data, CycleCount is not supported in _BIF */
459 Info->Technology = BifData.BatteryTechnology;
460 Info->CycleCount = 0;
461 RtlCopyMemory(Info->Chemistry, BifData.BatteryType, 4);
462
463 /* Check if the power stats are reported in ampere or watts */
464 if (BifData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
465 {
466 /*
467 * We have got power stats in milli-ampere but ReactOS expects the values
468 * to be reported in milli-watts, so we have to convert them.
469 * In order to do so we must expect the design voltage of the battery
470 * is not unknown.
471 */
472 DesignVoltage = BifData.DesignVoltage;
473 if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage != 0))
474 {
475 /* Convert the design capacity */
476 Info->DesignedCapacity = CONVERT_BATT_INFO(BifData.DesignCapacity, DesignVoltage);
477
478 /* Convert the full charged capacity */
479 Info->FullChargedCapacity = CONVERT_BATT_INFO(BifData.LastFullCapacity, DesignVoltage);
480
481 /* Convert the low capacity alarm (DefaultAlert1) */
482 Info->DefaultAlert1 = CONVERT_BATT_INFO(BifData.DesignCapacityLow, DesignVoltage);
483
484 /* Convert the designed capacity warning alarm (DefaultAlert2) */
485 Info->DefaultAlert2 = CONVERT_BATT_INFO(BifData.DesignCapacityWarning, DesignVoltage);
486 }
487 else
488 {
489 /*
490 * Without knowing the nominal designed voltage of the battery
491 * we cannot determine the power consumption of this battery.
492 */
493 Info->DesignedCapacity = BATTERY_UNKNOWN_CAPACITY;
494 Info->FullChargedCapacity = BATTERY_UNKNOWN_CAPACITY;
495 Info->DefaultAlert1 = BATTERY_UNKNOWN_CAPACITY;
496 Info->DefaultAlert2 = BATTERY_UNKNOWN_CAPACITY;
497 }
498 }
499 else
500 {
501 /* The stats are in milli-watts, use them directly */
502 Info->DesignedCapacity = BifData.DesignCapacity;
503 Info->FullChargedCapacity = BifData.LastFullCapacity;
504 Info->DefaultAlert1 = BifData.DesignCapacityLow;
505 Info->DefaultAlert2 = BifData.DesignCapacityWarning;
506 }
507 }
508
509 NTSTATUS
510 NTAPI
CmBattVerifyStaticInfo(_Inout_ PCMBATT_DEVICE_EXTENSION DeviceExtension,_In_ ULONG BatteryTag)511 CmBattVerifyStaticInfo(
512 _Inout_ PCMBATT_DEVICE_EXTENSION DeviceExtension,
513 _In_ ULONG BatteryTag)
514 {
515 NTSTATUS Status;
516 BOOLEAN UseBix;
517 PACPI_BATT_STATIC_INFO BattInfo;
518 PBATTERY_INFORMATION Info = &DeviceExtension->BatteryInformation;
519
520 /* FIXME: This function is not fully implemented, more checks need to be implemented */
521 UNREFERENCED_PARAMETER(BatteryTag);
522
523 /* Retrieve the battery static info */
524 Status = CmBattGetBattStaticInfo(DeviceExtension, &UseBix, &BattInfo);
525 if (NT_SUCCESS(Status))
526 {
527 /* Initialize the battery information data */
528 RtlZeroMemory(Info, sizeof(*Info));
529 Info->Capabilities = BATTERY_SYSTEM_BATTERY;
530
531 /* Copy the static information to the device extension of the battery */
532 RtlCopyMemory(&DeviceExtension->BattInfo, BattInfo, sizeof(*BattInfo));
533
534 /* Check if the data from _BIX has to be used or not */
535 if (UseBix)
536 {
537 CmBattVerifyBixData(DeviceExtension, Info);
538 }
539 else
540 {
541 CmBattVerifyBifData(DeviceExtension, Info);
542 }
543
544 /* Free the static information buffer as we already copied it */
545 ExFreePoolWithTag(BattInfo, CMBATT_BATT_STATIC_INFO_TAG);
546 }
547
548 return Status;
549 }
550
551 NTSTATUS
552 NTAPI
CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)553 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,
554 IN PIRP Irp)
555 {
556 NTSTATUS Status = STATUS_SUCCESS;
557 PIO_STACK_LOCATION IoStackLocation;
558 UCHAR Major;
559 ULONG Count;
560 PCMBATT_DEVICE_EXTENSION DeviceExtension;
561 PAGED_CODE();
562 if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattOpenClose\n");
563
564 /* Grab the device extension and lock it */
565 DeviceExtension = DeviceObject->DeviceExtension;
566 ExAcquireFastMutex(&DeviceExtension->FastMutex);
567
568 /* Check if someone is trying to open a device that doesn't exist yet */
569 Count = DeviceExtension->HandleCount;
570 if (Count == 0xFFFFFFFF)
571 {
572 /* Fail the request */
573 Status = STATUS_NO_SUCH_DEVICE;
574 if (CmBattDebug & CMBATT_PNP_INFO)
575 {
576 DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
577 DeviceExtension->Tag);
578 }
579 goto Complete;
580 }
581
582 /* Check if this is an open or close */
583 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
584 Major = IoStackLocation->MajorFunction;
585 if (Major == IRP_MJ_CREATE)
586 {
587 /* Increment the open count */
588 DeviceExtension->HandleCount = Count + 1;
589 if (CmBattDebug & CMBATT_PNP_INFO)
590 {
591 DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
592 DeviceExtension->DeviceId, Count + 1);
593 }
594 }
595 else if (Major == IRP_MJ_CLOSE)
596 {
597 /* Decrement the open count */
598 DeviceExtension->HandleCount = Count - 1;
599 if (CmBattDebug & CMBATT_PNP_INFO)
600 {
601 DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
602 DeviceExtension->DeviceId, Count + 1);
603 }
604 }
605
606 Complete:
607 /* Release lock and complete request */
608 ExReleaseFastMutex(&DeviceExtension->FastMutex);
609 Irp->IoStatus.Status = Status;
610 IoCompleteRequest(Irp, IO_NO_INCREMENT);
611 return Status;
612 }
613
614 NTSTATUS
615 NTAPI
CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)616 CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,
617 IN PIRP Irp)
618 {
619 PCMBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
620 NTSTATUS Status;
621 PIO_STACK_LOCATION IoStackLocation;
622 ULONG IoControlCode, OutputBufferLength, InputBufferLength;
623 PAGED_CODE();
624 if (CmBattDebug & 2) DbgPrint("CmBattIoctl\n");
625
626 /* Acquire the remove lock */
627 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, Irp);
628 if (!NT_SUCCESS(Status))
629 {
630 /* It's too late, fail */
631 Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
632 IoCompleteRequest(Irp, IO_NO_INCREMENT);
633 return STATUS_DEVICE_REMOVED;
634 }
635
636 /* There's nothing to do for an AC adapter */
637 if (DeviceExtension->FdoType == CmBattAcAdapter)
638 {
639 /* Pass it down, and release the remove lock */
640 IoSkipCurrentIrpStackLocation(Irp);
641 Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
642 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
643 return Status;
644 }
645
646 /* Send to class driver */
647 Status = BatteryClassIoctl(DeviceExtension->ClassData, Irp);
648 if (Status == STATUS_NOT_SUPPORTED)
649 {
650 /* Read IOCTL information from IRP stack */
651 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
652 IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
653 OutputBufferLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
654 InputBufferLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
655 if (CmBattDebug & 4)
656 DbgPrint("CmBattIoctl: Received Direct Access IOCTL %x\n", IoControlCode);
657
658 /* Handle internal IOCTLs */
659 switch (IoControlCode)
660 {
661 case IOCTL_BATTERY_QUERY_UNIQUE_ID:
662
663 /* Data is 4 bytes long */
664 if (OutputBufferLength == sizeof(ULONG))
665 {
666 /* Query it */
667 Status = CmBattGetUniqueId(DeviceExtension->PdoDeviceObject,
668 Irp->AssociatedIrp.SystemBuffer);
669 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
670 }
671 else
672 {
673 /* Buffer size invalid */
674 Status = STATUS_INVALID_BUFFER_SIZE;
675 }
676 break;
677
678 case IOCTL_BATTERY_QUERY_STA:
679
680 /* Data is 4 bytes long */
681 if (OutputBufferLength == sizeof(ULONG))
682 {
683 /* Query it */
684 Status = CmBattGetStaData(DeviceExtension->PdoDeviceObject,
685 Irp->AssociatedIrp.SystemBuffer);
686 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
687 }
688 else
689 {
690 /* Buffer size invalid */
691 Status = STATUS_INVALID_BUFFER_SIZE;
692 }
693 break;
694
695 case IOCTL_BATTERY_QUERY_PSR:
696
697 /* Data is 4 bytes long */
698 if (OutputBufferLength == sizeof(ULONG))
699 {
700 /* Do we have an AC adapter? */
701 if (AcAdapterPdo)
702 {
703 /* Query it */
704 Status = CmBattGetPsrData(AcAdapterPdo,
705 Irp->AssociatedIrp.SystemBuffer);
706 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
707 }
708 else
709 {
710 /* No adapter, just a battery, so fail */
711 Status = STATUS_NO_SUCH_DEVICE;
712 }
713 }
714 else
715 {
716 /* Buffer size invalid */
717 Status = STATUS_INVALID_BUFFER_SIZE;
718 }
719 break;
720
721 case IOCTL_BATTERY_SET_TRIP_POINT:
722
723 /* Data is 4 bytes long */
724 if (InputBufferLength == sizeof(ULONG))
725 {
726 /* Query it */
727 Status = CmBattSetTripPpoint(DeviceExtension,
728 *(PULONG)Irp->AssociatedIrp.SystemBuffer);
729 Irp->IoStatus.Information = 0;
730 }
731 else
732 {
733 /* Buffer size invalid */
734 Status = STATUS_INVALID_BUFFER_SIZE;
735 }
736 break;
737
738 case IOCTL_BATTERY_QUERY_BIF_BIX:
739
740 /* Return the battery static information to the caller depending on the supported ACPI method */
741 if (DeviceExtension->BattInfo.ExtendedData)
742 {
743 if (OutputBufferLength == sizeof(ACPI_BIX_DATA))
744 {
745 /* Query it */
746 Status = CmBattGetBixData(DeviceExtension,
747 Irp->AssociatedIrp.SystemBuffer);
748 if (NT_SUCCESS(Status))
749 {
750 Irp->IoStatus.Information = sizeof(ACPI_BIX_DATA);
751 }
752 }
753 else
754 {
755 /* Buffer size invalid */
756 Status = STATUS_INVALID_BUFFER_SIZE;
757 }
758 }
759 else
760 {
761 if (OutputBufferLength == sizeof(ACPI_BIF_DATA))
762 {
763 /* Query it */
764 Status = CmBattGetBifData(DeviceExtension,
765 Irp->AssociatedIrp.SystemBuffer);
766 if (NT_SUCCESS(Status))
767 {
768 Irp->IoStatus.Information = sizeof(ACPI_BIF_DATA);
769 }
770 }
771 else
772 {
773 /* Buffer size invalid */
774 Status = STATUS_INVALID_BUFFER_SIZE;
775 }
776 }
777 break;
778
779 case IOCTL_BATTERY_QUERY_BST:
780
781 /* Data is 16 bytes long */
782 if (OutputBufferLength == sizeof(ACPI_BST_DATA))
783 {
784 /* Query it */
785 Status = CmBattGetBstData(DeviceExtension,
786 Irp->AssociatedIrp.SystemBuffer);
787 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BST_DATA);
788 }
789 else
790 {
791 /* Buffer size invalid */
792 Status = STATUS_INVALID_BUFFER_SIZE;
793 }
794 break;
795
796 default:
797
798 /* Unknown, let us pass it on to ACPI */
799 if (CmBattDebug & 0xC)
800 DbgPrint("CmBattIoctl: Unknown IOCTL %x\n", IoControlCode);
801 break;
802 }
803
804 /* Did someone pick it up? */
805 if (Status != STATUS_NOT_SUPPORTED)
806 {
807 /* Complete the request */
808 Irp->IoStatus.Status = Status;
809 IoCompleteRequest(Irp, IO_NO_INCREMENT);
810 }
811 else
812 {
813 /* Still unsupported, try ACPI */
814 IoSkipCurrentIrpStackLocation(Irp);
815 Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
816 }
817 }
818
819 /* Release the remove lock and return status */
820 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
821 return Status;
822 }
823
824 NTSTATUS
825 NTAPI
CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,OUT PULONG Tag)826 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
827 OUT PULONG Tag)
828 {
829 PDEVICE_OBJECT PdoDevice;
830 ULONG StaData;
831 NTSTATUS Status;
832 PAGED_CODE();
833 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
834 DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
835 *Tag, DeviceExtension, DeviceExtension->DeviceId);
836
837 /* Get PDO and clear notification flag */
838 PdoDevice = DeviceExtension->PdoDeviceObject;
839 DeviceExtension->NotifySent = 0;
840
841 /* Get _STA from PDO (we need the machine status, not the battery status) */
842 Status = CmBattGetStaData(PdoDevice, &StaData);
843 if (NT_SUCCESS(Status))
844 {
845 /* Is a battery present? */
846 if (StaData & ACPI_STA_BATTERY_PRESENT)
847 {
848 /* Do we not have a tag yet? */
849 if (DeviceExtension->Tag == BATTERY_TAG_INVALID)
850 {
851 /* Set the new tag value, reset tags if we reached the maximum */
852 if (++DeviceExtension->TagData == BATTERY_TAG_INVALID)
853 DeviceExtension->TagData = 1;
854 DeviceExtension->Tag = DeviceExtension->TagData;
855 if (CmBattDebug & CMBATT_GENERIC_INFO)
856 DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension->Tag);
857
858 /* Reset trip point data */
859 DeviceExtension->TripPointOld = 0;
860 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
861
862 /* Clear AR lock and set new interrupt time */
863 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
864 DeviceExtension->InterruptTime = KeQueryInterruptTime();
865 }
866 }
867 else
868 {
869 /* No battery, so no tag */
870 DeviceExtension->Tag = BATTERY_TAG_INVALID;
871 Status = STATUS_NO_SUCH_DEVICE;
872 }
873 }
874
875 /* Return the tag and status result */
876 *Tag = DeviceExtension->Tag;
877 if (CmBattDebug & CMBATT_ACPI_WARNING)
878 DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag, Status);
879 return Status;
880 }
881
882 NTSTATUS
883 NTAPI
CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)884 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)
885 {
886 NTSTATUS Status;
887 PAGED_CODE();
888 if (CmBattDebug & 0xA) DbgPrint("CmBattDisableStatusNotify\n");
889
890 /* Do we have a trip point */
891 if (DeviceExtension->TripPointSet)
892 {
893 /* Is there a current value set? */
894 if (DeviceExtension->TripPointValue)
895 {
896 /* Reset it back to 0 */
897 DeviceExtension->TripPointValue = 0;
898 Status = CmBattSetTripPpoint(DeviceExtension, 0);
899 if (!NT_SUCCESS(Status))
900 {
901 /* If it failed, set unknown/invalid value */
902 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
903 if (CmBattDebug & 8)
904 DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status);
905 }
906 }
907 else
908 {
909 /* No trip point set, so this is a successful no-op */
910 Status = STATUS_SUCCESS;
911 }
912 }
913 else
914 {
915 /* Nothing we can do */
916 Status = STATUS_OBJECT_NAME_NOT_FOUND;
917 }
918
919 /* Return status */
920 return Status;
921 }
922
923 NTSTATUS
924 NTAPI
CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG BatteryTag,IN PBATTERY_NOTIFY BatteryNotify)925 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
926 IN ULONG BatteryTag,
927 IN PBATTERY_NOTIFY BatteryNotify)
928 {
929 NTSTATUS Status;
930 ACPI_BST_DATA BstData;
931 ULONG PowerUnit, Capacity, NewTripPoint, TripPoint, DesignVoltage;
932 BOOLEAN Charging;
933 PAGED_CODE();
934 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
935 DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
936 BatteryTag, BatteryNotify->LowCapacity);
937
938 /* Update any ACPI evaluations */
939 Status = CmBattVerifyStaticInfo(DeviceExtension, BatteryTag);
940 if (!NT_SUCCESS(Status)) return Status;
941
942 /* Trip point not supported, fail */
943 if (!DeviceExtension->TripPointSet) return STATUS_OBJECT_NAME_NOT_FOUND;
944
945 /* Are both capacities known? */
946 if ((BatteryNotify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
947 (BatteryNotify->LowCapacity == BATTERY_UNKNOWN_CAPACITY))
948 {
949 /* We can't set trip points without these */
950 if (CmBattDebug & CMBATT_GENERIC_WARNING)
951 DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
952 return STATUS_NOT_SUPPORTED;
953 }
954
955 /* Is the battery charging? */
956 Charging = DeviceExtension->BstData.State & ACPI_BATT_STAT_CHARGING;
957 if (Charging)
958 {
959 /* Then the trip point is when we hit the cap */
960 Capacity = BatteryNotify->HighCapacity;
961 NewTripPoint = BatteryNotify->HighCapacity;
962 }
963 else
964 {
965 /* Otherwise it's when we discharge to the bottom */
966 Capacity = BatteryNotify->LowCapacity;
967 NewTripPoint = BatteryNotify->LowCapacity;
968 }
969
970 /* Is this machine supporting _BIX or _BIF? */
971 if (DeviceExtension->BattInfo.ExtendedData)
972 {
973 PowerUnit = DeviceExtension->BattInfo.BixData.PowerUnit;
974 DesignVoltage = DeviceExtension->BattInfo.BixData.DesignVoltage;
975 }
976 else
977 {
978 PowerUnit = DeviceExtension->BattInfo.BifData.PowerUnit;
979 DesignVoltage = DeviceExtension->BattInfo.BifData.DesignVoltage;
980 }
981
982 /* Do we have data in Amps or Watts? */
983 if (PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
984 {
985 /* We need the voltage to do the conversion */
986 if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage))
987 {
988 /* Convert from mAh into Ah */
989 TripPoint = 1000 * NewTripPoint;
990 if (Charging)
991 {
992 /* Scale the high trip point */
993 NewTripPoint = (TripPoint + 500) / DesignVoltage + ((TripPoint + 500) % DesignVoltage != 0);
994 }
995 else
996 {
997 /* Scale the low trip point */
998 NewTripPoint = (TripPoint - 500) / DesignVoltage - ((TripPoint - 500) % DesignVoltage == 0);
999 }
1000 }
1001 else
1002 {
1003 /* Without knowing the voltage, Amps are not enough data on consumption */
1004 Status = STATUS_NOT_SUPPORTED;
1005 if (CmBattDebug & CMBATT_ACPI_WARNING)
1006 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
1007 DesignVoltage);
1008 }
1009 }
1010 else if (Charging)
1011 {
1012 /* Make it trip just one past the charge cap */
1013 ++NewTripPoint;
1014 }
1015 else if (NewTripPoint > 0)
1016 {
1017 /* Make it trip just one below the drain cap */
1018 --NewTripPoint;
1019 }
1020
1021 /* Do we actually have a new trip point? */
1022 if (NewTripPoint == DeviceExtension->TripPointValue)
1023 {
1024 /* No, so there is no work to be done */
1025 if (CmBattDebug & CMBATT_GENERIC_STATUS)
1026 DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension->TripPointValue);
1027 return STATUS_SUCCESS;
1028 }
1029
1030 /* Set the trip point with ACPI and check for success */
1031 DeviceExtension->TripPointValue = NewTripPoint;
1032 Status = CmBattSetTripPpoint(DeviceExtension, NewTripPoint);
1033 if (!(NewTripPoint) && (Capacity)) Status = STATUS_NOT_SUPPORTED;
1034 if (!NT_SUCCESS(Status))
1035 {
1036 /* We failed to set the trip point, or there wasn't one settable */
1037 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
1038 if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
1039 DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status);
1040 return Status;
1041 }
1042
1043 /* Read the new BST data to see the latest state */
1044 Status = CmBattGetBstData(DeviceExtension, &BstData);
1045 if (!NT_SUCCESS(Status))
1046 {
1047 /* We'll return failure to the caller */
1048 if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
1049 DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status);
1050 }
1051 else if ((Charging) && (BstData.RemainingCapacity >= NewTripPoint))
1052 {
1053 /* We are charging and our capacity is past the trip point, so trip now */
1054 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1055 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
1056 NewTripPoint, BstData.RemainingCapacity);
1057 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
1058 }
1059 else if ((BstData.RemainingCapacity) && (Capacity))
1060 {
1061 /* We are discharging, and our capacity is below the trip point, trip now */
1062 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1063 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
1064 NewTripPoint, BstData.RemainingCapacity);
1065 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
1066 }
1067
1068 /* All should've went well if we got here, unless BST failed... return! */
1069 if (CmBattDebug & CMBATT_GENERIC_STATUS)
1070 DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
1071 Capacity, DeviceExtension->RemainingCapacity);
1072 if (CmBattDebug & CMBATT_ACPI_WARNING)
1073 DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %lx\n",
1074 BatteryNotify->PowerState,
1075 BatteryNotify->LowCapacity,
1076 BatteryNotify->HighCapacity,
1077 Status);
1078 return Status;
1079 }
1080
1081 NTSTATUS
1082 NTAPI
CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Tag)1083 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1084 IN ULONG Tag)
1085 {
1086 ULONG PsrData = 0;
1087 NTSTATUS Status;
1088 BOOLEAN WasDischarging;
1089 ULONG BstState;
1090 ULONG PowerUnit;
1091 ULONG DesignVoltage, PresentRate, RemainingCapacity;
1092 PAGED_CODE();
1093 if (CmBattDebug & CMBATT_GENERIC_INFO)
1094 DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
1095
1096 /* Validate ACPI data */
1097 Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
1098 if (!NT_SUCCESS(Status)) return Status;
1099
1100 /* Check for delayed status notifications */
1101 if (DeviceExtension->DelayNotification)
1102 {
1103 /* Process them now and don't do any other work */
1104 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
1105 return Status;
1106 }
1107
1108 /* Get _BST from ACPI */
1109 Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
1110 if (!NT_SUCCESS(Status))
1111 {
1112 /* Fail */
1113 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
1114 return Status;
1115 }
1116
1117 /* Remember if the battery was discharging at the time of querying new status */
1118 WasDischarging = !!(DeviceExtension->State & BATTERY_DISCHARGING);
1119
1120 /* Clear current BST information */
1121 DeviceExtension->State = 0;
1122 DeviceExtension->RemainingCapacity = 0;
1123 DeviceExtension->PresentVoltage = 0;
1124 DeviceExtension->Rate = 0;
1125
1126 /* Get battery state */
1127 BstState = DeviceExtension->BstData.State;
1128
1129 /* Is the battery both charging and discharging? */
1130 if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
1131 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
1132 DbgPrint("************************ ACPI BIOS BUG ********************\n* "
1133 "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
1134 "* One battery cannot be charging and discharging at the same time.\n",
1135 BstState);
1136
1137 /* Is the battery discharging? */
1138 if (BstState & ACPI_BATT_STAT_DISCHARG)
1139 {
1140 /* Set power state and check if it just started discharging now */
1141 DeviceExtension->State |= BATTERY_DISCHARGING;
1142 if (!WasDischarging)
1143 {
1144 /* The battery is discharging now and not before, remember the time when the state changed */
1145 DeviceExtension->InterruptTime = KeQueryInterruptTime();
1146 }
1147 }
1148 else if (BstState & ACPI_BATT_STAT_CHARGING)
1149 {
1150 /* Battery is charging, update power state */
1151 DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
1152 }
1153
1154 /* Is the battery in a critical state? */
1155 if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
1156
1157 /* Read the voltage data */
1158 DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
1159
1160 /* Check if we have an A/C adapter */
1161 if (AcAdapterPdo)
1162 {
1163 /* Query information on it */
1164 CmBattGetPsrData(AcAdapterPdo, &PsrData);
1165 }
1166 else
1167 {
1168 /* Otherwise, check if the battery is charging */
1169 if (BstState & ACPI_BATT_STAT_CHARGING)
1170 {
1171 /* Then we'll assume there's a charger */
1172 PsrData = 1;
1173 }
1174 else
1175 {
1176 /* Assume no charger */
1177 PsrData = 0;
1178 }
1179 }
1180
1181 /* Is there a charger? */
1182 if (PsrData)
1183 {
1184 /* Set the power state flag to reflect this */
1185 DeviceExtension->State |= BATTERY_POWER_ON_LINE;
1186 if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
1187 DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
1188 }
1189 else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
1190 {
1191 DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
1192 }
1193
1194 /* Is this machine supporting _BIX or _BIF? */
1195 if (DeviceExtension->BattInfo.ExtendedData)
1196 {
1197 PowerUnit = DeviceExtension->BattInfo.BixData.PowerUnit;
1198 DesignVoltage = DeviceExtension->BattInfo.BixData.DesignVoltage;
1199 }
1200 else
1201 {
1202 PowerUnit = DeviceExtension->BattInfo.BifData.PowerUnit;
1203 DesignVoltage = DeviceExtension->BattInfo.BifData.DesignVoltage;
1204 }
1205
1206 /* Get some data we'll need */
1207 PresentRate = DeviceExtension->BstData.PresentRate;
1208 RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
1209
1210 /* Check if we have battery data in Watts instead of Amps */
1211 if (PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
1212 {
1213 /* Get the data from the BST */
1214 DeviceExtension->RemainingCapacity = RemainingCapacity;
1215 DeviceExtension->Rate = PresentRate;
1216
1217 /* Check if the rate is invalid */
1218 if (PresentRate > CM_MAX_VALUE)
1219 {
1220 /* Set an unknown rate and don't touch the old value */
1221 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1222 if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
1223 {
1224 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
1225 DbgPrint("---------------------- PresentRate = 0x%08x\n", PresentRate);
1226 }
1227 }
1228 }
1229 else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage != 0)) // Same as doing PowerUnit == ACPI_BATT_POWER_UNIT_AMPS
1230 {
1231 /* We have voltage data, what about capacity? */
1232 if (RemainingCapacity == CM_UNKNOWN_VALUE)
1233 {
1234 /* Unable to calculate it */
1235 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
1236 if (CmBattDebug & CMBATT_ACPI_WARNING)
1237 {
1238 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
1239 DbgPrint("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n");
1240 }
1241 }
1242 else
1243 {
1244 /* Compute the capacity with the information we have */
1245 DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
1246 }
1247
1248 /* Check if we have a rate */
1249 if (PresentRate != CM_UNKNOWN_VALUE)
1250 {
1251 /* Make sure the rate isn't too large */
1252 if (PresentRate > (-500 / DesignVoltage))
1253 {
1254 /* It is, so set unknown state */
1255 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1256 if (CmBattDebug & CMBATT_ACPI_WARNING)
1257 {
1258 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
1259 DbgPrint("---------------------- Overflow: PresentRate = 0x%08x\n", PresentRate);
1260 }
1261 }
1262
1263 /* Compute the rate */
1264 DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
1265 }
1266 else
1267 {
1268 /* We don't have a rate, so set unknown value */
1269 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1270 if (CmBattDebug & CMBATT_ACPI_WARNING)
1271 {
1272 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
1273 DbgPrint("---------------------- Present Rate = CM_UNKNOWN_VALUE\n");
1274 }
1275 }
1276 }
1277 else
1278 {
1279 /* We have no rate, and no capacity, set unknown values */
1280 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
1281 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
1282 if (CmBattDebug & CMBATT_ACPI_WARNING)
1283 {
1284 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
1285 DbgPrint("---------------------- DesignVoltage = 0x%08x\n", DesignVoltage);
1286 }
1287 }
1288
1289 /* Check if we have an unknown rate */
1290 if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
1291 {
1292 /* The battery is discharging but we don't know by how much... this is bad! */
1293 if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
1294 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
1295 DbgPrint("CmBattGetBatteryStatus: battery rate is unknown when battery is not charging!\n");
1296 }
1297 else if (DeviceExtension->State & BATTERY_DISCHARGING)
1298 {
1299 /* The battery is discharging, so treat the rate as a negative rate */
1300 DeviceExtension->Rate = -(LONG)DeviceExtension->Rate;
1301 }
1302 else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
1303 {
1304 /* We are not charging, not discharging, but have a rate? Ignore it! */
1305 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1306 DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
1307 DeviceExtension->Rate);
1308 DeviceExtension->Rate = 0;
1309 }
1310
1311 /* Done */
1312 return STATUS_SUCCESS;
1313 }
1314
1315 NTSTATUS
1316 NTAPI
CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,IN ULONG Tag,IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,IN OPTIONAL LONG AtRate,IN PVOID Buffer,IN ULONG BufferLength,OUT PULONG ReturnedLength)1317 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
1318 IN ULONG Tag,
1319 IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
1320 IN OPTIONAL LONG AtRate,
1321 IN PVOID Buffer,
1322 IN ULONG BufferLength,
1323 OUT PULONG ReturnedLength)
1324 {
1325 NTSTATUS Status;
1326 PVOID QueryData = NULL;
1327 ULONG QueryLength = 0;
1328 ULONG RemainingTime = 0;
1329 ANSI_STRING TempString;
1330 UNICODE_STRING TempString2;
1331 WCHAR InfoBuffer[256];
1332 WCHAR TempBuffer[256];
1333 UNICODE_STRING InfoString;
1334 ULONG RemainingCapacity;
1335 BATTERY_REPORTING_SCALE BatteryReportingScale[2];
1336 LONG Rate;
1337 PAGED_CODE();
1338 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1339 DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
1340 Tag,
1341 FdoExtension->DeviceId,
1342 InfoLevel);
1343
1344 /* Check ACPI Data */
1345 Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
1346 if (!NT_SUCCESS(Status)) return Status;
1347
1348 /* Check what caller wants */
1349 switch (InfoLevel)
1350 {
1351 case BatteryInformation:
1352 /* Just return our static information */
1353 QueryData = &FdoExtension->BatteryInformation;
1354 QueryLength = sizeof(BATTERY_INFORMATION);
1355 break;
1356
1357 case BatteryGranularityInformation:
1358
1359 /* Return our static information, we have two scales */
1360 BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
1361 BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
1362 BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
1363 BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
1364 QueryData = BatteryReportingScale;
1365 QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
1366 break;
1367
1368 case BatteryEstimatedTime:
1369
1370 /* Check if it's been more than 15 seconds since the last change */
1371 if (KeQueryInterruptTime() > (FdoExtension->InterruptTime + CMBATT_DISCHARGE_TIME))
1372 {
1373 /* Get new battery status */
1374 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
1375
1376 /* If the caller didn't specify a rate, use our static one */
1377 Rate = AtRate;
1378 if (!Rate) Rate = FdoExtension->Rate;
1379
1380 /* If we don't have a valid negative rate, use unknown value */
1381 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
1382
1383 /* Grab the remaining capacity */
1384 RemainingCapacity = FdoExtension->RemainingCapacity;
1385
1386 /* Default time to unknown if we fail the request later */
1387 RemainingTime = BATTERY_UNKNOWN_TIME;
1388
1389 /* See if we don't know one or the other */
1390 if ((Rate == BATTERY_UNKNOWN_RATE) ||
1391 (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
1392 {
1393 /* If the battery is discharging, we can't give out a time */
1394 if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
1395 (CmBattDebug & CMBATT_GENERIC_WARNING))
1396 DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
1397
1398 /* Check if we don't have a rate and capacity is going down */
1399 if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
1400 (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
1401 {
1402 /* We have to fail, since we lack data */
1403 Status = STATUS_INVALID_DEVICE_REQUEST;
1404 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1405 DbgPrint("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n");
1406 }
1407
1408 /* If we don't have capacity, the rate is useless */
1409 if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
1410 {
1411 /* We have to fail the request */
1412 Status = STATUS_INVALID_DEVICE_REQUEST;
1413 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1414 DbgPrint("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
1415 }
1416 }
1417 else
1418 {
1419 /* We have data, but is it valid? */
1420 if (RemainingCapacity > CMBATT_CAPACITY_BOGUS)
1421 {
1422 /* The capacity seems bogus, so don't use it */
1423 if (CmBattDebug & CMBATT_ACPI_WARNING)
1424 DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
1425 }
1426 else
1427 {
1428 /* Compute the remaining time in seconds, based on rate */
1429 RemainingTime = (RemainingCapacity * 3600) / -Rate;
1430 }
1431 }
1432 }
1433 else
1434 {
1435 RemainingTime = BATTERY_UNKNOWN_TIME;
1436 }
1437
1438 /* Return the remaining time */
1439 QueryData = &RemainingTime;
1440 QueryLength = sizeof(ULONG);
1441 break;
1442
1443 case BatteryDeviceName:
1444
1445 /* Build the model number string */
1446 if (FdoExtension->BattInfo.ExtendedData)
1447 {
1448 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.ModelNumber);
1449 }
1450 else
1451 {
1452 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.ModelNumber);
1453 }
1454
1455 /* Convert it to Unicode */
1456 InfoString.Buffer = InfoBuffer;
1457 InfoString.MaximumLength = sizeof(InfoBuffer);
1458 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1459
1460 /* Return the unicode buffer */
1461 QueryData = InfoString.Buffer;
1462 QueryLength = InfoString.Length;
1463 break;
1464
1465 case BatteryTemperature:
1466 case BatteryManufactureDate:
1467
1468 /* We don't support these */
1469 Status = STATUS_INVALID_DEVICE_REQUEST;
1470 break;
1471
1472 case BatteryManufactureName:
1473
1474 /* Build the OEM info string */
1475 if (FdoExtension->BattInfo.ExtendedData)
1476 {
1477 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.OemInfo);
1478 }
1479 else
1480 {
1481 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.OemInfo);
1482 }
1483
1484 /* Convert it to Unicode */
1485 InfoString.Buffer = InfoBuffer;
1486 InfoString.MaximumLength = sizeof(InfoBuffer);
1487 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1488
1489 /* Return the unicode buffer */
1490 QueryData = InfoString.Buffer;
1491 QueryLength = InfoString.Length;
1492 break;
1493
1494 case BatteryUniqueID:
1495
1496 /* Build the serial number string */
1497 if (FdoExtension->BattInfo.ExtendedData)
1498 {
1499 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.SerialNumber);
1500 }
1501 else
1502 {
1503 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.SerialNumber);
1504 }
1505
1506 /* Convert it to Unicode */
1507 InfoString.Buffer = InfoBuffer;
1508 InfoString.MaximumLength = sizeof(InfoBuffer);
1509 RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1510
1511 /* Setup a temporary string for concatenation */
1512 TempString2.Buffer = TempBuffer;
1513 TempString2.MaximumLength = sizeof(TempBuffer);
1514
1515 /* Check if there's an OEM string */
1516 if ((FdoExtension->BattInfo.ExtendedData && FdoExtension->BattInfo.BixData.OemInfo[0]) ||
1517 FdoExtension->BattInfo.BifData.OemInfo[0])
1518 {
1519 /* Build the OEM info string */
1520 if (FdoExtension->BattInfo.ExtendedData)
1521 {
1522 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.OemInfo);
1523 }
1524 else
1525 {
1526 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.OemInfo);
1527 }
1528
1529 /* Convert it to Unicode and append it */
1530 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1531 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1532 }
1533
1534 /* Build the model number string */
1535 if (FdoExtension->BattInfo.ExtendedData)
1536 {
1537 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BixData.ModelNumber);
1538 }
1539 else
1540 {
1541 RtlInitAnsiString(&TempString, FdoExtension->BattInfo.BifData.ModelNumber);
1542 }
1543
1544 /* Convert it to Unicode and append it */
1545 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1546 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1547
1548 /* Return the final appended string */
1549 QueryData = InfoString.Buffer;
1550 QueryLength = InfoString.Length;
1551 break;
1552
1553 default:
1554
1555 /* Everything else is unknown */
1556 Status = STATUS_INVALID_PARAMETER;
1557 break;
1558 }
1559
1560 /* Return the required length and check if the caller supplied enough */
1561 *ReturnedLength = QueryLength;
1562 if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
1563
1564 /* Copy the data if there's enough space and it exists */
1565 if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
1566
1567 /* Return function result */
1568 return Status;
1569 }
1570
1571 NTSTATUS
1572 NTAPI
CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,IN ULONG Tag,IN PBATTERY_STATUS BatteryStatus)1573 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1574 IN ULONG Tag,
1575 IN PBATTERY_STATUS BatteryStatus)
1576 {
1577 NTSTATUS Status;
1578 PAGED_CODE();
1579 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1580 DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
1581
1582 /* Query ACPI information */
1583 Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
1584 if (NT_SUCCESS(Status))
1585 {
1586 BatteryStatus->PowerState = DeviceExtension->State;
1587 BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
1588 BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
1589 BatteryStatus->Rate = DeviceExtension->Rate;
1590 }
1591
1592 /* Return status */
1593 if (CmBattDebug & (CMBATT_GENERIC_INFO))
1594 DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
1595 BatteryStatus->PowerState,
1596 BatteryStatus->Capacity,
1597 BatteryStatus->Voltage,
1598 BatteryStatus->Rate);
1599 return Status;
1600 }
1601
1602 NTSTATUS
1603 NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)1604 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1605 IN PUNICODE_STRING RegistryPath)
1606 {
1607 NTSTATUS Status;
1608 PDRIVER_EXTENSION DriverExtension;
1609 OBJECT_ATTRIBUTES ObjectAttributes;
1610 UNICODE_STRING CallbackName;
1611
1612 /* Allocate registry path */
1613 GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
1614 GlobalRegistryPath.Length = RegistryPath->Length;
1615 GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
1616 GlobalRegistryPath.MaximumLength,
1617 'MtaB');
1618 if (!GlobalRegistryPath.Buffer)
1619 {
1620 /* Fail if we're out of memory this early */
1621 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1622 DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
1623 return STATUS_INSUFFICIENT_RESOURCES;
1624 }
1625
1626 /* Buffer allocated, copy the string */
1627 RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
1628 if (CmBattDebug & CMBATT_GENERIC_INFO)
1629 DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%wZ\"\n",
1630 DriverObject,
1631 RegistryPath);
1632
1633 /* Setup the major dispatchers */
1634 DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
1635 DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
1636 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
1637 DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
1638 DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
1639 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
1640
1641 /* And the unload routine */
1642 DriverObject->DriverUnload = CmBattUnload;
1643
1644 /* And the add device routine */
1645 DriverExtension = DriverObject->DriverExtension;
1646 DriverExtension->AddDevice = CmBattAddDevice;
1647
1648 /* Create a power callback */
1649 RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
1650 InitializeObjectAttributes(&ObjectAttributes,
1651 &CallbackName,
1652 OBJ_KERNEL_HANDLE,
1653 NULL,
1654 NULL);
1655 Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
1656 if (!NT_SUCCESS(Status))
1657 {
1658 /* No callback, fail */
1659 CmBattPowerCallBackObject = 0;
1660 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1661 DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
1662 }
1663 else
1664 {
1665 /* Register the power callback now */
1666 CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
1667 (PVOID)CmBattPowerCallBack,
1668 DriverObject);
1669 if (CmBattPowerCallBackRegistration)
1670 {
1671 /* Last thing: setup our DPC and timer for battery wake */
1672 KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
1673 KeInitializeTimer(&CmBattWakeDpcTimerObject);
1674 }
1675 else
1676 {
1677 ObDereferenceObject(CmBattPowerCallBackObject);
1678 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1679 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1680 }
1681
1682 /* All good */
1683 Status = STATUS_SUCCESS;
1684 }
1685
1686 /* Return failure or success */
1687 return Status;
1688 }
1689
1690 /* EOF */
1691