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