570 lines
13 KiB
C
570 lines
13 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1990 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
ApmBatt.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Control Method Battery Miniport Driver - Wacked to work on APM.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Bryan Willman
|
|||
|
Ron Mosgrove (Intel)
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ApmBattp.h"
|
|||
|
#include "ntddk.h"
|
|||
|
#include "ntapm.h"
|
|||
|
|
|||
|
|
|||
|
ULONG ApmBattDebug = APMBATT_ERROR;
|
|||
|
//ULONG ApmBattDebug = -1;
|
|||
|
|
|||
|
//
|
|||
|
// Prototypes
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattOpenClose(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattIoctl(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Globals. Globals are a little odd in a device driver,
|
|||
|
// but this is an odd driver
|
|||
|
//
|
|||
|
|
|||
|
//
|
|||
|
// Vector used to call NtApm.sys (our PDO) and ask about
|
|||
|
// current battery status
|
|||
|
//
|
|||
|
ULONG (*NtApmGetBatteryLevel)() = NULL;
|
|||
|
|
|||
|
//
|
|||
|
// APM event notifications and SET_POWER ops will cause
|
|||
|
// this value to be incremented.
|
|||
|
//
|
|||
|
ULONG TagValue = 1;
|
|||
|
|
|||
|
//
|
|||
|
// If somebody tries to claim there is more than 1 APM driver battery
|
|||
|
// in the system, somebody somewhere is very confused. So keep track
|
|||
|
// and forbig this.
|
|||
|
//
|
|||
|
ULONG DeviceCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
//
|
|||
|
//
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT,DriverEntry)
|
|||
|
#pragma alloc_text(PAGE,ApmBattQueryTag)
|
|||
|
#pragma alloc_text(PAGE,ApmBattQueryInformation)
|
|||
|
#pragma alloc_text(PAGE,ApmBattQueryStatus)
|
|||
|
#pragma alloc_text(PAGE,ApmBattSetStatusNotify)
|
|||
|
#pragma alloc_text(PAGE,ApmBattDisableStatusNotify)
|
|||
|
#pragma alloc_text(PAGE,ApmBattOpenClose)
|
|||
|
#pragma alloc_text(PAGE,ApmBattIoctl)
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine initializes the ACPI Embedded Controller Driver
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Pointer to driver object created by system.
|
|||
|
|
|||
|
RegistryPath - Pointer to the Unicode name of the registry path
|
|||
|
for this driver.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The function value is the final status from the initialization operation.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ApmBattPrint (APMBATT_TRACE, ("ApmBatt DriverEntry - Obj (%08x) Path (%08x)\n",
|
|||
|
DriverObject, RegistryPath));
|
|||
|
//
|
|||
|
// Set up the device driver entry points.
|
|||
|
//
|
|||
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ApmBattIoctl;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = ApmBattOpenClose;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ApmBattOpenClose;
|
|||
|
|
|||
|
DriverObject->MajorFunction[IRP_MJ_POWER] = ApmBattPowerDispatch;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_PNP] = ApmBattPnpDispatch;
|
|||
|
DriverObject->DriverExtension->AddDevice = ApmBattAddDevice;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattOpenClose(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This is the routine called as a result of a Open or Close on the device
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
DeviceObject - Battery for request
|
|||
|
Irp - IO request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - no way to fail this puppy
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ApmBattPrint (APMBATT_TRACE, ("ApmBattOpenClose\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Complete the request and return status.
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
return(STATUS_SUCCESS);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattIoctl(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
IOCTL handler. As this is an exclusive battery device, send the
|
|||
|
Irp to the battery class driver to handle battery IOCTLs.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Battery for request
|
|||
|
Irp - IO request
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status of request
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PCM_BATT ApmBatt;
|
|||
|
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ApmBattPrint (APMBATT_TRACE, ("ApmBattIoctl\n"));
|
|||
|
|
|||
|
ApmBatt = (PCM_BATT) DeviceObject->DeviceExtension;
|
|||
|
Status = BatteryClassIoctl (ApmBatt->Class, Irp);
|
|||
|
|
|||
|
if (Status == STATUS_NOT_SUPPORTED) {
|
|||
|
//
|
|||
|
// Not for the battery, complete it
|
|||
|
//
|
|||
|
|
|||
|
Irp->IoStatus.Status = Status;
|
|||
|
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattQueryTag (
|
|||
|
IN PVOID Context,
|
|||
|
OUT PULONG TagPtr
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
BATTERY CLASS ENTRY
|
|||
|
|
|||
|
Called by the class driver to retrieve the batteries current tag value
|
|||
|
|
|||
|
The battery class driver will serialize all requests it issues to
|
|||
|
the miniport for a given battery.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Miniport context value for battery
|
|||
|
TagPtr - Pointer to return current tag
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Success if there is a battery currently installed, else no such device.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG BatteryLevel;
|
|||
|
UNREFERENCED_PARAMETER(Context);
|
|||
|
PAGED_CODE();
|
|||
|
ApmBattPrint ((APMBATT_TRACE | APMBATT_MINI),
|
|||
|
("ApmBattQueryTag - TagValue = %08x\n", TagValue));
|
|||
|
//
|
|||
|
// The code that catches APM event notification, and the code
|
|||
|
// that handles Power IRPs, will both increment the tag.
|
|||
|
// We simply report that.
|
|||
|
//
|
|||
|
|
|||
|
*TagPtr = TagValue;
|
|||
|
|
|||
|
//
|
|||
|
// Call ntapm, it will return a DWORD with the relevent data in it,
|
|||
|
// crack this DWORD, and fill this stuff in.
|
|||
|
//
|
|||
|
if (NtApmGetBatteryLevel) {
|
|||
|
BatteryLevel = NtApmGetBatteryLevel();
|
|||
|
if ((BatteryLevel & NTAPM_NO_BATT) || (BatteryLevel & NTAPM_NO_SYS_BATT)) {
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
} else {
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//
|
|||
|
// if we cannot get battery status, it's likely we don't have
|
|||
|
// a battery, so say we don't have one.
|
|||
|
//
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattQueryInformation (
|
|||
|
IN PVOID Context,
|
|||
|
IN ULONG BatteryTag,
|
|||
|
IN BATTERY_QUERY_INFORMATION_LEVEL Level,
|
|||
|
IN ULONG AtRate OPTIONAL,
|
|||
|
OUT PVOID Buffer,
|
|||
|
IN ULONG BufferLength,
|
|||
|
OUT PULONG ReturnedLength
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
BATTERY CLASS ENTRY
|
|||
|
|
|||
|
Called by the class driver to retrieve battery information
|
|||
|
|
|||
|
The battery class driver will serialize all requests it issues to
|
|||
|
the miniport for a given battery.
|
|||
|
|
|||
|
We return invalid parameter when we can't handle a request for a
|
|||
|
specific level of information. This is defined in the battery class spec.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Miniport context value for battery
|
|||
|
BatteryTag - Tag of current battery
|
|||
|
Level - type of information required
|
|||
|
AtRate - Used only when Level==BatteryEstimatedTime
|
|||
|
Buffer - Location for the information
|
|||
|
BufferLength - Length in bytes of the buffer
|
|||
|
ReturnedLength - Length in bytes of the returned data
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Success if there is a battery currently installed, else no such device.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
NTSTATUS Status;
|
|||
|
PVOID ReturnBuffer;
|
|||
|
ULONG ReturnBufferLength;
|
|||
|
ULONG CapabilityVector = (BATTERY_SYSTEM_BATTERY | BATTERY_CAPACITY_RELATIVE);
|
|||
|
BATTERY_INFORMATION bi;
|
|||
|
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ApmBattPrint ((APMBATT_TRACE | APMBATT_MINI),
|
|||
|
("ApmBattQueryInformation Level=%08xl\n", Level));
|
|||
|
|
|||
|
//
|
|||
|
// We cannot tell (reliably/safely) if there is a battery
|
|||
|
// present or not, so always return what the query code tells us
|
|||
|
//
|
|||
|
|
|||
|
ReturnBuffer = NULL;
|
|||
|
ReturnBufferLength = 0;
|
|||
|
Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Get the info requested
|
|||
|
//
|
|||
|
|
|||
|
switch (Level) {
|
|||
|
case BatteryInformation:
|
|||
|
ApmBattPrint((APMBATT_TRACE|APMBATT_MINI), ("Batteryinformation\n"));
|
|||
|
RtlZeroMemory(&bi, sizeof(bi));
|
|||
|
bi.Capabilities = CapabilityVector;
|
|||
|
bi.Technology = BATTERY_SECONDARY_CHARGABLE;
|
|||
|
bi.DesignedCapacity = 100;
|
|||
|
bi.FullChargedCapacity = UNKNOWN_CAPACITY;
|
|||
|
ReturnBuffer = (PVOID) &bi;
|
|||
|
ReturnBufferLength = sizeof(bi);
|
|||
|
break;
|
|||
|
|
|||
|
case BatteryEstimatedTime:
|
|||
|
case BatteryTemperature:
|
|||
|
case BatteryGranularityInformation:
|
|||
|
case BatteryDeviceName:
|
|||
|
case BatteryManufactureDate:
|
|||
|
case BatteryManufactureName:
|
|||
|
case BatteryUniqueID:
|
|||
|
Status = STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
Status = STATUS_INVALID_PARAMETER;
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Done, return buffer if needed
|
|||
|
//
|
|||
|
*ReturnedLength = ReturnBufferLength;
|
|||
|
if (BufferLength < ReturnBufferLength) {
|
|||
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(Status) && ReturnBuffer) {
|
|||
|
RtlZeroMemory (Buffer, BufferLength); // Clear entire user buffer
|
|||
|
RtlCopyMemory (Buffer, ReturnBuffer, ReturnBufferLength); // Copy what's needed
|
|||
|
}
|
|||
|
return Status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattQueryStatus (
|
|||
|
IN PVOID Context,
|
|||
|
IN ULONG BatteryTag,
|
|||
|
OUT PBATTERY_STATUS BatteryStatus
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
BATTERY CLASS ENTRY
|
|||
|
|
|||
|
Called by the class driver to retrieve the batteries current status
|
|||
|
|
|||
|
The battery class driver will serialize all requests it issues to
|
|||
|
the miniport for a given battery.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Miniport context value for battery
|
|||
|
BatteryTag - Tag of current battery
|
|||
|
BatteryStatus - Pointer to structure to return the current battery status
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Success if there is a battery currently installed, else no such device.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG BatteryLevel;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
ApmBattPrint ((APMBATT_TRACE | APMBATT_MINI), ("ApmBattQueryStatus\n"));
|
|||
|
|
|||
|
//
|
|||
|
// Call ntapm, it will return a DWORD with the relevent data in it,
|
|||
|
// crack this DWORD, and fill this stuff in.
|
|||
|
//
|
|||
|
if (NtApmGetBatteryLevel) {
|
|||
|
BatteryLevel = NtApmGetBatteryLevel();
|
|||
|
BatteryStatus->PowerState = ((BatteryLevel & NTAPM_BATTERY_STATE) >> NTAPM_BATTERY_STATE_SHIFT);
|
|||
|
BatteryStatus->Capacity = BatteryLevel & NTAPM_POWER_PERCENT;
|
|||
|
BatteryStatus->Voltage = UNKNOWN_VOLTAGE;
|
|||
|
BatteryStatus->Current = UNKNOWN_RATE;
|
|||
|
|
|||
|
ApmBattPrint((APMBATT_MINI), ("ApmBattQueryStatus: BatteryLevel = %08lx\n", BatteryLevel));
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
ApmBattPrint((APMBATT_ERROR), ("ApmBattQueryStatus: failure NtApmGetBatteryLevel == NULL\n"));
|
|||
|
//
|
|||
|
// return some "safe" values to keep from looping forever
|
|||
|
//
|
|||
|
BatteryStatus->PowerState = 0;
|
|||
|
BatteryStatus->Capacity = 1;
|
|||
|
BatteryStatus->Voltage = UNKNOWN_VOLTAGE;
|
|||
|
BatteryStatus->Current = UNKNOWN_RATE;
|
|||
|
return STATUS_UNSUCCESSFUL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattSetStatusNotify (
|
|||
|
IN PVOID Context,
|
|||
|
IN ULONG BatteryTag,
|
|||
|
IN PBATTERY_NOTIFY Notify
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
BATTERY CLASS ENTRY
|
|||
|
|
|||
|
Called by the class driver to set the batteries current notification
|
|||
|
setting. When the battery trips the notification, one call to
|
|||
|
BatteryClassStatusNotify is issued. If an error is returned, the
|
|||
|
class driver will poll the battery status - primarily for capacity
|
|||
|
changes. Which is to say the miniport should still issue BatteryClass-
|
|||
|
StatusNotify whenever the power state changes.
|
|||
|
|
|||
|
The class driver will always set the notification level it needs
|
|||
|
after each call to BatteryClassStatusNotify.
|
|||
|
|
|||
|
The battery class driver will serialize all requests it issues to
|
|||
|
the miniport for a given battery.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Miniport context value for battery
|
|||
|
BatteryTag - Tag of current battery
|
|||
|
BatteryNotify - The notification setting
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// need to fill this in
|
|||
|
//
|
|||
|
ApmBattPrint (APMBATT_TRACE, ("ApmBattSetStatusNotify\n"));
|
|||
|
return STATUS_NOT_IMPLEMENTED;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ApmBattDisableStatusNotify (
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
BATTERY CLASS ENTRY
|
|||
|
|
|||
|
Called by the class driver to disable the notification setting
|
|||
|
for the battery supplied by Context. Note, to disable a setting
|
|||
|
does not require the battery tag. Any notification is to be
|
|||
|
masked off until a subsequent call to ApmBattSetStatusNotify.
|
|||
|
|
|||
|
The battery class driver will serialize all requests it issues to
|
|||
|
the miniport for a given battery.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Miniport context value for battery
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
Status
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// need to fill this in
|
|||
|
//
|
|||
|
ApmBattPrint (APMBATT_TRACE, ("ApmBattDisableStatusNotify\n"));
|
|||
|
return STATUS_NOT_IMPLEMENTED;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ApmBattPowerNotifyHandler (
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
NTAPM CALLBACK
|
|||
|
|
|||
|
This routine fields power device notifications from the APM driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ApmBattPrint (APMBATT_TRACE, ("ApmBattPowerNotifyHandler\n"));
|
|||
|
// DbgBreakPoint();
|
|||
|
TagValue++;
|
|||
|
BatteryClassStatusNotify(ApmGlobalClass);
|
|||
|
}
|
|||
|
|