Sync with trunk rev.61910 to get latest improvements and bugfixes.
[reactos.git] / 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 #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
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");
69 }
70 }
71 }
72
73 VOID
74 NTAPI
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
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
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 NTSTATUS
277 NTAPI
278 CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension,
279 ULONG BatteryTag)
280 {
281 UNIMPLEMENTED;
282 return STATUS_NOT_IMPLEMENTED;
283 }
284
285 NTSTATUS
286 NTAPI
287 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject,
288 IN PIRP Irp)
289 {
290 NTSTATUS Status = STATUS_SUCCESS;
291 PIO_STACK_LOCATION IoStackLocation;
292 UCHAR Major;
293 ULONG Count;
294 PCMBATT_DEVICE_EXTENSION DeviceExtension;
295 PAGED_CODE();
296 if (CmBattDebug & CMBATT_GENERIC_INFO) DPRINT("CmBattOpenClose\n");
297
298 /* Grab the device extension and lock it */
299 DeviceExtension = DeviceObject->DeviceExtension;
300 ExAcquireFastMutex(&DeviceExtension->FastMutex);
301
302 /* Check if someone is trying to open a device that doesn't exist yet */
303 Count = DeviceExtension->HandleCount;
304 if (Count == 0xFFFFFFFF)
305 {
306 /* Fail the request */
307 Status = STATUS_NO_SUCH_DEVICE;
308 if (CmBattDebug & CMBATT_PNP_INFO)
309 {
310 DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
311 DeviceExtension->Tag);
312 }
313 goto Complete;
314 }
315
316 /* Check if this is an open or close */
317 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
318 Major = IoStackLocation->MajorFunction;
319 if (Major == IRP_MJ_CREATE)
320 {
321 /* Increment the open count */
322 DeviceExtension->HandleCount = Count + 1;
323 if (CmBattDebug & CMBATT_PNP_INFO)
324 {
325 DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
326 DeviceExtension->DeviceId, Count + 1);
327 }
328 }
329 else if (Major == IRP_MJ_CLOSE)
330 {
331 /* Decrement the open count */
332 DeviceExtension->HandleCount = Count - 1;
333 if (CmBattDebug & CMBATT_PNP_INFO)
334 {
335 DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
336 DeviceExtension->DeviceId, Count + 1);
337 }
338 }
339
340 Complete:
341 /* Release lock and complete request */
342 ExReleaseFastMutex(&DeviceExtension->FastMutex);
343 Irp->IoStatus.Status = Status;
344 IoCompleteRequest(Irp, IO_NO_INCREMENT);
345 return Status;
346 }
347
348 NTSTATUS
349 NTAPI
350 CmBattIoctl(IN PDEVICE_OBJECT DeviceObject,
351 IN PIRP Irp)
352 {
353 PCMBATT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension;
354 NTSTATUS Status;
355 PIO_STACK_LOCATION IoStackLocation;
356 ULONG IoControlCode, OutputBufferLength, InputBufferLength;
357 PAGED_CODE();
358 if (CmBattDebug & 2) DbgPrint("CmBattIoctl\n");
359
360 /* Acquire the remove lock */
361 Status = IoAcquireRemoveLock(&DeviceExtension->RemoveLock, 0);
362 if (!NT_SUCCESS(Status))
363 {
364 /* It's too late, fail */
365 Irp->IoStatus.Status = STATUS_DEVICE_REMOVED;
366 IoCompleteRequest(Irp, IO_NO_INCREMENT);
367 return STATUS_DEVICE_REMOVED;
368 }
369
370 /* There's nothing to do for an AC adapter */
371 if (DeviceExtension->FdoType == CmBattAcAdapter)
372 {
373 /* Pass it down, and release the remove lock */
374 IoSkipCurrentIrpStackLocation(Irp);
375 Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
376 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
377 return Status;
378 }
379
380 /* Send to class driver */
381 Status = BatteryClassIoctl(DeviceExtension->ClassData, Irp);
382 if (Status == STATUS_NOT_SUPPORTED)
383 {
384 /* Read IOCTL information from IRP stack */
385 IoStackLocation = IoGetCurrentIrpStackLocation(Irp);
386 IoControlCode = IoStackLocation->Parameters.DeviceIoControl.IoControlCode;
387 OutputBufferLength = IoStackLocation->Parameters.DeviceIoControl.OutputBufferLength;
388 InputBufferLength = IoStackLocation->Parameters.DeviceIoControl.InputBufferLength;
389 if (CmBattDebug & 4)
390 DbgPrint("CmBattIoctl: Received Direct Access IOCTL %x\n", IoControlCode);
391
392 /* Handle internal IOCTLs */
393 switch (IoControlCode)
394 {
395 case IOCTL_BATTERY_QUERY_UNIQUE_ID:
396
397 /* Data is 4 bytes long */
398 if (OutputBufferLength == sizeof(ULONG))
399 {
400 /* Query it */
401 Status = CmBattGetUniqueId(DeviceExtension->PdoDeviceObject,
402 Irp->AssociatedIrp.SystemBuffer);
403 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
404 }
405 else
406 {
407 /* Buffer size invalid */
408 Status = STATUS_INVALID_BUFFER_SIZE;
409 }
410 break;
411
412 case IOCTL_BATTERY_QUERY_STA:
413
414 /* Data is 4 bytes long */
415 if (OutputBufferLength == sizeof(ULONG))
416 {
417 /* Query it */
418 Status = CmBattGetStaData(DeviceExtension->PdoDeviceObject,
419 Irp->AssociatedIrp.SystemBuffer);
420 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
421 }
422 else
423 {
424 /* Buffer size invalid */
425 Status = STATUS_INVALID_BUFFER_SIZE;
426 }
427 break;
428
429 case IOCTL_BATTERY_QUERY_PSR:
430
431 /* Data is 4 bytes long */
432 if (OutputBufferLength == sizeof(ULONG))
433 {
434 /* Do we have an AC adapter? */
435 if (AcAdapterPdo)
436 {
437 /* Query it */
438 Status = CmBattGetPsrData(AcAdapterPdo,
439 Irp->AssociatedIrp.SystemBuffer);
440 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ULONG);
441 }
442 else
443 {
444 /* No adapter, just a battery, so fail */
445 Status = STATUS_NO_SUCH_DEVICE;
446 }
447 }
448 else
449 {
450 /* Buffer size invalid */
451 Status = STATUS_INVALID_BUFFER_SIZE;
452 }
453 break;
454
455 case IOCTL_BATTERY_SET_TRIP_POINT:
456
457 /* Data is 4 bytes long */
458 if (InputBufferLength == sizeof(ULONG))
459 {
460 /* Query it */
461 Status = CmBattSetTripPpoint(DeviceExtension,
462 *(PULONG)Irp->AssociatedIrp.SystemBuffer);
463 Irp->IoStatus.Information = 0;
464 }
465 else
466 {
467 /* Buffer size invalid */
468 Status = STATUS_INVALID_BUFFER_SIZE;
469 }
470 break;
471
472 case IOCTL_BATTERY_QUERY_BIF:
473
474 /* Data is 1060 bytes long */
475 if (OutputBufferLength == sizeof(ACPI_BIF_DATA))
476 {
477 /* Query it */
478 Status = CmBattGetBifData(DeviceExtension,
479 Irp->AssociatedIrp.SystemBuffer);
480 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BIF_DATA);
481 }
482 else
483 {
484 /* Buffer size invalid */
485 Status = STATUS_INVALID_BUFFER_SIZE;
486 }
487 break;
488
489 case IOCTL_BATTERY_QUERY_BST:
490
491 /* Data is 16 bytes long */
492 if (OutputBufferLength == sizeof(ACPI_BST_DATA))
493 {
494 /* Query it */
495 Status = CmBattGetBstData(DeviceExtension,
496 Irp->AssociatedIrp.SystemBuffer);
497 if (NT_SUCCESS(Status)) Irp->IoStatus.Information = sizeof(ACPI_BST_DATA);
498 }
499 else
500 {
501 /* Buffer size invalid */
502 Status = STATUS_INVALID_BUFFER_SIZE;
503 }
504 break;
505
506 default:
507
508 /* Unknown, let us pass it on to ACPI */
509 if (CmBattDebug & 0xC)
510 DbgPrint("CmBattIoctl: Unknown IOCTL %x\n", IoControlCode);
511 break;
512 }
513
514 /* Did someone pick it up? */
515 if (Status != STATUS_NOT_SUPPORTED)
516 {
517 /* Complete the request */
518 Irp->IoStatus.Status = Status;
519 IoCompleteRequest(Irp, IO_NO_INCREMENT);
520 }
521 else
522 {
523 /* Still unsupported, try ACPI */
524 IoSkipCurrentIrpStackLocation(Irp);
525 Status = IoCallDriver(DeviceExtension->AttachedDevice, Irp);
526 }
527 }
528
529 /* Release the remove lock and return status */
530 IoReleaseRemoveLock(&DeviceExtension->RemoveLock, Irp);
531 return Status;
532 }
533
534 NTSTATUS
535 NTAPI
536 CmBattQueryTag(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
537 OUT PULONG Tag)
538 {
539 PDEVICE_OBJECT PdoDevice;
540 ULONG StaData;
541 ULONG NewTag;
542 NTSTATUS Status;
543 PAGED_CODE();
544 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
545 DbgPrint("CmBattQueryTag - Tag (%d), Battery %x, Device %d\n",
546 *Tag, DeviceExtension, DeviceExtension->DeviceId);
547
548 /* Get PDO and clear notification flag */
549 PdoDevice = DeviceExtension->PdoDeviceObject;
550 DeviceExtension->NotifySent = 0;
551
552 /* Get _STA from PDO (we need the machine status, not the battery status) */
553 Status = CmBattGetStaData(PdoDevice, &StaData);
554 if (NT_SUCCESS(Status))
555 {
556 /* Is a battery present? */
557 if (StaData & ACPI_STA_BATTERY_PRESENT)
558 {
559 /* Do we not have a tag yet? */
560 if (!DeviceExtension->Tag)
561 {
562 /* Set the new tag value, reset tags if we reached the maximum */
563 NewTag = DeviceExtension->TagData;
564 if (DeviceExtension->TagData++ == 0xFFFFFFFF) NewTag = 1;
565 DeviceExtension->Tag = NewTag;
566 if (CmBattDebug & CMBATT_GENERIC_INFO)
567 DbgPrint("CmBattQueryTag - New Tag: (%d)\n", DeviceExtension->Tag);
568
569 /* Reset trip point data */
570 DeviceExtension->TripPointOld = 0;
571 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
572
573 /* Clear AR lock and set new interrupt time */
574 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
575 DeviceExtension->InterruptTime = KeQueryInterruptTime();
576 }
577 }
578 else
579 {
580 /* No battery, so no tag */
581 DeviceExtension->Tag = 0;
582 Status = STATUS_NO_SUCH_DEVICE;
583 }
584 }
585
586 /* Return the tag and status result */
587 *Tag = DeviceExtension->Tag;
588 if (CmBattDebug & CMBATT_ACPI_WARNING)
589 DbgPrint("CmBattQueryTag: Returning Tag: 0x%x, status 0x%x\n", *Tag, Status);
590 return Status;
591 }
592
593 NTSTATUS
594 NTAPI
595 CmBattDisableStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension)
596 {
597 NTSTATUS Status;
598 PAGED_CODE();
599 if (CmBattDebug & 0xA) DbgPrint("CmBattDisableStatusNotify\n");
600
601 /* Do we have a trip point */
602 if (DeviceExtension->TripPointSet)
603 {
604 /* Is there a current value set? */
605 if (DeviceExtension->TripPointValue)
606 {
607 /* Reset it back to 0 */
608 DeviceExtension->TripPointValue = 0;
609 Status = CmBattSetTripPpoint(DeviceExtension, 0);
610 if (!NT_SUCCESS(Status))
611 {
612 /* If it failed, set unknown/invalid value */
613 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
614 if (CmBattDebug & 8)
615 DbgPrint("CmBattDisableStatusNotify: SetTripPoint failed - %x\n", Status);
616 }
617 }
618 else
619 {
620 /* No trip point set, so this is a successful no-op */
621 Status = STATUS_SUCCESS;
622 }
623 }
624 else
625 {
626 /* Nothing we can do */
627 Status = STATUS_OBJECT_NAME_NOT_FOUND;
628 }
629
630 /* Return status */
631 return Status;
632 }
633
634 NTSTATUS
635 NTAPI
636 CmBattSetStatusNotify(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
637 IN ULONG BatteryTag,
638 IN PBATTERY_NOTIFY BatteryNotify)
639 {
640 NTSTATUS Status;
641 ACPI_BST_DATA BstData;
642 ULONG Capacity, NewTripPoint, TripPoint, DesignVoltage;
643 BOOLEAN Charging;
644 PAGED_CODE();
645 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
646 DbgPrint("CmBattSetStatusNotify: Tag (%d) Target(0x%x)\n",
647 BatteryTag, BatteryNotify->LowCapacity);
648
649 /* Update any ACPI evaluations */
650 Status = CmBattVerifyStaticInfo(DeviceExtension, BatteryTag);
651 if (!NT_SUCCESS(Status)) return Status;
652
653 /* Trip point not supported, fail */
654 if (!DeviceExtension->TripPointSet) return STATUS_OBJECT_NAME_NOT_FOUND;
655
656 /* Are both capacities known? */
657 if ((BatteryNotify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
658 (BatteryNotify->LowCapacity == BATTERY_UNKNOWN_CAPACITY))
659 {
660 /* We can't set trip points without these */
661 if (CmBattDebug & CMBATT_GENERIC_WARNING)
662 DbgPrint("CmBattSetStatusNotify: Failing request because of BATTERY_UNKNOWN_CAPACITY.\n");
663 return STATUS_NOT_SUPPORTED;
664 }
665
666 /* Is the battery charging? */
667 Charging = DeviceExtension->BstData.State & ACPI_BATT_STAT_CHARGING;
668 if (Charging)
669 {
670 /* Then the trip point is when we hit the cap */
671 Capacity = BatteryNotify->HighCapacity;
672 NewTripPoint = BatteryNotify->HighCapacity;
673 }
674 else
675 {
676 /* Otherwise it's when we discharge to the bottom */
677 Capacity = BatteryNotify->LowCapacity;
678 NewTripPoint = BatteryNotify->LowCapacity;
679 }
680
681 /* Do we have data in Amps or Watts? */
682 if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_AMPS)
683 {
684 /* We need the voltage to do the conversion */
685 DesignVoltage = DeviceExtension->BifData.DesignVoltage;
686 if ((DesignVoltage != BATTERY_UNKNOWN_VOLTAGE) && (DesignVoltage))
687 {
688 /* Convert from mAh into Ah */
689 TripPoint = 1000 * NewTripPoint;
690 if (Charging)
691 {
692 /* Scale the high trip point */
693 NewTripPoint = (TripPoint + 500) / DesignVoltage + ((TripPoint + 500) % DesignVoltage != 0);
694 }
695 else
696 {
697 /* Scale the low trip point */
698 NewTripPoint = (TripPoint - 500) / DesignVoltage - ((TripPoint - 500) % DesignVoltage == 0);
699 }
700 }
701 else
702 {
703 /* Without knowing the voltage, Amps are not enough data on consumption */
704 Status = STATUS_NOT_SUPPORTED;
705 if (CmBattDebug & CMBATT_ACPI_WARNING)
706 DbgPrint("CmBattSetStatusNotify: Can't calculate BTP, DesignVoltage = 0x%08x\n",
707 DesignVoltage);
708 }
709 }
710 else if (Charging)
711 {
712 /* Make it trip just one past the charge cap */
713 ++NewTripPoint;
714 }
715 else if (NewTripPoint > 0)
716 {
717 /* Make it trip just one below the drain cap */
718 --NewTripPoint;
719 }
720
721 /* Do we actually have a new trip point? */
722 if (NewTripPoint == DeviceExtension->TripPointValue)
723 {
724 /* No, so there is no work to be done */
725 if (CmBattDebug & CMBATT_GENERIC_STATUS)
726 DbgPrint("CmBattSetStatusNotify: Keeping original setting: %X\n", DeviceExtension->TripPointValue);
727 return STATUS_SUCCESS;
728 }
729
730 /* Set the trip point with ACPI and check for success */
731 DeviceExtension->TripPointValue = NewTripPoint;
732 Status = CmBattSetTripPpoint(DeviceExtension, NewTripPoint);
733 if (!(NewTripPoint) && (Capacity)) Status = STATUS_NOT_SUPPORTED;
734 if (!NT_SUCCESS(Status))
735 {
736 /* We failed to set the trip point, or there wasn't one settable */
737 DeviceExtension->TripPointValue = BATTERY_UNKNOWN_CAPACITY;
738 if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
739 DbgPrint("CmBattSetStatusNotify: SetTripPoint failed - %x\n", Status);
740 return Status;
741 }
742
743 /* Read the new BST data to see the latest state */
744 Status = CmBattGetBstData(DeviceExtension, &BstData);
745 if (!NT_SUCCESS(Status))
746 {
747 /* We'll return failure to the caller */
748 if (CmBattDebug & (CMBATT_GENERIC_WARNING | CMBATT_ACPI_WARNING))
749 DbgPrint("CmBattSetStatusNotify: GetBstData - %x\n", Status);
750 }
751 else if ((Charging) && (BstData.RemainingCapacity >= NewTripPoint))
752 {
753 /* We are charging and our capacity is past the trip point, so trip now */
754 if (CmBattDebug & CMBATT_GENERIC_WARNING)
755 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
756 NewTripPoint, BstData.RemainingCapacity);
757 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
758 }
759 else if ((BstData.RemainingCapacity) && (Capacity))
760 {
761 /* We are discharging, and our capacity is below the trip point, trip now */
762 if (CmBattDebug & CMBATT_GENERIC_WARNING)
763 DbgPrint("CmBattSetStatusNotify: Trip point already crossed (1): TP = %08x, remaining capacity = %08x\n",
764 NewTripPoint, BstData.RemainingCapacity);
765 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
766 }
767
768 /* All should've went well if we got here, unless BST failed... return! */
769 if (CmBattDebug & CMBATT_GENERIC_STATUS)
770 DbgPrint("CmBattSetStatusNotify: Want %X CurrentCap %X\n",
771 Capacity, DeviceExtension->RemainingCapacity);
772 if (CmBattDebug & CMBATT_ACPI_WARNING)
773 DbgPrint("CmBattSetStatusNotify: Set to: [%#08lx][%#08lx][%#08lx] Status %x\n",
774 BatteryNotify->PowerState,
775 BatteryNotify->LowCapacity,
776 BatteryNotify->HighCapacity);
777 return Status;
778 }
779
780 NTSTATUS
781 NTAPI
782 CmBattGetBatteryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
783 IN ULONG Tag)
784 {
785 ULONG PsrData = 0;
786 NTSTATUS Status;
787 ULONG BstState;
788 ULONG DesignVoltage, PresentRate, RemainingCapacity;
789 PAGED_CODE();
790 if (CmBattDebug & CMBATT_GENERIC_INFO)
791 DbgPrint("CmBattGetBatteryStatus - CmBatt (%08x) Tag (%d)\n", DeviceExtension, Tag);
792
793 /* Validate ACPI data */
794 Status = CmBattVerifyStaticInfo(DeviceExtension, Tag);
795 if (!NT_SUCCESS(Status)) return Status;
796
797 /* Check for delayed status notifications */
798 if (DeviceExtension->DelayNotification)
799 {
800 /* Process them now and don't do any other work */
801 CmBattNotifyHandler(DeviceExtension, ACPI_BATT_NOTIFY_STATUS);
802 return Status;
803 }
804
805 /* Get _BST from ACPI */
806 Status = CmBattGetBstData(DeviceExtension, &DeviceExtension->BstData);
807 if (!NT_SUCCESS(Status))
808 {
809 /* Fail */
810 InterlockedExchange(&DeviceExtension->ArLockValue, 0);
811 return Status;
812 }
813
814 /* Clear current BST information */
815 DeviceExtension->State = 0;
816 DeviceExtension->RemainingCapacity = 0;
817 DeviceExtension->PresentVoltage = 0;
818 DeviceExtension->Rate = 0;
819
820 /* Get battery state */
821 BstState = DeviceExtension->BstData.State;
822
823 /* Is the battery both charging and discharging? */
824 if ((BstState & ACPI_BATT_STAT_DISCHARG) && (BstState & ACPI_BATT_STAT_CHARGING) &&
825 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
826 DbgPrint("************************ ACPI BIOS BUG ********************\n* "
827 "CmBattGetBatteryStatus: Invalid state: _BST method returned 0x%08x for Battery State.\n"
828 "* One battery cannot be charging and discharging at the same time.\n",
829 BstState);
830
831 /* Is the battery discharging? */
832 if (BstState & ACPI_BATT_STAT_DISCHARG)
833 {
834 /* Set power state and check if it just started discharging now */
835 DeviceExtension->State |= BATTERY_DISCHARGING;
836 if (!(DeviceExtension->State & ACPI_BATT_STAT_DISCHARG))
837 {
838 /* Remember the time when the state changed */
839 DeviceExtension->InterruptTime = KeQueryInterruptTime();
840 }
841 }
842 else if (BstState & ACPI_BATT_STAT_CHARGING)
843 {
844 /* Battery is charging, update power state */
845 DeviceExtension->State |= (BATTERY_CHARGING | BATTERY_POWER_ON_LINE);
846 }
847
848 /* Is the battery in a critical state? */
849 if (BstState & ACPI_BATT_STAT_CRITICAL) DeviceExtension->State |= BATTERY_CRITICAL;
850
851 /* Read the voltage data */
852 DeviceExtension->PresentVoltage = DeviceExtension->BstData.PresentVoltage;
853
854 /* Check if we have an A/C adapter */
855 if (AcAdapterPdo)
856 {
857 /* Query information on it */
858 CmBattGetPsrData(AcAdapterPdo, &PsrData);
859 }
860 else
861 {
862 /* Otherwise, check if the battery is charging */
863 if (BstState & ACPI_BATT_STAT_CHARGING)
864 {
865 /* Then we'll assume there's a charger */
866 PsrData = 1;
867 }
868 else
869 {
870 /* Assume no charger */
871 PsrData = 0;
872 }
873 }
874
875 /* Is there a charger? */
876 if (PsrData)
877 {
878 /* Set the power state flag to reflect this */
879 DeviceExtension->State |= BATTERY_POWER_ON_LINE;
880 if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
881 DbgPrint("CmBattGetBatteryStatus: AC adapter is connected\n");
882 }
883 else if (CmBattDebug & (CMBATT_GENERIC_INFO | CMBATT_GENERIC_STATUS))
884 {
885 DbgPrint("CmBattGetBatteryStatus: AC adapter is NOT connected\n");
886 }
887
888 /* Get some data we'll need */
889 DesignVoltage = DeviceExtension->BifData.DesignVoltage;
890 PresentRate = DeviceExtension->BstData.PresentRate;
891 RemainingCapacity = DeviceExtension->BstData.RemainingCapacity;
892
893 /* Check if we have battery data in Watts instead of Amps */
894 if (DeviceExtension->BifData.PowerUnit == ACPI_BATT_POWER_UNIT_WATTS)
895 {
896 /* Get the data from the BST */
897 DeviceExtension->RemainingCapacity = RemainingCapacity;
898 DeviceExtension->Rate = PresentRate;
899
900 /* Check if the rate is invalid */
901 if (PresentRate > CM_MAX_VALUE)
902 {
903 /* Set an unknown rate and don't touch the old value */
904 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
905 if ((PresentRate != CM_UNKNOWN_VALUE) && (CmBattDebug & CMBATT_ACPI_WARNING))
906 {
907 DbgPrint("CmBattGetBatteryStatus - Rate is greater than CM_MAX_VALUE\n");
908 DbgPrint("---------------------- PresentRate = 0x%08x\n", PresentRate);
909 }
910 }
911 }
912 else if ((DesignVoltage != CM_UNKNOWN_VALUE) && (DesignVoltage))
913 {
914 /* We have voltage data, what about capacity? */
915 if (RemainingCapacity == CM_UNKNOWN_VALUE)
916 {
917 /* Unable to calculate it */
918 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
919 if (CmBattDebug & CMBATT_ACPI_WARNING)
920 {
921 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity \n");
922 DbgPrint("---------------------- RemainingCapacity = CM_UNKNOWN_VALUE\n");
923 }
924 }
925 else
926 {
927 /* Compute the capacity with the information we have */
928 DeviceExtension->RemainingCapacity = (DesignVoltage * RemainingCapacity + 500) / 1000;
929 }
930
931 /* Check if we have a rate */
932 if (PresentRate != CM_UNKNOWN_VALUE)
933 {
934 /* Make sure the rate isn't too large */
935 if (PresentRate > (-500 / DesignVoltage))
936 {
937 /* It is, so set unknown state */
938 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
939 if (CmBattDebug & CMBATT_ACPI_WARNING)
940 {
941 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
942 DbgPrint("---------------------- Overflow: PresentRate = 0x%08x\n", PresentRate);
943 }
944 }
945
946 /* Compute the rate */
947 DeviceExtension->Rate = (PresentRate * DesignVoltage + 500) / 1000;
948 }
949 else
950 {
951 /* We don't have a rate, so set unknown value */
952 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
953 if (CmBattDebug & CMBATT_ACPI_WARNING)
954 {
955 DbgPrint("CmBattGetBatteryStatus - Can't calculate Rate \n");
956 DbgPrint("---------------------- Present Rate = CM_UNKNOWN_VALUE\n");
957 }
958 }
959 }
960 else
961 {
962 /* We have no rate, and no capacity, set unknown values */
963 DeviceExtension->Rate = BATTERY_UNKNOWN_RATE;
964 DeviceExtension->RemainingCapacity = BATTERY_UNKNOWN_CAPACITY;
965 if (CmBattDebug & CMBATT_ACPI_WARNING)
966 {
967 DbgPrint("CmBattGetBatteryStatus - Can't calculate RemainingCapacity and Rate \n");
968 DbgPrint("---------------------- DesignVoltage = 0x%08x\n", DesignVoltage);
969 }
970 }
971
972 /* Check if we have an unknown rate */
973 if (DeviceExtension->Rate == BATTERY_UNKNOWN_RATE)
974 {
975 /* The battery is discharging but we don't know by how much... this is bad! */
976 if ((BstState & ACPI_BATT_STAT_DISCHARG) &&
977 (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_WARNING)))
978 DbgPrint("CmBattGetBatteryStatus: battery rate is unkown when battery is not charging!\n");
979 }
980 else if (DeviceExtension->State & BATTERY_DISCHARGING)
981 {
982 /* The battery is discharging, so treat the rate as a negative rate */
983 DeviceExtension->Rate = -DeviceExtension->Rate;
984 }
985 else if (!(DeviceExtension->State & BATTERY_CHARGING) && (DeviceExtension->Rate))
986 {
987 /* We are not charging, not discharging, but have a rate? Ignore it! */
988 if (CmBattDebug & CMBATT_GENERIC_WARNING)
989 DbgPrint("CmBattGetBatteryStatus: battery is not charging or discharging, but rate = %x\n",
990 DeviceExtension->Rate);
991 DeviceExtension->Rate = 0;
992 }
993
994 /* Done */
995 return STATUS_SUCCESS;
996 }
997
998 NTSTATUS
999 NTAPI
1000 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension,
1001 IN ULONG Tag,
1002 IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel,
1003 IN OPTIONAL LONG AtRate,
1004 IN PVOID Buffer,
1005 IN ULONG BufferLength,
1006 OUT PULONG ReturnedLength)
1007 {
1008 NTSTATUS Status;
1009 PVOID QueryData = NULL;
1010 ULONG QueryLength = 0;
1011 ULONG RemainingTime = 0;
1012 ANSI_STRING TempString;
1013 UNICODE_STRING TempString2;
1014 WCHAR InfoBuffer[256];
1015 WCHAR TempBuffer[256];
1016 UNICODE_STRING InfoString;
1017 ULONG RemainingCapacity;
1018 BATTERY_REPORTING_SCALE BatteryReportingScale[2];
1019 LONG Rate;
1020 PAGED_CODE();
1021 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1022 DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
1023 Tag,
1024 FdoExtension->DeviceId,
1025 InfoLevel);
1026
1027 /* Check ACPI Data */
1028 Status = CmBattVerifyStaticInfo(FdoExtension, Tag);
1029 if (!NT_SUCCESS(Status)) return Status;
1030
1031 /* Check what caller wants */
1032 switch (InfoLevel)
1033 {
1034 case BatteryInformation:
1035 /* Just return our static information */
1036 QueryData = &FdoExtension->BatteryInformation;
1037 QueryLength = sizeof(BATTERY_INFORMATION);
1038 break;
1039
1040 case BatteryGranularityInformation:
1041
1042 /* Return our static information, we have two scales */
1043 BatteryReportingScale[0].Granularity = FdoExtension->BatteryCapacityGranularity1;
1044 BatteryReportingScale[0].Capacity = FdoExtension->BatteryInformation.DefaultAlert1;
1045 BatteryReportingScale[1].Granularity = FdoExtension->BatteryCapacityGranularity2;
1046 BatteryReportingScale[1].Capacity = FdoExtension->BatteryInformation.DesignedCapacity;
1047 QueryData = BatteryReportingScale;
1048 QueryLength = sizeof(BATTERY_REPORTING_SCALE) * 2;
1049 break;
1050
1051 case BatteryEstimatedTime:
1052
1053 /* Check if it's been more than 2 1/2 minutes since the last change */
1054 if ((KeQueryInterruptTime() - 150000000) > (FdoExtension->InterruptTime))
1055 {
1056 /* Get new battery status */
1057 CmBattGetBatteryStatus(FdoExtension, FdoExtension->Tag);
1058
1059 /* If the caller didn't specify a rate, use our static one */
1060 Rate = AtRate;
1061 if (!Rate) Rate = FdoExtension->Rate;
1062
1063 /* If we don't have a valid negative rate, use unknown value */
1064 if (Rate >= 0) Rate = BATTERY_UNKNOWN_RATE;
1065
1066 /* Grab the remaining capacity */
1067 RemainingCapacity = FdoExtension->RemainingCapacity;
1068
1069 /* See if we don't know one or the other */
1070 if ((Rate == BATTERY_UNKNOWN_RATE) ||
1071 (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY))
1072 {
1073 /* If the battery is discharging, we can't give out a time */
1074 if ((FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG) &&
1075 (CmBattDebug & CMBATT_GENERIC_WARNING))
1076 DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
1077
1078 /* Check if we don't have a rate and capacity is going down */
1079 if ((FdoExtension->Rate == BATTERY_UNKNOWN_RATE) &&
1080 (FdoExtension->BstData.State & ACPI_BATT_STAT_DISCHARG))
1081 {
1082 /* We have to fail, since we lack data */
1083 Status = STATUS_INVALID_DEVICE_REQUEST;
1084 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1085 DbgPrint("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n");
1086 }
1087
1088 /* If we don't have capacity, the rate is useless */
1089 if (RemainingCapacity == BATTERY_UNKNOWN_CAPACITY)
1090 {
1091 /* We have to fail the request */
1092 Status = STATUS_INVALID_DEVICE_REQUEST;
1093 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1094 DbgPrint("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
1095 }
1096 }
1097 else
1098 {
1099 /* We have data, but is it valid? */
1100 if (RemainingCapacity > 0x123456)
1101 {
1102 /* The capacity seems bogus, so don't use it */
1103 if (CmBattDebug & CMBATT_ACPI_WARNING)
1104 DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
1105 }
1106 else
1107 {
1108 /* Compute the remaining time in seconds, based on rate */
1109 RemainingTime = (RemainingCapacity * 3600) / -Rate;
1110 }
1111 }
1112 }
1113
1114 /* Return the remaining time */
1115 QueryData = &RemainingTime;
1116 QueryLength = sizeof(ULONG);
1117 break;
1118
1119 case BatteryDeviceName:
1120
1121 /* Build the model number string */
1122 RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
1123
1124 /* Convert it to Unicode */
1125 InfoString.Buffer = InfoBuffer;
1126 InfoString.MaximumLength = sizeof(InfoBuffer);
1127 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1128
1129 /* Return the unicode buffer */
1130 QueryData = InfoString.Buffer;
1131 QueryLength = InfoString.Length;
1132 break;
1133
1134 case BatteryTemperature:
1135 case BatteryManufactureDate:
1136
1137 /* We don't support these */
1138 Status = STATUS_INVALID_DEVICE_REQUEST;
1139 break;
1140
1141 case BatteryManufactureName:
1142
1143 /* Build the OEM info string */
1144 RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
1145
1146 /* Convert it to Unicode */
1147 InfoString.Buffer = InfoBuffer;
1148 InfoString.MaximumLength = sizeof(InfoBuffer);
1149 Status = RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1150
1151 /* Return the unicode buffer */
1152 QueryData = InfoString.Buffer;
1153 QueryLength = InfoString.Length;
1154 break;
1155
1156 case BatteryUniqueID:
1157
1158 /* Build the serial number string */
1159 RtlInitAnsiString(&TempString, FdoExtension->SerialNumber);
1160
1161 /* Convert it to Unicode */
1162 InfoString.Buffer = InfoBuffer;
1163 InfoString.MaximumLength = sizeof(InfoBuffer);
1164 RtlAnsiStringToUnicodeString(&InfoString, &TempString, 0);
1165
1166 /* Setup a temporary string for concatenation */
1167 TempString2.Buffer = TempBuffer;
1168 TempString2.MaximumLength = sizeof(TempBuffer);
1169
1170 /* Check if there's an OEM string */
1171 if (FdoExtension->OemInfo[0])
1172 {
1173 /* Build the OEM info string */
1174 RtlInitAnsiString(&TempString, FdoExtension->OemInfo);
1175
1176 /* Convert it to Unicode and append it */
1177 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1178 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1179 }
1180
1181 /* Build the model number string */
1182 RtlInitAnsiString(&TempString, FdoExtension->ModelNumber);
1183
1184 /* Convert it to Unicode and append it */
1185 RtlAnsiStringToUnicodeString(&TempString2, &TempString, 0);
1186 RtlAppendUnicodeStringToString(&InfoString, &TempString2);
1187
1188 /* Return the final appended string */
1189 QueryData = InfoString.Buffer;
1190 QueryLength = InfoString.Length;
1191 break;
1192
1193 default:
1194
1195 /* Everything else is unknown */
1196 Status = STATUS_INVALID_PARAMETER;
1197 break;
1198 }
1199
1200 /* Return the required length and check if the caller supplied enough */
1201 *ReturnedLength = QueryLength;
1202 if (BufferLength < QueryLength) Status = STATUS_BUFFER_TOO_SMALL;
1203
1204 /* Copy the data if there's enough space and it exists */
1205 if ((NT_SUCCESS(Status)) && (QueryData)) RtlCopyMemory(Buffer, QueryData, QueryLength);
1206
1207 /* Return function result */
1208 return Status;
1209 }
1210
1211 NTSTATUS
1212 NTAPI
1213 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension,
1214 IN ULONG Tag,
1215 IN PBATTERY_STATUS BatteryStatus)
1216 {
1217 NTSTATUS Status;
1218 PAGED_CODE();
1219 if (CmBattDebug & (CMBATT_ACPI_WARNING | CMBATT_GENERIC_INFO))
1220 DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag, DeviceExtension->DeviceId);
1221
1222 /* Query ACPI information */
1223 Status = CmBattGetBatteryStatus(DeviceExtension, Tag);
1224 if (NT_SUCCESS(Status))
1225 {
1226 BatteryStatus->PowerState = DeviceExtension->State;
1227 BatteryStatus->Capacity = DeviceExtension->RemainingCapacity;
1228 BatteryStatus->Voltage = DeviceExtension->PresentVoltage;
1229 BatteryStatus->Rate = DeviceExtension->Rate;
1230 }
1231
1232 /* Return status */
1233 if (CmBattDebug & (CMBATT_GENERIC_INFO))
1234 DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
1235 BatteryStatus->PowerState,
1236 BatteryStatus->Capacity,
1237 BatteryStatus->Voltage,
1238 BatteryStatus->Rate);
1239 return Status;
1240 }
1241
1242 NTSTATUS
1243 NTAPI
1244 DriverEntry(IN PDRIVER_OBJECT DriverObject,
1245 IN PUNICODE_STRING RegistryPath)
1246 {
1247 NTSTATUS Status;
1248 PDRIVER_EXTENSION DriverExtension;
1249 OBJECT_ATTRIBUTES ObjectAttributes;
1250 UNICODE_STRING CallbackName;
1251
1252 /* Allocate registry path */
1253 GlobalRegistryPath.MaximumLength = RegistryPath->Length + sizeof(UNICODE_NULL);
1254 GlobalRegistryPath.Length = RegistryPath->Length;
1255 GlobalRegistryPath.Buffer = ExAllocatePoolWithTag(PagedPool,
1256 GlobalRegistryPath.MaximumLength,
1257 'MtaB');
1258 if (!GlobalRegistryPath.Buffer)
1259 {
1260 /* Fail if we're out of memory this early */
1261 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1262 DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
1263 return STATUS_INSUFFICIENT_RESOURCES;
1264 }
1265
1266 /* Buffer allocated, copy the string */
1267 RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
1268 if (CmBattDebug & CMBATT_GENERIC_INFO)
1269 DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
1270 DriverObject,
1271 RegistryPath->Buffer);
1272
1273 /* Setup the major dispatchers */
1274 DriverObject->MajorFunction[IRP_MJ_CREATE] = CmBattOpenClose;
1275 DriverObject->MajorFunction[IRP_MJ_CLOSE] = CmBattOpenClose;
1276 DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = CmBattIoctl;
1277 DriverObject->MajorFunction[IRP_MJ_POWER] = CmBattPowerDispatch;
1278 DriverObject->MajorFunction[IRP_MJ_PNP] = CmBattPnpDispatch;
1279 DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = CmBattSystemControl;
1280
1281 /* And the unload routine */
1282 DriverObject->DriverUnload = CmBattUnload;
1283
1284 /* And the add device routine */
1285 DriverExtension = DriverObject->DriverExtension;
1286 DriverExtension->AddDevice = CmBattAddDevice;
1287
1288 /* Create a power callback */
1289 RtlInitUnicodeString(&CallbackName, L"\\Callback\\PowerState");
1290 InitializeObjectAttributes(&ObjectAttributes,
1291 &CallbackName,
1292 OBJ_KERNEL_HANDLE,
1293 NULL,
1294 NULL);
1295 Status = ExCreateCallback(&CmBattPowerCallBackObject, &ObjectAttributes, 0, TRUE);
1296 if (!NT_SUCCESS(Status))
1297 {
1298 /* No callback, fail */
1299 CmBattPowerCallBackObject = 0;
1300 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1301 DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status);
1302 }
1303 else
1304 {
1305 /* Register the power callback now */
1306 CmBattPowerCallBackRegistration = ExRegisterCallback(CmBattPowerCallBackObject,
1307 (PVOID)CmBattPowerCallBack,
1308 DriverObject);
1309 if (CmBattPowerCallBackRegistration)
1310 {
1311 /* Last thing: setup our DPC and timer for battery wake */
1312 KeInitializeDpc(&CmBattWakeDpcObject, (PVOID)CmBattWakeDpc, DriverObject);
1313 KeInitializeTimer(&CmBattWakeDpcTimerObject);
1314 }
1315 else
1316 {
1317 ObDereferenceObject(CmBattPowerCallBackObject);
1318 if (CmBattDebug & CMBATT_GENERIC_WARNING)
1319 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
1320 }
1321
1322 /* All good */
1323 Status = STATUS_SUCCESS;
1324 }
1325
1326 /* Return failure or success */
1327 return Status;
1328 }
1329
1330 /* EOF */