[CMBATT]: Implement CmBattQueryTag and CmBattNotifyHandler for getting ACPI Battery...
[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 = 0xFFFFFFFF;
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 = 0xFFFFFFFF;
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(PCMBATT_DEVICE_EXTENSION DeviceExtension)
326 {
327 UNIMPLEMENTED;
328 return STATUS_NOT_IMPLEMENTED;
329 }
330
331 NTSTATUS
332 NTAPI
333 CmBattSetStatusNotify(PCMBATT_DEVICE_EXTENSION DeviceExtension,
334 ULONG BatteryTag,
335 PBATTERY_NOTIFY BatteryNotify)
336 {
337 UNIMPLEMENTED;
338 return STATUS_NOT_IMPLEMENTED;
339 }
340
341 NTSTATUS
342 NTAPI
343 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
344 IN ULONG Tag)
345 {
346 ULONG PsrData = 0;
347 NTSTATUS Status;
348 ULONG BstState;
349 ULONG DesignVoltage, PresentRate, RemainingCapacity;
350 PAGED_CODE();
351 if (CmBattDebug & CMBATT_GENERIC_INFO)
352 DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
353
354 /* Validate ACPI data */
355 Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
356 if (!NT_SUCCESS(Status)) return Status;
357
358 /* Check for delayed status notifications */
359 if (DeviceExtension->DelayNotification)
360 {
361 /* Process them now and don't do any other work */
362 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
363 return Status;
364 }
365
366 /* Get _BST from ACPI */
367 Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
368 if (!NT_SUCCESS(Status))
369 {
370 /* Fail */
371 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
372 return Status;
373 }
374
375 /* Clear current BST information */
376 DeviceExtension->State = 0;
377 DeviceExtension->RemainingCapacity = 0;
378 DeviceExtension->PresentVoltage = 0;
379 DeviceExtension->Rate = 0;
380
381 /* Get battery state */
382 BstState = DeviceExtension->BstData.State;
383
384 /* Is the battery both charging and discharging? */
385 if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
386 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
387 DbgPrint("************************ ACPI BIOS BUG ********************\n* "
388 "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
389 "* One battery cannot be charging and discharging at the same time.\n",
390 BstState);
391
392 /* Is the battery discharging? */
393 if (BstState & ACPI_BATT_STAT_DISCHARG)
394 {
395 /* Set power state and check if it just started discharging now */
396 DeviceExtension->State |= BATTERY_DISCHARGING;
397 if (!(DeviceExtension->State & ACPI_BATT_STAT_DISCHARG))
398 {
399 /* Remember the time when the state changed */
400 DeviceExtension->InterruptTime = KeQueryInterruptTime();
401 }
402 }
403 else if (BstState & ACPI_BATT_STAT_CHARGING)
404 {
405 /* Battery is charging, update power state */
406 DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
407 }
408
409 /* Is the battery in a critical state? */
410 if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
411
412 /* Read the voltage data */
413 DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
414
415 /* Check if we have an A/C adapter */
416 if (AcAdapterPdo)
417 {
418 /* Query information on it */
419 CmBattGetPsrData(AcAdapterPdo, &PsrData);
420 }
421 else
422 {
423 /* Otherwise, check if the battery is charging */
424 if (BstState & ACPI_BATT_STAT_CHARGING)
425 {
426 /* Then we'll assume there's a charger */
427 PsrData = 1;
428 }
429 else
430 {
431 /* Assume no charger */
432 PsrData = 0;
433 }
434 }
435
436 /* Is there a charger? */
437 if (PsrData)
438 {
439 /* Set the power state flag to reflect this */
440 DeviceExtension->State |= BATTERY_POWER_ON_LINE;
441 if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
442 DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
443 }
444 else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
445 {
446 DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
447 }
448
449 /* Get some data we'll need */
450 DesignVoltage = DeviceExtension->BifData.DesignVoltage;
451 PresentRate = DeviceExtension->BstData.PresentRate;
452 RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
453
454 /* Check if we have battery data in Watts instead of Amps */
455 if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
456 {
457 /* Get the data from the BST */
458 DeviceExtension->RemainingCapacity = RemainingCapacity;
459 DeviceExtension->Rate = PresentRate;
460
461 /* Check if the rate is invalid */
462 if (PresentRate > CM_MAX_VALUE)
463 {
464 /* Set an unknown rate and don't touch the old value */
465 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
466 if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
467 {
468 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
469 DbgPrint("---------------------- PresentRate = 0x%08x\n", PresentRate);
470 }
471 }
472 }
473 else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage))
474 {
475 /* We have voltage data, what about capacity? */
476 if (RemainingCapacity == CM_UNKNOWN_VALUE)
477 {
478 /* Unable to calculate it */
479 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
480 if (CmBattDebug & CMBATT_ACPI_WARNING)
481 {
482 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
483 DbgPrint("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n");
484 }
485 }
486 else
487 {
488 /* Compute the capacity with the information we have */
489 DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
490 }
491
492 /* Check if we have a rate */
493 if (PresentRate != CM_UNKNOWN_VALUE)
494 {
495 /* Make sure the rate isn't too large */
496 if (PresentRate > (-500 / DesignVoltage))
497 {
498 /* It is, so set unknown state */
499 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
500 if (CmBattDebug & CMBATT_ACPI_WARNING)
501 {
502 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
503 DbgPrint("---------------------- Overflow: PresentRate = 0x%08x\n", PresentRate);
504 }
505 }
506
507 /* Compute the rate */
508 DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
509 }
510 else
511 {
512 /* We don't have a rate, so set unknown value */
513 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
514 if (CmBattDebug & CMBATT_ACPI_WARNING)
515 {
516 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
517 DbgPrint("---------------------- Present Rate = CM_UNKNOWN_VALUE\n");
518 }
519 }
520 }
521 else
522 {
523 /* We have no rate, and no capacity, set unknown values */
524 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
525 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
526 if (CmBattDebug & CMBATT_ACPI_WARNING)
527 {
528 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
529 DbgPrint("---------------------- DesignVoltage = 0x%08x\n", DesignVoltage);
530 }
531 }
532
533 /* Check if we have an unknown rate */
534 if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
535 {
536 /* The battery is discharging but we don't know by how much... this is bad! */
537 if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
538 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
539 DbgPrint("CmBattGetBatteryStatus: battery rate is unkown when battery is not charging!\n");
540 }
541 else if (DeviceExtension->State & BATTERY_DISCHARGING)
542 {
543 /* The battery is discharging, so treat the rate as a negative rate */
544 DeviceExtension->Rate = -DeviceExtension->Rate;
545 }
546 else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
547 {
548 /* We are not charging, not discharging, but have a rate? Ignore it! */
549 if (CmBattDebug & CMBATT_GENERIC_WARNING)
550 DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
551 DeviceExtension->Rate);
552 DeviceExtension->Rate = 0;
553 }
554
555 /* Done */
556 return STATUS_SUCCESS;
557 }
558
559 NTSTATUS
560 NTAPI
561 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
562 IN ULONG Tag,
563 IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
564 IN OPTIONAL LONG AtRate,
565 IN PVOID Buffer,
566 IN ULONG BufferLength,
567 OUT PULONG ReturnedLength)
568 {
569 NTSTATUS Status;
570 PVOID QueryData = NULL;
571 ULONG QueryLength = 0;
572 ULONG RemainingTime = 0;
573 ANSI_STRING TempString;
574 UNICODE_STRING TempString2;
575 WCHAR InfoBuffer[256];
576 WCHAR TempBuffer[256];
577 UNICODE_STRING InfoString;
578 ULONG RemainingCapacity;
579 BATTERY_REPORTING_SCALE BatteryReportingScale[2];
580 LONG Rate;
581 PAGED_CODE();
582 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
583 DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
584 Tag,
585 FdoExtension->DeviceId,
586 InfoLevel);
587
588 /* Check ACPI Data */
589 Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
590 if (!NT_SUCCESS(Status)) return Status;
591
592 /* Check what caller wants */
593 switch (InfoLevel)
594 {
595 case BatteryInformation:
596 /* Just return our static information */
597 QueryData = &FdoExtension->BatteryInformation;
598 QueryLength = sizeof(BATTERY_INFORMATION);
599 break;
600
601 case BatteryGranularityInformation:
602
603 /* Return our static information, we have two scales */
604 BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
605 BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
606 BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
607 BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
608 QueryData = BatteryReportingScale;
609 QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
610 break;
611
612 case BatteryEstimatedTime:
613
614 /* Check if it's been more than 2 1/2 minutes since the last change */
615 if ((KeQueryInterruptTime() - 150000000) > (FdoExtension->InterruptTime))
616 {
617 /* Get new battery status */
618 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
619
620 /* If the caller didn't specify a rate, use our static one */
621 Rate = AtRate;
622 if (!Rate) Rate = FdoExtension->Rate;
623
624 /* If we don't have a valid negative rate, use unknown value */
625 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
626
627 /* Grab the remaining capacity */
628 RemainingCapacity = FdoExtension->RemainingCapacity;
629
630 /* See if we don't know one or the other */
631 if ((Rate == BATTERY_UNKNOWN_RATE) ||
632 (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
633 {
634 /* If the battery is discharging, we can't give out a time */
635 if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
636 (CmBattDebug & CMBATT_GENERIC_WARNING))
637 DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
638
639 /* Check if we don't have a rate and capacity is going down */
640 if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
641 (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
642 {
643 /* We have to fail, since we lack data */
644 Status = STATUS_INVALID_DEVICE_REQUEST;
645 if (CmBattDebug & CMBATT_GENERIC_WARNING)
646 DbgPrint("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n");
647 }
648
649 /* If we don't have capacity, the rate is useless */
650 if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
651 {
652 /* We have to fail the request */
653 Status = STATUS_INVALID_DEVICE_REQUEST;
654 if (CmBattDebug & CMBATT_GENERIC_WARNING)
655 DbgPrint("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
656 }
657 }
658 else
659 {
660 /* We have data, but is it valid? */
661 if (RemainingCapacity > 0x123456)
662 {
663 /* The capacity seems bogus, so don't use it */
664 if (CmBattDebug & CMBATT_ACPI_WARNING)
665 DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
666 }
667 else
668 {
669 /* Compute the remaining time in seconds, based on rate */
670 RemainingTime = (RemainingCapacity * 3600) / -Rate;
671 }
672 }
673 }
674
675 /* Return the remaining time */
676 QueryData = &RemainingTime;
677 QueryLength = sizeof(ULONG);
678 break;
679
680 case BatteryDeviceName:
681
682 /* Build the model number string */
683 RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
684
685 /* Convert it to Unicode */
686 InfoString.Buffer = InfoBuffer;
687 InfoString.MaximumLength = sizeof(InfoBuffer);
688 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
689
690 /* Return the unicode buffer */
691 QueryData = InfoString.Buffer;
692 QueryLength = InfoString.Length;
693 break;
694
695 case BatteryTemperature:
696 case BatteryManufactureDate:
697
698 /* We don't support these */
699 Status = STATUS_INVALID_DEVICE_REQUEST;
700 break;
701
702 case BatteryManufactureName:
703
704 /* Build the OEM info string */
705 RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
706
707 /* Convert it to Unicode */
708 InfoString.Buffer = InfoBuffer;
709 InfoString.MaximumLength = sizeof(InfoBuffer);
710 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
711
712 /* Return the unicode buffer */
713 QueryData = InfoString.Buffer;
714 QueryLength = InfoString.Length;
715 break;
716
717 case BatteryUniqueID:
718
719 /* Build the serial number string */
720 RtlInitAnsiString(&TempString, FdoExtension->SerialNumber);
721
722 /* Convert it to Unicode */
723 InfoString.Buffer = InfoBuffer;
724 InfoString.MaximumLength = sizeof(InfoBuffer);
725 RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
726
727 /* Setup a temporary string for concatenation */
728 TempString2.Buffer = TempBuffer;
729 TempString2.MaximumLength = sizeof(TempBuffer);
730
731 /* Check if there's an OEM string */
732 if (FdoExtension->OemInfo[0])
733 {
734 /* Build the OEM info string */
735 RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
736
737 /* Convert it to Unicode and append it */
738 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
739 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
740 }
741
742 /* Build the model number string */
743 RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
744
745 /* Convert it to Unicode and append it */
746 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
747 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
748
749 /* Return the final appended string */
750 QueryData = InfoString.Buffer;
751 QueryLength = InfoString.Length;
752 break;
753
754 default:
755
756 /* Everything else is unknown */
757 Status = STATUS_INVALID_PARAMETER;
758 break;
759 }
760
761 /* Return the required length and check if the caller supplied enough */
762 *ReturnedLength = QueryLength;
763 if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
764
765 /* Copy the data if there's enough space and it exists */
766 if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
767
768 /* Return function result */
769 return Status;
770 }
771
772 NTSTATUS
773 NTAPI
774 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
775 IN ULONG Tag,
776 IN PBATTERY_STATUS BatteryStatus)
777 {
778 NTSTATUS Status;
779 PAGED_CODE();
780 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
781 DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
782
783 /* Query ACPI information */
784 Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
785 if (NT_SUCCESS(Status))
786 {
787 BatteryStatus->PowerState = DeviceExtension->State;
788 BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
789 BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
790 BatteryStatus->Rate = DeviceExtension->Rate;
791 }
792
793 /* Return status */
794 if (CmBattDebug & (CMBATT_GENERIC_INFO))
795 DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
796 BatteryStatus->PowerState,
797 BatteryStatus->Capacity,
798 BatteryStatus->Voltage,
799 BatteryStatus->Rate);
800 return Status;
801 }
802
803 NTSTATUS
804 NTAPI
805 DriverEntry(IN PDRIVER_OBJECT DriverObject,
806 IN PUNICODE_STRING RegistryPath)
807 {
808 NTSTATUS Status;
809 PDRIVER_EXTENSION DriverExtension;
810 OBJECT_ATTRIBUTES ObjectAttributes;
811 UNICODE_STRING CallbackName;
812
813 /* Allocate registry path */
814 GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
815 GlobalRegistryPath.Length = RegistryPath->Length;
816 GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
817 GlobalRegistryPath.MaximumLength,
818 'MtaB');
819 if (!GlobalRegistryPath.Buffer)
820 {
821 /* Fail if we're out of memory this early */
822 if (CmBattDebug & CMBATT_GENERIC_WARNING)
823 DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
824 return STATUS_INSUFFICIENT_RESOURCES;
825 }
826
827 /* Buffer allocated, copy the string */
828 RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
829 if (CmBattDebug & CMBATT_GENERIC_INFO)
830 DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
831 DriverObject,
832 RegistryPath->Buffer);
833
834 /* Setup the major dispatchers */
835 DriverObject->MajorFunction[0] = CmBattOpenClose;
836 DriverObject->MajorFunction[2] = CmBattOpenClose;
837 DriverObject->MajorFunction[14] = CmBattIoctl;
838 DriverObject->MajorFunction[22] = CmBattPowerDispatch;
839 DriverObject->MajorFunction[27] = CmBattPnpDispatch;
840 DriverObject->MajorFunction[23] = CmBattSystemControl;
841
842 /* And the unload routine */
843 DriverObject->DriverUnload = CmBattUnload;
844
845 /* And the add device routine */
846 DriverExtension = DriverObject->DriverExtension;
847 DriverExtension->AddDevice = CmBattAddDevice;
848
849 /* Create a power callback */
850 RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
851 InitializeObjectAttributes(&ObjectAttributes,
852 &CallbackName,
853 OBJ_KERNEL_HANDLE,
854 NULL,
855 NULL);
856 Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
857 if (!NT_SUCCESS(Status))
858 {
859 /* No callback, fail */
860 CmBattPowerCallBackObject = 0;
861 if (CmBattDebug & CMBATT_GENERIC_WARNING)
862 DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
863 }
864 else
865 {
866 /* Register the power callback now */
867 CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
868 (PVOID)CmBattPowerCallBack,
869 DriverObject);
870 if (CmBattPowerCallBackRegistration)
871 {
872 /* Last thing: setup our DPC and timer for battery wake */
873 KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
874 KeInitializeTimer(&CmBattWakeDpcTimerObject);
875 }
876 else
877 {
878 ObfDereferenceObject(CmBattPowerCallBackObject);
879 if (CmBattDebug & CMBATT_GENERIC_WARNING)
880 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
881 }
882
883 /* All good */
884 Status = STATUS_SUCCESS;
885 }
886
887 /* Return failure or success */
888 return Status;
889 }
890
891 /* EOF */