5b11e1ee60493244aa7276860c73f0a6ffd9c4bd
[reactos.git] / reactos / drivers / bus / acpi / cmbatt / cmbatt.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/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 /* GLOBALS ********************************************************************/
14
15 ULONG CmBattDebug;
16 PCALLBACK_OBJECT CmBattPowerCallBackObject;
17 PVOID CmBattPowerCallBackRegistration;
18 UNICODE_STRING GlobalRegistryPath;
19 KTIMER CmBattWakeDpcTimerObject;
20 KDPC CmBattWakeDpcObject;
21 PDEVICE_OBJECT AcAdapterPdo;
22
23 /* FUNCTIONS ******************************************************************/
24
25 VOID
26 NTAPI
27 CmBattPowerCallBack(PCMBATT_DEVICE_EXTENSION DeviceExtension,
28 PVOID Argument1,
29 PVOID Argument2)
30 {
31 UNIMPLEMENTED;
32 }
33
34 VOID
35 NTAPI
36 CmBattWakeDpc(PKDPC Dpc,
37 PCMBATT_DEVICE_EXTENSION FdoExtension,
38 PVOID SystemArgument1,
39 PVOID SystemArgument2)
40 {
41
42 }
43
44 VOID
45 NTAPI
46 CmBattNotifyHandler(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
47 IN ULONG NotifyValue)
48 {
49 ULONG ArFlag;
50 PCMBATT_DEVICE_EXTENSION FdoExtension;
51 PDEVICE_OBJECT DeviceObject;
52
53 if (CmBattDebug & (CMBATT_ACPI_ASSERT | CMBATT_PNP_INFO))
54 DbgPrint("CmBattNotifyHandler: CmBatt 0x%08x Type %d Number %d Notify Value: %x\n",
55 DeviceExtension,
56 DeviceExtension->FdoType,
57 DeviceExtension->DeviceId,
58 NotifyValue);
59
60 /* Check what kind of notification was received */
61 switch (NotifyValue)
62 {
63 /* ACPI Specification says is sends a "Bus Check" when power source changes */
64 case ACPI_BUS_CHECK:
65
66 /* We treat it as possible physical change */
67 DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY | CMBATT_AR_INSERT);
68 if ((DeviceExtension->Tag) &&
69 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
70 DbgPrint("CmBattNotifyHandler: Received battery #%x insertion, but tag was not invalid.\n",
71 DeviceExtension->DeviceId);
72 break;
73
74 /* Status of the battery has changed */
75 case ACPI_BATT_NOTIFY_STATUS:
76
77 /* All we'll do is notify the class driver */
78 DeviceExtension->ArFlag |= CMBATT_AR_NOTIFY;
79 break;
80
81 /* Information on the battery has changed, such as physical presence */
82 case ACPI_DEVICE_CHECK:
83 case ACPI_BATT_NOTIFY_INFO:
84
85 /* Reset all state and let the class driver re-evaluate it all */
86 DeviceExtension->ArFlag |= (CMBATT_AR_NOTIFY |
87 CMBATT_AR_INSERT |
88 CMBATT_AR_REMOVE);
89 break;
90
91 default:
92
93 if (CmBattDebug & CMBATT_PNP_INFO)
94 DbgPrint("CmBattNotifyHandler: Unknown Notify Value: %x\n", NotifyValue);
95 }
96
97 /* Check if we're supposed to delay the notification till later */
98 if (DeviceExtension->DelayNotification)
99 {
100 /* We'll handle this when we get a status query later on */
101 if (CmBattDebug & CMBATT_PNP_INFO)
102 DbgPrint("CmBattNotifyHandler: Notification delayed: ARs = %01x\n",
103 DeviceExtension->ArFlag);
104 return;
105 }
106
107 /* We're going to handle this now */
108 if (CmBattDebug & CMBATT_PNP_INFO)
109 DbgPrint("CmBattNotifyHandler: Performing ARs: %01x\n", DeviceExtension->ArFlag);
110
111 /* Check if this is a battery or AC adapter notification */
112 if (DeviceExtension->FdoType == CmBattBattery)
113 {
114 /* Reset the current trip point */
115 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
116
117 /* Check what ARs have to be done */
118 ArFlag = DeviceExtension->ArFlag;
119
120 /* New battery inserted, reset lock value */
121 if (ArFlag & CMBATT_AR_INSERT) InterlockedExchange(&DeviceExtension->ArLockValue, 0);
122
123 /* Check if the battery may have been removed */
124 if (ArFlag & CMBATT_AR_REMOVE) DeviceExtension->Tag = 0;
125
126 /* Check if there's been any sort of change to the battery */
127 if (ArFlag & CMBATT_AR_NOTIFY)
128 {
129 /* We'll probably end up re-evaluating _BIF and _BST */
130 DeviceExtension->NotifySent = TRUE;
131 BatteryClassStatusNotify(DeviceExtension->ClassData);
132 }
133 }
134 else
135 {
136 /* The only known notification is AC/DC change */
137 if (DeviceExtension->ArFlag & CMBATT_AR_NOTIFY)
138 {
139 for (DeviceObject = DeviceExtension->FdoDeviceObject->DriverObject->DeviceObject;
140 DeviceObject;
141 DeviceObject = DeviceObject->NextDevice)
142 {
143 /* Is this a battery? */
144 FdoExtension = DeviceObject->DeviceExtension;
145 if (FdoExtension->FdoType == CmBattBattery)
146 {
147 /* Send a notification to the class driver */
148 FdoExtension->NotifySent = TRUE;
149 BatteryClassStatusNotify(FdoExtension->ClassData);
150 }
151 }
152 }
153 }
154
155 /* ARs have been processed */
156 DeviceExtension->ArFlag = 0;
157 }
158
159 VOID
160 NTAPI
161 CmBattUnload(IN PDRIVER_OBJECT DriverObject)
162 {
163 if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattUnload: \n");
164
165 /* Check if we have a registered power callback */
166 if (CmBattPowerCallBackObject)
167 {
168 /* Get rid of it */
169 ExUnregisterCallback(CmBattPowerCallBackRegistration);
170 ObfDereferenceObject(CmBattPowerCallBackObject);
171 }
172
173 /* Free the registry buffer if it exists */
174 if (GlobalRegistryPath.Buffer) ExFreePool(GlobalRegistryPath.Buffer);
175
176 /* Make sure we don't still have references to the DO */
177 if ((DriverObject->DeviceObject) && (CmBattDebug & CMBATT_GENERIC_WARNING))
178 {
179 DbgPrint("Unload called before all devices removed.\n");
180 }
181 }
182
183 NTSTATUS
184 NTAPI
185 CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension,
186 ULONG BatteryTag)
187 {
188 UNIMPLEMENTED;
189 return STATUS_NOT_IMPLEMENTED;
190 }
191
192 NTSTATUS
193 NTAPI
194 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,
195 IN PIRP Irp)
196 {
197 NTSTATUS Status = STATUS_SUCCESS;
198 PIO_STACK_LOCATION IoStackLocation;
199 UCHAR Major;
200 ULONG Count;
201 PCMBATT_DEVICE_EXTENSION DeviceExtension;
202 PAGED_CODE();
203 if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattOpenClose\n");
204
205 /* Grab the device extension and lock it */
206 DeviceExtension = DeviceObject->DeviceExtension;
207 ExAcquireFastMutex(&DeviceExtension->FastMutex);
208
209 /* Check if someone is trying to open a device that doesn't exist yet */
210 Count = DeviceExtension->HandleCount;
211 if (Count == 0xFFFFFFFF)
212 {
213 /* Fail the request */
214 Status = STATUS_NO_SUCH_DEVICE;
215 if (CmBattDebug & CMBATT_PNP_INFO)
216 {
217 DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
218 DeviceExtension->Tag);
219 }
220 goto Complete;
221 }
222
223 /* Check if this is an open or close */
224 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
225 Major = IoStackLocation->MajorFunction;
226 if (Major == IRP_MJ_CREATE)
227 {
228 /* Increment the open count */
229 DeviceExtension->HandleCount = Count + 1;
230 if (CmBattDebug & CMBATT_PNP_INFO)
231 {
232 DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
233 DeviceExtension->DeviceId, Count + 1);
234 }
235 }
236 else if (Major == IRP_MJ_CLOSE)
237 {
238 /* Decrement the open count */
239 DeviceExtension->HandleCount = Count - 1;
240 if (CmBattDebug & CMBATT_PNP_INFO)
241 {
242 DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
243 DeviceExtension->DeviceId, Count + 1);
244 }
245 }
246
247 Complete:
248 /* Release lock and complete request */
249 ExReleaseFastMutex(&DeviceExtension->FastMutex);
250 Irp->IoStatus.Status = Status;
251 IofCompleteRequest(Irp, IO_NO_INCREMENT);
252 return Status;
253 }
254
255 NTSTATUS
256 NTAPI
257 CmBattIoctl(PDEVICE_OBJECT DeviceObject,
258 PIRP Irp)
259 {
260 UNIMPLEMENTED;
261 return STATUS_NOT_IMPLEMENTED;
262 }
263
264 NTSTATUS
265 NTAPI
266 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
267 OUT PULONG Tag)
268 {
269 PDEVICE_OBJECT PdoDevice;
270 ULONG StaData;
271 ULONG NewTag;
272 NTSTATUS Status;
273 PAGED_CODE();
274 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
275 DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
276 *Tag, DeviceExtension, DeviceExtension->DeviceId);
277
278 /* Get PDO and clear notification flag */
279 PdoDevice = DeviceExtension->PdoDeviceObject;
280 DeviceExtension->NotifySent = 0;
281
282 /* Get _STA from PDO (we need the machine status, not the battery status) */
283 Status = CmBattGetStaData(PdoDevice, &StaData);
284 if (NT_SUCCESS(Status))
285 {
286 /* Is a battery present? */
287 if (StaData & ACPI_STA_BATTERY_PRESENT)
288 {
289 /* Do we not have a tag yet? */
290 if (!DeviceExtension->Tag)
291 {
292 /* Set the new tag value, reset tags if we reached the maximum */
293 NewTag = DeviceExtension->TagData;
294 if (DeviceExtension->TagData++ == 0xFFFFFFFF) NewTag = 1;
295 DeviceExtension->Tag = NewTag;
296 if (CmBattDebug & CMBATT_GENERIC_INFO)
297 DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension->Tag);
298
299 /* Reset trip point data */
300 DeviceExtension->TripPointOld = 0;
301 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
302
303 /* Clear AR lock and set new interrupt time */
304 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
305 DeviceExtension->InterruptTime = KeQueryInterruptTime();
306 }
307 }
308 else
309 {
310 /* No battery, so no tag */
311 DeviceExtension->Tag = 0;
312 Status = STATUS_NO_SUCH_DEVICE;
313 }
314 }
315
316 /* Return the tag and status result */
317 *Tag = DeviceExtension->Tag;
318 if (CmBattDebug & CMBATT_ACPI_WARNING)
319 DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag, Status);
320 return Status;
321 }
322
323 NTSTATUS
324 NTAPI
325 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)
326 {
327 NTSTATUS Status;
328 PAGED_CODE();
329 if (CmBattDebug & 0xA) DbgPrint("CmBattDisableStatusNotify\n");
330
331 /* Do we have a trip point */
332 if (DeviceExtension->TripPointSet)
333 {
334 /* Is there a current value set? */
335 if (DeviceExtension->TripPointValue)
336 {
337 /* Reset it back to 0 */
338 DeviceExtension->TripPointValue = 0;
339 Status = CmBattSetTripPpoint(DeviceExtension, 0);
340 if (!NT_SUCCESS(Status))
341 {
342 /* If it failed, set unknown/invalid value */
343 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
344 if (CmBattDebug & 8)
345 DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status);
346 }
347 }
348 else
349 {
350 /* No trip point set, so this is a successful no-op */
351 Status = STATUS_SUCCESS;
352 }
353 }
354 else
355 {
356 /* Nothing we can do */
357 Status = STATUS_OBJECT_NAME_NOT_FOUND;
358 }
359
360 /* Return status */
361 return Status;
362 }
363
364 NTSTATUS
365 NTAPI
366 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
367 IN ULONG BatteryTag,
368 IN PBATTERY_NOTIFY BatteryNotify)
369 {
370 NTSTATUS Status;
371 ACPI_BST_DATA BstData;
372 ULONG Capacity, NewTripPoint, TripPoint, DesignVoltage;
373 BOOLEAN Charging;
374 PAGED_CODE();
375 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
376 DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
377 BatteryTag, BatteryNotify->LowCapacity);
378
379 /* Update any ACPI evaluations */
380 Status = CmBattVerifyStaticInfo(DeviceExtension, BatteryTag);
381 if (!NT_SUCCESS(Status)) return Status;
382
383 /* Trip point not supported, fail */
384 if (!DeviceExtension->TripPointSet) return STATUS_OBJECT_NAME_NOT_FOUND;
385
386 /* Are both capacities known? */
387 if ((BatteryNotify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
388 (BatteryNotify->LowCapacity == BATTERY_UNKNOWN_CAPACITY))
389 {
390 /* We can't set trip points without these */
391 if (CmBattDebug & CMBATT_GENERIC_WARNING)
392 DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
393 return STATUS_NOT_SUPPORTED;
394 }
395
396 /* Is the battery charging? */
397 Charging = DeviceExtension->BstData.State & ACPI_BATT_STAT_CHARGING;
398 if (Charging)
399 {
400 /* Then the trip point is when we hit the cap */
401 Capacity = BatteryNotify->HighCapacity;
402 NewTripPoint = BatteryNotify->HighCapacity;
403 }
404 else
405 {
406 /* Otherwise it's when we discharge to the bottom */
407 Capacity = BatteryNotify->LowCapacity;
408 NewTripPoint = BatteryNotify->LowCapacity;
409 }
410
411 /* Do we have data in Amps or Watts? */
412 if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
413 {
414 /* We need the voltage to do the conversion */
415 DesignVoltage = DeviceExtension->BifData.DesignVoltage;
416 if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage))
417 {
418 /* Convert from mAh into Ah */
419 TripPoint = 1000 * NewTripPoint;
420 if (Charging)
421 {
422 /* Scale the high trip point */
423 NewTripPoint = (TripPoint + 500) / DesignVoltage + ((TripPoint + 500) % DesignVoltage != 0);
424 }
425 else
426 {
427 /* Scale the low trip point */
428 NewTripPoint = (TripPoint - 500) / DesignVoltage - ((TripPoint - 500) % DesignVoltage == 0);
429 }
430 }
431 else
432 {
433 /* Without knowing the voltage, Amps are not enough data on consumption */
434 Status = STATUS_NOT_SUPPORTED;
435 if (CmBattDebug & CMBATT_ACPI_WARNING)
436 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
437 DesignVoltage);
438 }
439 }
440 else if (Charging)
441 {
442 /* Make it trip just one past the charge cap */
443 ++NewTripPoint;
444 }
445 else if (NewTripPoint > 0)
446 {
447 /* Make it trip just one below the drain cap */
448 --NewTripPoint;
449 }
450
451 /* Do we actually have a new trip point? */
452 if (NewTripPoint == DeviceExtension->TripPointValue)
453 {
454 /* No, so there is no work to be done */
455 if (CmBattDebug & CMBATT_GENERIC_STATUS)
456 DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension->TripPointValue);
457 return STATUS_SUCCESS;
458 }
459
460 /* Set the trip point with ACPI and check for success */
461 DeviceExtension->TripPointValue = NewTripPoint;
462 Status = CmBattSetTripPpoint(DeviceExtension, NewTripPoint);
463 if (!(NewTripPoint) && (Capacity)) Status = STATUS_NOT_SUPPORTED;
464 if (!NT_SUCCESS(Status))
465 {
466 /* We failed to set the trip point, or there wasn't one settable */
467 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
468 if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
469 DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status);
470 return Status;
471 }
472
473 /* Read the new BST data to see the latest state */
474 Status = CmBattGetBstData(DeviceExtension, &BstData);
475 if (!NT_SUCCESS(Status))
476 {
477 /* We'll return failure to the caller */
478 if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
479 DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status);
480 }
481 else if ((Charging) && (BstData.RemainingCapacity >= NewTripPoint))
482 {
483 /* We are charging and our capacity is past the trip point, so trip now */
484 if (CmBattDebug & CMBATT_GENERIC_WARNING)
485 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
486 NewTripPoint, BstData.RemainingCapacity);
487 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
488 }
489 else if ((BstData.RemainingCapacity) && (Capacity))
490 {
491 /* We are discharging, and our capacity is below the trip point, trip now */
492 if (CmBattDebug & CMBATT_GENERIC_WARNING)
493 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
494 NewTripPoint, BstData.RemainingCapacity);
495 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
496 }
497
498 /* All should've went well if we got here, unless BST failed... return! */
499 if (CmBattDebug & CMBATT_GENERIC_STATUS)
500 DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
501 Capacity, DeviceExtension->RemainingCapacity);
502 if (CmBattDebug & CMBATT_ACPI_WARNING)
503 DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %x\n",
504 BatteryNotify->PowerState,
505 BatteryNotify->LowCapacity,
506 BatteryNotify->HighCapacity);
507 return Status;
508 }
509
510 NTSTATUS
511 NTAPI
512 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
513 IN ULONG Tag)
514 {
515 ULONG PsrData = 0;
516 NTSTATUS Status;
517 ULONG BstState;
518 ULONG DesignVoltage, PresentRate, RemainingCapacity;
519 PAGED_CODE();
520 if (CmBattDebug & CMBATT_GENERIC_INFO)
521 DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
522
523 /* Validate ACPI data */
524 Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
525 if (!NT_SUCCESS(Status)) return Status;
526
527 /* Check for delayed status notifications */
528 if (DeviceExtension->DelayNotification)
529 {
530 /* Process them now and don't do any other work */
531 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
532 return Status;
533 }
534
535 /* Get _BST from ACPI */
536 Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
537 if (!NT_SUCCESS(Status))
538 {
539 /* Fail */
540 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
541 return Status;
542 }
543
544 /* Clear current BST information */
545 DeviceExtension->State = 0;
546 DeviceExtension->RemainingCapacity = 0;
547 DeviceExtension->PresentVoltage = 0;
548 DeviceExtension->Rate = 0;
549
550 /* Get battery state */
551 BstState = DeviceExtension->BstData.State;
552
553 /* Is the battery both charging and discharging? */
554 if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
555 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
556 DbgPrint("************************ ACPI BIOS BUG ********************\n* "
557 "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
558 "* One battery cannot be charging and discharging at the same time.\n",
559 BstState);
560
561 /* Is the battery discharging? */
562 if (BstState & ACPI_BATT_STAT_DISCHARG)
563 {
564 /* Set power state and check if it just started discharging now */
565 DeviceExtension->State |= BATTERY_DISCHARGING;
566 if (!(DeviceExtension->State & ACPI_BATT_STAT_DISCHARG))
567 {
568 /* Remember the time when the state changed */
569 DeviceExtension->InterruptTime = KeQueryInterruptTime();
570 }
571 }
572 else if (BstState & ACPI_BATT_STAT_CHARGING)
573 {
574 /* Battery is charging, update power state */
575 DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
576 }
577
578 /* Is the battery in a critical state? */
579 if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
580
581 /* Read the voltage data */
582 DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
583
584 /* Check if we have an A/C adapter */
585 if (AcAdapterPdo)
586 {
587 /* Query information on it */
588 CmBattGetPsrData(AcAdapterPdo, &PsrData);
589 }
590 else
591 {
592 /* Otherwise, check if the battery is charging */
593 if (BstState & ACPI_BATT_STAT_CHARGING)
594 {
595 /* Then we'll assume there's a charger */
596 PsrData = 1;
597 }
598 else
599 {
600 /* Assume no charger */
601 PsrData = 0;
602 }
603 }
604
605 /* Is there a charger? */
606 if (PsrData)
607 {
608 /* Set the power state flag to reflect this */
609 DeviceExtension->State |= BATTERY_POWER_ON_LINE;
610 if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
611 DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
612 }
613 else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
614 {
615 DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
616 }
617
618 /* Get some data we'll need */
619 DesignVoltage = DeviceExtension->BifData.DesignVoltage;
620 PresentRate = DeviceExtension->BstData.PresentRate;
621 RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
622
623 /* Check if we have battery data in Watts instead of Amps */
624 if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
625 {
626 /* Get the data from the BST */
627 DeviceExtension->RemainingCapacity = RemainingCapacity;
628 DeviceExtension->Rate = PresentRate;
629
630 /* Check if the rate is invalid */
631 if (PresentRate > CM_MAX_VALUE)
632 {
633 /* Set an unknown rate and don't touch the old value */
634 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
635 if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
636 {
637 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
638 DbgPrint("---------------------- PresentRate = 0x%08x\n", PresentRate);
639 }
640 }
641 }
642 else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage))
643 {
644 /* We have voltage data, what about capacity? */
645 if (RemainingCapacity == CM_UNKNOWN_VALUE)
646 {
647 /* Unable to calculate it */
648 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
649 if (CmBattDebug & CMBATT_ACPI_WARNING)
650 {
651 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
652 DbgPrint("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n");
653 }
654 }
655 else
656 {
657 /* Compute the capacity with the information we have */
658 DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
659 }
660
661 /* Check if we have a rate */
662 if (PresentRate != CM_UNKNOWN_VALUE)
663 {
664 /* Make sure the rate isn't too large */
665 if (PresentRate > (-500 / DesignVoltage))
666 {
667 /* It is, so set unknown state */
668 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
669 if (CmBattDebug & CMBATT_ACPI_WARNING)
670 {
671 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
672 DbgPrint("---------------------- Overflow: PresentRate = 0x%08x\n", PresentRate);
673 }
674 }
675
676 /* Compute the rate */
677 DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
678 }
679 else
680 {
681 /* We don't have a rate, so set unknown value */
682 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
683 if (CmBattDebug & CMBATT_ACPI_WARNING)
684 {
685 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
686 DbgPrint("---------------------- Present Rate = CM_UNKNOWN_VALUE\n");
687 }
688 }
689 }
690 else
691 {
692 /* We have no rate, and no capacity, set unknown values */
693 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
694 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
695 if (CmBattDebug & CMBATT_ACPI_WARNING)
696 {
697 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
698 DbgPrint("---------------------- DesignVoltage = 0x%08x\n", DesignVoltage);
699 }
700 }
701
702 /* Check if we have an unknown rate */
703 if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
704 {
705 /* The battery is discharging but we don't know by how much... this is bad! */
706 if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
707 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
708 DbgPrint("CmBattGetBatteryStatus: battery rate is unkown when battery is not charging!\n");
709 }
710 else if (DeviceExtension->State & BATTERY_DISCHARGING)
711 {
712 /* The battery is discharging, so treat the rate as a negative rate */
713 DeviceExtension->Rate = -DeviceExtension->Rate;
714 }
715 else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
716 {
717 /* We are not charging, not discharging, but have a rate? Ignore it! */
718 if (CmBattDebug & CMBATT_GENERIC_WARNING)
719 DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
720 DeviceExtension->Rate);
721 DeviceExtension->Rate = 0;
722 }
723
724 /* Done */
725 return STATUS_SUCCESS;
726 }
727
728 NTSTATUS
729 NTAPI
730 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
731 IN ULONG Tag,
732 IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
733 IN OPTIONAL LONG AtRate,
734 IN PVOID Buffer,
735 IN ULONG BufferLength,
736 OUT PULONG ReturnedLength)
737 {
738 NTSTATUS Status;
739 PVOID QueryData = NULL;
740 ULONG QueryLength = 0;
741 ULONG RemainingTime = 0;
742 ANSI_STRING TempString;
743 UNICODE_STRING TempString2;
744 WCHAR InfoBuffer[256];
745 WCHAR TempBuffer[256];
746 UNICODE_STRING InfoString;
747 ULONG RemainingCapacity;
748 BATTERY_REPORTING_SCALE BatteryReportingScale[2];
749 LONG Rate;
750 PAGED_CODE();
751 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
752 DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
753 Tag,
754 FdoExtension->DeviceId,
755 InfoLevel);
756
757 /* Check ACPI Data */
758 Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
759 if (!NT_SUCCESS(Status)) return Status;
760
761 /* Check what caller wants */
762 switch (InfoLevel)
763 {
764 case BatteryInformation:
765 /* Just return our static information */
766 QueryData = &FdoExtension->BatteryInformation;
767 QueryLength = sizeof(BATTERY_INFORMATION);
768 break;
769
770 case BatteryGranularityInformation:
771
772 /* Return our static information, we have two scales */
773 BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
774 BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
775 BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
776 BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
777 QueryData = BatteryReportingScale;
778 QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
779 break;
780
781 case BatteryEstimatedTime:
782
783 /* Check if it's been more than 2 1/2 minutes since the last change */
784 if ((KeQueryInterruptTime() - 150000000) > (FdoExtension->InterruptTime))
785 {
786 /* Get new battery status */
787 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
788
789 /* If the caller didn't specify a rate, use our static one */
790 Rate = AtRate;
791 if (!Rate) Rate = FdoExtension->Rate;
792
793 /* If we don't have a valid negative rate, use unknown value */
794 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
795
796 /* Grab the remaining capacity */
797 RemainingCapacity = FdoExtension->RemainingCapacity;
798
799 /* See if we don't know one or the other */
800 if ((Rate == BATTERY_UNKNOWN_RATE) ||
801 (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
802 {
803 /* If the battery is discharging, we can't give out a time */
804 if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
805 (CmBattDebug & CMBATT_GENERIC_WARNING))
806 DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
807
808 /* Check if we don't have a rate and capacity is going down */
809 if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
810 (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
811 {
812 /* We have to fail, since we lack data */
813 Status = STATUS_INVALID_DEVICE_REQUEST;
814 if (CmBattDebug & CMBATT_GENERIC_WARNING)
815 DbgPrint("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n");
816 }
817
818 /* If we don't have capacity, the rate is useless */
819 if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
820 {
821 /* We have to fail the request */
822 Status = STATUS_INVALID_DEVICE_REQUEST;
823 if (CmBattDebug & CMBATT_GENERIC_WARNING)
824 DbgPrint("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
825 }
826 }
827 else
828 {
829 /* We have data, but is it valid? */
830 if (RemainingCapacity > 0x123456)
831 {
832 /* The capacity seems bogus, so don't use it */
833 if (CmBattDebug & CMBATT_ACPI_WARNING)
834 DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
835 }
836 else
837 {
838 /* Compute the remaining time in seconds, based on rate */
839 RemainingTime = (RemainingCapacity * 3600) / -Rate;
840 }
841 }
842 }
843
844 /* Return the remaining time */
845 QueryData = &RemainingTime;
846 QueryLength = sizeof(ULONG);
847 break;
848
849 case BatteryDeviceName:
850
851 /* Build the model number string */
852 RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
853
854 /* Convert it to Unicode */
855 InfoString.Buffer = InfoBuffer;
856 InfoString.MaximumLength = sizeof(InfoBuffer);
857 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
858
859 /* Return the unicode buffer */
860 QueryData = InfoString.Buffer;
861 QueryLength = InfoString.Length;
862 break;
863
864 case BatteryTemperature:
865 case BatteryManufactureDate:
866
867 /* We don't support these */
868 Status = STATUS_INVALID_DEVICE_REQUEST;
869 break;
870
871 case BatteryManufactureName:
872
873 /* Build the OEM info string */
874 RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
875
876 /* Convert it to Unicode */
877 InfoString.Buffer = InfoBuffer;
878 InfoString.MaximumLength = sizeof(InfoBuffer);
879 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
880
881 /* Return the unicode buffer */
882 QueryData = InfoString.Buffer;
883 QueryLength = InfoString.Length;
884 break;
885
886 case BatteryUniqueID:
887
888 /* Build the serial number string */
889 RtlInitAnsiString(&TempString, FdoExtension->SerialNumber);
890
891 /* Convert it to Unicode */
892 InfoString.Buffer = InfoBuffer;
893 InfoString.MaximumLength = sizeof(InfoBuffer);
894 RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
895
896 /* Setup a temporary string for concatenation */
897 TempString2.Buffer = TempBuffer;
898 TempString2.MaximumLength = sizeof(TempBuffer);
899
900 /* Check if there's an OEM string */
901 if (FdoExtension->OemInfo[0])
902 {
903 /* Build the OEM info string */
904 RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
905
906 /* Convert it to Unicode and append it */
907 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
908 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
909 }
910
911 /* Build the model number string */
912 RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
913
914 /* Convert it to Unicode and append it */
915 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
916 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
917
918 /* Return the final appended string */
919 QueryData = InfoString.Buffer;
920 QueryLength = InfoString.Length;
921 break;
922
923 default:
924
925 /* Everything else is unknown */
926 Status = STATUS_INVALID_PARAMETER;
927 break;
928 }
929
930 /* Return the required length and check if the caller supplied enough */
931 *ReturnedLength = QueryLength;
932 if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
933
934 /* Copy the data if there's enough space and it exists */
935 if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
936
937 /* Return function result */
938 return Status;
939 }
940
941 NTSTATUS
942 NTAPI
943 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
944 IN ULONG Tag,
945 IN PBATTERY_STATUS BatteryStatus)
946 {
947 NTSTATUS Status;
948 PAGED_CODE();
949 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
950 DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
951
952 /* Query ACPI information */
953 Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
954 if (NT_SUCCESS(Status))
955 {
956 BatteryStatus->PowerState = DeviceExtension->State;
957 BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
958 BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
959 BatteryStatus->Rate = DeviceExtension->Rate;
960 }
961
962 /* Return status */
963 if (CmBattDebug & (CMBATT_GENERIC_INFO))
964 DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
965 BatteryStatus->PowerState,
966 BatteryStatus->Capacity,
967 BatteryStatus->Voltage,
968 BatteryStatus->Rate);
969 return Status;
970 }
971
972 NTSTATUS
973 NTAPI
974 DriverEntry(IN PDRIVER_OBJECT DriverObject,
975 IN PUNICODE_STRING RegistryPath)
976 {
977 NTSTATUS Status;
978 PDRIVER_EXTENSION DriverExtension;
979 OBJECT_ATTRIBUTES ObjectAttributes;
980 UNICODE_STRING CallbackName;
981
982 /* Allocate registry path */
983 GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
984 GlobalRegistryPath.Length = RegistryPath->Length;
985 GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
986 GlobalRegistryPath.MaximumLength,
987 'MtaB');
988 if (!GlobalRegistryPath.Buffer)
989 {
990 /* Fail if we're out of memory this early */
991 if (CmBattDebug & CMBATT_GENERIC_WARNING)
992 DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
993 return STATUS_INSUFFICIENT_RESOURCES;
994 }
995
996 /* Buffer allocated, copy the string */
997 RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
998 if (CmBattDebug & CMBATT_GENERIC_INFO)
999 DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
1000 DriverObject,
1001 RegistryPath->Buffer);
1002
1003 /* Setup the major dispatchers */
1004 DriverObject->MajorFunction[0] = CmBattOpenClose;
1005 DriverObject->MajorFunction[2] = CmBattOpenClose;
1006 DriverObject->MajorFunction[14] = CmBattIoctl;
1007 DriverObject->MajorFunction[22] = CmBattPowerDispatch;
1008 DriverObject->MajorFunction[27] = CmBattPnpDispatch;
1009 DriverObject->MajorFunction[23] = CmBattSystemControl;
1010
1011 /* And the unload routine */
1012 DriverObject->DriverUnload = CmBattUnload;
1013
1014 /* And the add device routine */
1015 DriverExtension = DriverObject->DriverExtension;
1016 DriverExtension->AddDevice = CmBattAddDevice;
1017
1018 /* Create a power callback */
1019 RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
1020 InitializeObjectAttributes(&ObjectAttributes,
1021 &CallbackName,
1022 OBJ_KERNEL_HANDLE,
1023 NULL,
1024 NULL);
1025 Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
1026 if (!NT_SUCCESS(Status))
1027 {
1028 /* No callback, fail */
1029 CmBattPowerCallBackObject = 0;
1030 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1031 DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
1032 }
1033 else
1034 {
1035 /* Register the power callback now */
1036 CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
1037 (PVOID)CmBattPowerCallBack,
1038 DriverObject);
1039 if (CmBattPowerCallBackRegistration)
1040 {
1041 /* Last thing: setup our DPC and timer for battery wake */
1042 KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
1043 KeInitializeTimer(&CmBattWakeDpcTimerObject);
1044 }
1045 else
1046 {
1047 ObfDereferenceObject(CmBattPowerCallBackObject);
1048 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1049 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1050 }
1051
1052 /* All good */
1053 Status = STATUS_SUCCESS;
1054 }
1055
1056 /* Return failure or success */
1057 return Status;
1058 }
1059
1060 /* EOF */