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
9 /* INCLUDES *******************************************************************/
13 /* GLOBALS ********************************************************************/
16 PCALLBACK_OBJECT CmBattPowerCallBackObject
;
17 PVOID CmBattPowerCallBackRegistration
;
18 UNICODE_STRING GlobalRegistryPath
;
19 KTIMER CmBattWakeDpcTimerObject
;
20 KDPC CmBattWakeDpcObject
;
22 /* FUNCTIONS ******************************************************************/
26 CmBattPowerCallBack(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
35 CmBattWakeDpc(PKDPC Dpc
,
36 PCMBATT_DEVICE_EXTENSION FdoExtension
,
37 PVOID SystemArgument1
,
38 PVOID SystemArgument2
)
45 CmBattNotifyHandler(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
53 CmBattUnload(IN PDRIVER_OBJECT DriverObject
)
55 if (CmBattDebug
& CMBATT_GENERIC_INFO
) DPRINT("CmBattUnload: \n");
57 /* Check if we have a registered power callback */
58 if (CmBattPowerCallBackObject
)
61 ExUnregisterCallback(CmBattPowerCallBackRegistration
);
62 ObfDereferenceObject(CmBattPowerCallBackObject
);
65 /* Free the registry buffer if it exists */
66 if (GlobalRegistryPath
.Buffer
) ExFreePool(GlobalRegistryPath
.Buffer
);
68 /* Make sure we don't still have references to the DO */
69 if ((DriverObject
->DeviceObject
) && (CmBattDebug
& CMBATT_GENERIC_WARNING
))
71 DbgPrint("Unload called before all devices removed.\n");
77 CmBattVerifyStaticInfo(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
81 return STATUS_NOT_IMPLEMENTED
;
86 CmBattOpenClose(IN PDEVICE_OBJECT DeviceObject
,
89 NTSTATUS Status
= STATUS_SUCCESS
;
90 PIO_STACK_LOCATION IoStackLocation
;
93 PCMBATT_DEVICE_EXTENSION DeviceExtension
;
95 if (CmBattDebug
& CMBATT_GENERIC_INFO
) DPRINT("CmBattOpenClose\n");
97 /* Grab the device extension and lock it */
98 DeviceExtension
= DeviceObject
->DeviceExtension
;
99 ExAcquireFastMutex(&DeviceExtension
->FastMutex
);
101 /* Check if someone is trying to open a device that doesn't exist yet */
102 Count
= DeviceExtension
->HandleCount
;
103 if (Count
== 0xFFFFFFFF)
105 /* Fail the request */
106 Status
= STATUS_NO_SUCH_DEVICE
;
107 if (CmBattDebug
& CMBATT_PNP_INFO
)
109 DbgPrint("CmBattOpenClose: Failed (UID = %x)(device being removed).\n",
110 DeviceExtension
->Tag
);
115 /* Check if this is an open or close */
116 IoStackLocation
= IoGetCurrentIrpStackLocation(Irp
);
117 Major
= IoStackLocation
->MajorFunction
;
118 if (Major
== IRP_MJ_CREATE
)
120 /* Increment the open count */
121 DeviceExtension
->HandleCount
= Count
+ 1;
122 if (CmBattDebug
& CMBATT_PNP_INFO
)
124 DbgPrint("CmBattOpenClose: Open (DeviceNumber = %x)(count = %x).\n",
125 DeviceExtension
->DeviceId
, Count
+ 1);
128 else if (Major
== IRP_MJ_CLOSE
)
130 /* Decrement the open count */
131 DeviceExtension
->HandleCount
= Count
- 1;
132 if (CmBattDebug
& CMBATT_PNP_INFO
)
134 DbgPrint("CmBattOpenClose: Close (DeviceNumber = %x)(count = %x).\n",
135 DeviceExtension
->DeviceId
, Count
+ 1);
140 /* Release lock and complete request */
141 ExReleaseFastMutex(&DeviceExtension
->FastMutex
);
142 Irp
->IoStatus
.Status
= Status
;
143 IofCompleteRequest(Irp
, IO_NO_INCREMENT
);
149 CmBattIoctl(PDEVICE_OBJECT DeviceObject
,
153 return STATUS_NOT_IMPLEMENTED
;
158 CmBattQueryTag(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
162 return STATUS_NOT_IMPLEMENTED
;
167 CmBattDisableStatusNotify(PCMBATT_DEVICE_EXTENSION DeviceExtension
)
170 return STATUS_NOT_IMPLEMENTED
;
175 CmBattSetStatusNotify(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
177 PBATTERY_NOTIFY BatteryNotify
)
180 return STATUS_NOT_IMPLEMENTED
;
185 CmBattGetBatteryStatus(PCMBATT_DEVICE_EXTENSION DeviceExtension
,
189 return STATUS_NOT_IMPLEMENTED
;
194 CmBattQueryInformation(IN PCMBATT_DEVICE_EXTENSION FdoExtension
,
196 IN BATTERY_QUERY_INFORMATION_LEVEL InfoLevel
,
197 IN OPTIONAL LONG AtRate
,
199 IN ULONG BufferLength
,
200 OUT PULONG ReturnedLength
)
203 PVOID QueryData
= NULL
;
204 ULONG QueryLength
= 0;
205 ULONG RemainingTime
= 0;
206 ANSI_STRING TempString
;
207 UNICODE_STRING TempString2
;
208 WCHAR InfoBuffer
[256];
209 WCHAR TempBuffer
[256];
210 UNICODE_STRING InfoString
;
211 ULONG RemainingCapacity
;
212 BATTERY_REPORTING_SCALE BatteryReportingScale
[2];
215 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
216 DbgPrint("CmBattQueryInformation - Tag (%d) Device %d, Informationlevel %d\n",
218 FdoExtension
->DeviceId
,
221 /* Check ACPI Data */
222 Status
= CmBattVerifyStaticInfo(FdoExtension
, Tag
);
223 if (!NT_SUCCESS(Status
)) return Status
;
225 /* Check what caller wants */
228 case BatteryInformation
:
229 /* Just return our static information */
230 QueryData
= &FdoExtension
->BatteryInformation
;
231 QueryLength
= sizeof(BATTERY_INFORMATION
);
234 case BatteryGranularityInformation
:
236 /* Return our static information, we have two scales */
237 BatteryReportingScale
[0].Granularity
= FdoExtension
->BatteryCapacityGranularity1
;
238 BatteryReportingScale
[0].Capacity
= FdoExtension
->BatteryInformation
.DefaultAlert1
;
239 BatteryReportingScale
[1].Granularity
= FdoExtension
->BatteryCapacityGranularity2
;
240 BatteryReportingScale
[1].Capacity
= FdoExtension
->BatteryInformation
.DesignedCapacity
;
241 QueryData
= BatteryReportingScale
;
242 QueryLength
= sizeof(BATTERY_REPORTING_SCALE
) * 2;
245 case BatteryEstimatedTime
:
247 /* Check if it's been more than 2 1/2 minutes since the last change */
248 if ((KeQueryInterruptTime() - 150000000) > (FdoExtension
->InterruptTime
))
250 /* Get new battery status */
251 CmBattGetBatteryStatus(FdoExtension
, FdoExtension
->Tag
);
253 /* If the caller didn't specify a rate, use our static one */
255 if (!Rate
) Rate
= FdoExtension
->Rate
;
257 /* If we don't have a valid negative rate, use unknown value */
258 if (Rate
>= 0) Rate
= BATTERY_UNKNOWN_RATE
;
260 /* Grab the remaining capacity */
261 RemainingCapacity
= FdoExtension
->RemainingCapacity
;
263 /* See if we don't know one or the other */
264 if ((Rate
== BATTERY_UNKNOWN_RATE
) ||
265 (RemainingCapacity
== BATTERY_UNKNOWN_CAPACITY
))
267 /* If the battery is discharging, we can't give out a time */
268 if ((FdoExtension
->BstData
.State
& ACPI_BATT_STAT_DISCHARG
) &&
269 (CmBattDebug
& CMBATT_GENERIC_WARNING
))
270 DbgPrint("CmBattQueryInformation: Can't calculate EstimatedTime.\n");
272 /* Check if we don't have a rate and capacity is going down */
273 if ((FdoExtension
->Rate
== BATTERY_UNKNOWN_RATE
) &&
274 (FdoExtension
->BstData
.State
& ACPI_BATT_STAT_DISCHARG
))
276 /* We have to fail, since we lack data */
277 Status
= STATUS_INVALID_DEVICE_REQUEST
;
278 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
279 DbgPrint("---------------------- PresentRate = BATTERY_UNKNOWN_RATE\n");
282 /* If we don't have capacity, the rate is useless */
283 if (RemainingCapacity
== BATTERY_UNKNOWN_CAPACITY
)
285 /* We have to fail the request */
286 Status
= STATUS_INVALID_DEVICE_REQUEST
;
287 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
288 DbgPrint("---------------------- RemainingCapacity = BATTERY_UNKNOWN_CAPACITY\n");
293 /* We have data, but is it valid? */
294 if (RemainingCapacity
> 0x123456)
296 /* The capacity seems bogus, so don't use it */
297 if (CmBattDebug
& CMBATT_ACPI_WARNING
)
298 DbgPrint("CmBattQueryInformation: Data Overflow in calculating Remaining Capacity.\n");
302 /* Compute the remaining time in seconds, based on rate */
303 RemainingTime
= (RemainingCapacity
* 3600) / -Rate
;
308 /* Return the remaining time */
309 QueryData
= &RemainingTime
;
310 QueryLength
= sizeof(ULONG
);
313 case BatteryDeviceName
:
315 /* Build the model number string */
316 RtlInitAnsiString(&TempString
, FdoExtension
->ModelNumber
);
318 /* Convert it to Unicode */
319 InfoString
.Buffer
= InfoBuffer
;
320 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
321 Status
= RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
323 /* Return the unicode buffer */
324 QueryData
= InfoString
.Buffer
;
325 QueryLength
= InfoString
.Length
;
328 case BatteryTemperature
:
329 case BatteryManufactureDate
:
331 /* We don't support these */
332 Status
= STATUS_INVALID_DEVICE_REQUEST
;
335 case BatteryManufactureName
:
337 /* Build the OEM info string */
338 RtlInitAnsiString(&TempString
, FdoExtension
->OemInfo
);
340 /* Convert it to Unicode */
341 InfoString
.Buffer
= InfoBuffer
;
342 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
343 Status
= RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
345 /* Return the unicode buffer */
346 QueryData
= InfoString
.Buffer
;
347 QueryLength
= InfoString
.Length
;
350 case BatteryUniqueID
:
352 /* Build the serial number string */
353 RtlInitAnsiString(&TempString
, FdoExtension
->SerialNumber
);
355 /* Convert it to Unicode */
356 InfoString
.Buffer
= InfoBuffer
;
357 InfoString
.MaximumLength
= sizeof(InfoBuffer
);
358 RtlAnsiStringToUnicodeString(&InfoString
, &TempString
, 0);
360 /* Setup a temporary string for concatenation */
361 TempString2
.Buffer
= TempBuffer
;
362 TempString2
.MaximumLength
= sizeof(TempBuffer
);
364 /* Check if there's an OEM string */
365 if (FdoExtension
->OemInfo
[0])
367 /* Build the OEM info string */
368 RtlInitAnsiString(&TempString
, FdoExtension
->OemInfo
);
370 /* Convert it to Unicode and append it */
371 RtlAnsiStringToUnicodeString(&TempString2
, &TempString
, 0);
372 RtlAppendUnicodeStringToString(&InfoString
, &TempString2
);
375 /* Build the model number string */
376 RtlInitAnsiString(&TempString
, FdoExtension
->ModelNumber
);
378 /* Convert it to Unicode and append it */
379 RtlAnsiStringToUnicodeString(&TempString2
, &TempString
, 0);
380 RtlAppendUnicodeStringToString(&InfoString
, &TempString2
);
382 /* Return the final appended string */
383 QueryData
= InfoString
.Buffer
;
384 QueryLength
= InfoString
.Length
;
389 /* Everything else is unknown */
390 Status
= STATUS_INVALID_PARAMETER
;
394 /* Return the required length and check if the caller supplied enough */
395 *ReturnedLength
= QueryLength
;
396 if (BufferLength
< QueryLength
) Status
= STATUS_BUFFER_TOO_SMALL
;
398 /* Copy the data if there's enough space and it exists */
399 if ((NT_SUCCESS(Status
)) && (QueryData
)) RtlCopyMemory(Buffer
, QueryData
, QueryLength
);
401 /* Return function result */
407 CmBattQueryStatus(IN PCMBATT_DEVICE_EXTENSION DeviceExtension
,
409 IN PBATTERY_STATUS BatteryStatus
)
413 if (CmBattDebug
& (CMBATT_ACPI_WARNING
| CMBATT_GENERIC_INFO
))
414 DbgPrint("CmBattQueryStatus - Tag (%d) Device %x\n", Tag
, DeviceExtension
->DeviceId
);
416 /* Query ACPI information */
417 Status
= CmBattGetBatteryStatus(DeviceExtension
, Tag
);
418 if (NT_SUCCESS(Status
))
420 BatteryStatus
->PowerState
= DeviceExtension
->State
;
421 BatteryStatus
->Capacity
= DeviceExtension
->RemainingCapacity
;
422 BatteryStatus
->Voltage
= DeviceExtension
->PresentVoltage
;
423 BatteryStatus
->Rate
= DeviceExtension
->Rate
;
427 if (CmBattDebug
& (CMBATT_GENERIC_INFO
))
428 DbgPrint("CmBattQueryStatus: Returning [%#08lx][%#08lx][%#08lx][%#08lx]\n",
429 BatteryStatus
->PowerState
,
430 BatteryStatus
->Capacity
,
431 BatteryStatus
->Voltage
,
432 BatteryStatus
->Rate
);
438 DriverEntry(IN PDRIVER_OBJECT DriverObject
,
439 IN PUNICODE_STRING RegistryPath
)
442 PDRIVER_EXTENSION DriverExtension
;
443 OBJECT_ATTRIBUTES ObjectAttributes
;
444 UNICODE_STRING CallbackName
;
446 /* Allocate registry path */
447 GlobalRegistryPath
.MaximumLength
= RegistryPath
->Length
+ sizeof(UNICODE_NULL
);
448 GlobalRegistryPath
.Length
= RegistryPath
->Length
;
449 GlobalRegistryPath
.Buffer
= ExAllocatePoolWithTag(PagedPool
,
450 GlobalRegistryPath
.MaximumLength
,
452 if (!GlobalRegistryPath
.Buffer
)
454 /* Fail if we're out of memory this early */
455 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
456 DbgPrint("CmBatt: Couldn't allocate pool for registry path.");
457 return STATUS_INSUFFICIENT_RESOURCES
;
460 /* Buffer allocated, copy the string */
461 RtlCopyUnicodeString(&GlobalRegistryPath
, RegistryPath
);
462 if (CmBattDebug
& CMBATT_GENERIC_INFO
)
463 DbgPrint("CmBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
465 RegistryPath
->Buffer
);
467 /* Setup the major dispatchers */
468 DriverObject
->MajorFunction
[0] = CmBattOpenClose
;
469 DriverObject
->MajorFunction
[2] = CmBattOpenClose
;
470 DriverObject
->MajorFunction
[14] = CmBattIoctl
;
471 DriverObject
->MajorFunction
[22] = CmBattPowerDispatch
;
472 DriverObject
->MajorFunction
[27] = CmBattPnpDispatch
;
473 DriverObject
->MajorFunction
[23] = CmBattSystemControl
;
475 /* And the unload routine */
476 DriverObject
->DriverUnload
= CmBattUnload
;
478 /* And the add device routine */
479 DriverExtension
= DriverObject
->DriverExtension
;
480 DriverExtension
->AddDevice
= CmBattAddDevice
;
482 /* Create a power callback */
483 RtlInitUnicodeString(&CallbackName
, L
"\\Callback\\PowerState");
484 InitializeObjectAttributes(&ObjectAttributes
,
489 Status
= ExCreateCallback(&CmBattPowerCallBackObject
, &ObjectAttributes
, 0, TRUE
);
490 if (!NT_SUCCESS(Status
))
492 /* No callback, fail */
493 CmBattPowerCallBackObject
= 0;
494 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
495 DbgPrint("CmBattRegisterPowerCallBack: failed status=0x%08x\n", Status
);
499 /* Register the power callback now */
500 CmBattPowerCallBackRegistration
= ExRegisterCallback(CmBattPowerCallBackObject
,
501 (PVOID
)CmBattPowerCallBack
,
503 if (CmBattPowerCallBackRegistration
)
505 /* Last thing: setup our DPC and timer for battery wake */
506 KeInitializeDpc(&CmBattWakeDpcObject
, (PVOID
)CmBattWakeDpc
, DriverObject
);
507 KeInitializeTimer(&CmBattWakeDpcTimerObject
);
511 ObfDereferenceObject(CmBattPowerCallBackObject
);
512 if (CmBattDebug
& CMBATT_GENERIC_WARNING
)
513 DbgPrint("CmBattRegisterPowerCallBack: ExRegisterCallback failed.\n");
517 Status
= STATUS_SUCCESS
;
520 /* Return failure or success */