3015 lines
88 KiB
C
3015 lines
88 KiB
C
/*++
|
||
|
||
Copyright (c) 1990 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
smbbatt.c
|
||
|
||
Abstract:
|
||
|
||
SMBus Smart Battery Subsystem Miniport Driver
|
||
(Selector, Battery, Charger)
|
||
|
||
Author:
|
||
|
||
Ken Reneris
|
||
|
||
Environment:
|
||
|
||
Notes:
|
||
|
||
|
||
Revision History:
|
||
|
||
Chris Windle 1/27/98 Bug Fixes
|
||
|
||
--*/
|
||
|
||
#include "smbbattp.h"
|
||
|
||
#include <initguid.h>
|
||
#include <batclass.h>
|
||
|
||
|
||
|
||
#if DEBUG
|
||
ULONG SMBBattDebug = BAT_WARN | BAT_ERROR | BAT_BIOS_ERROR;
|
||
#endif
|
||
|
||
// Global
|
||
BOOLEAN SmbBattUseGlobalLock = TRUE;
|
||
UNICODE_STRING GlobalRegistryPath;
|
||
|
||
//
|
||
// Prototypes
|
||
//
|
||
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattNewDevice (
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT PDO
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattQueryTag (
|
||
IN PVOID Context,
|
||
OUT PULONG BatteryTag
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattQueryInformation (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
IN BATTERY_QUERY_INFORMATION_LEVEL Level,
|
||
IN LONG AtRate OPTIONAL,
|
||
OUT PVOID Buffer,
|
||
IN ULONG BufferLength,
|
||
OUT PULONG ReturnedLength
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattSetStatusNotify (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
IN PBATTERY_NOTIFY BatteryNotify
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattDisableStatusNotify (
|
||
IN PVOID Context
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattQueryStatus (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
OUT PBATTERY_STATUS BatteryStatus
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattCreate(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattIoctl(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
);
|
||
|
||
VOID
|
||
SmbBattUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
);
|
||
|
||
VOID
|
||
SmbBattProcessSelectorAlarm (
|
||
IN PSMB_BATT_SUBSYSTEM SubsystemExt,
|
||
IN ULONG OldSelectorState,
|
||
IN ULONG NewSelectorState
|
||
);
|
||
|
||
NTSTATUS
|
||
SmbBattGetPowerState (
|
||
IN PSMB_BATT SmbBatt,
|
||
OUT PULONG PowerState,
|
||
OUT PLONG Current
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
#pragma alloc_text(INIT,DriverEntry)
|
||
#pragma alloc_text(PAGE,SmbBattNewDevice)
|
||
#pragma alloc_text(PAGE,SmbBattUnload)
|
||
#pragma alloc_text(PAGE,SmbBattCreate)
|
||
#pragma alloc_text(PAGE,SmbBattClose)
|
||
#pragma alloc_text(PAGE,SmbBattIoctl)
|
||
#pragma alloc_text(PAGE,SmbBattQueryTag)
|
||
#pragma alloc_text(PAGE,SmbBattQueryInformation)
|
||
#pragma alloc_text(PAGE,SmbBattSetInformation)
|
||
#pragma alloc_text(PAGE,SmbBattGetPowerState)
|
||
#pragma alloc_text(PAGE,SmbBattQueryStatus)
|
||
#pragma alloc_text(PAGE,SmbBattSetStatusNotify)
|
||
#pragma alloc_text(PAGE,SmbBattDisableStatusNotify)
|
||
#endif
|
||
|
||
|
||
|
||
NTSTATUS
|
||
DriverEntry(
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PUNICODE_STRING RegistryPath
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine initializes the Smart Battery 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.
|
||
|
||
--*/
|
||
{
|
||
OBJECT_ATTRIBUTES objAttributes;
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBatt: DriverEntry\n"));
|
||
|
||
//
|
||
// Save the RegistryPath.
|
||
//
|
||
|
||
GlobalRegistryPath.MaximumLength = RegistryPath->Length +
|
||
sizeof(UNICODE_NULL);
|
||
GlobalRegistryPath.Length = RegistryPath->Length;
|
||
GlobalRegistryPath.Buffer = ExAllocatePoolWithTag (
|
||
PagedPool,
|
||
GlobalRegistryPath.MaximumLength,
|
||
'StaB');
|
||
|
||
if (!GlobalRegistryPath.Buffer) {
|
||
|
||
BattPrint ((BAT_ERROR),("SmbBatt: Couldn't allocate pool for registry path."));
|
||
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlCopyUnicodeString(&GlobalRegistryPath, RegistryPath);
|
||
|
||
BattPrint (BAT_TRACE, ("SmbBatt DriverEntry - Obj (%08x) Path \"%ws\"\n",
|
||
DriverObject, RegistryPath->Buffer));
|
||
|
||
|
||
DriverObject->DriverUnload = SmbBattUnload;
|
||
DriverObject->DriverExtension->AddDevice = SmbBattNewDevice;
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = SmbBattIoctl;
|
||
DriverObject->MajorFunction[IRP_MJ_CREATE] = SmbBattCreate;
|
||
DriverObject->MajorFunction[IRP_MJ_CLOSE] = SmbBattClose;
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_PNP] = SmbBattPnpDispatch;
|
||
DriverObject->MajorFunction[IRP_MJ_POWER] = SmbBattPowerDispatch;
|
||
|
||
DriverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = SmbBattSystemControl;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattNewDevice (
|
||
IN PDRIVER_OBJECT DriverObject,
|
||
IN PDEVICE_OBJECT PDO
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This creates a smb smart battery functional device objects. The first
|
||
object created will be the one for the "smart battery subsystem" which will
|
||
have a PDO from ACPI. This will receive a START Irp, then a
|
||
QUERY_DEVICE_RELATIONS Irp. In the QUERY it will create PDOs for the
|
||
batteries that are supported by the system and eventually they will end
|
||
up here for FDOs to be created and attached to them.
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Pointer to driver object created by system.
|
||
|
||
PDO - PDO for the new device(s)
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_OBJECT fdo;
|
||
PSMB_BATT_SUBSYSTEM subsystemExt;
|
||
PSMB_BATT_PDO pdoExt;
|
||
|
||
PSMB_NP_BATT SmbNPBatt;
|
||
PSMB_BATT SmbBatt;
|
||
BATTERY_MINIPORT_INFO BattInit;
|
||
|
||
NTSTATUS status = STATUS_UNSUCCESSFUL;
|
||
BOOLEAN selectorPresent = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_IRPS, ("SmbBattNewDevice: AddDevice for device %x\n", PDO));
|
||
|
||
//
|
||
// Check to see if we are being asked to enumerate ourself
|
||
//
|
||
|
||
if (PDO == NULL) {
|
||
BattPrint(BAT_ERROR, ("SmbBattNewDevice: Being asked to enumerate\n"));
|
||
return STATUS_NOT_IMPLEMENTED;
|
||
}
|
||
|
||
|
||
//
|
||
// Check to see if the PDO is the battery subsystem PDO or a battery PDO. This will be
|
||
// determined by the PDO's DeviceType.
|
||
//
|
||
// FILE_DEVICE_ACPI This PDO is from ACPI and belongs to battery subsystem
|
||
// FILE_DEVICE_BATTERY This PDO is a battery PDO
|
||
//
|
||
|
||
if (PDO->DeviceType == FILE_DEVICE_ACPI) {
|
||
|
||
//
|
||
// Create the device object
|
||
//
|
||
|
||
status = IoCreateDevice(
|
||
DriverObject,
|
||
sizeof (SMB_BATT_SUBSYSTEM),
|
||
NULL,
|
||
FILE_DEVICE_BATTERY,
|
||
0,
|
||
FALSE,
|
||
&fdo
|
||
);
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
BattPrint(BAT_ERROR, ("SmbBattNewDevice: error creating Fdo for battery subsystem %x\n", status));
|
||
return(status);
|
||
}
|
||
|
||
|
||
//
|
||
// Initialize the Fdo
|
||
//
|
||
|
||
fdo->Flags |= DO_BUFFERED_IO;
|
||
fdo->Flags |= DO_POWER_PAGABLE;
|
||
|
||
//
|
||
// Initialize the extension
|
||
//
|
||
|
||
subsystemExt = (PSMB_BATT_SUBSYSTEM)fdo->DeviceExtension;
|
||
RtlZeroMemory (subsystemExt, sizeof (PSMB_BATT_SUBSYSTEM));
|
||
|
||
subsystemExt->DeviceObject = fdo;
|
||
subsystemExt->SmbBattFdoType = SmbTypeSubsystem;
|
||
IoInitializeRemoveLock (&subsystemExt->RemoveLock,
|
||
SMB_BATTERY_TAG,
|
||
REMOVE_LOCK_MAX_LOCKED_MINUTES,
|
||
REMOVE_LOCK_HIGH_WATER_MARK);
|
||
|
||
//
|
||
// These fields are implicitly initialize by zeroing the extension
|
||
//
|
||
// subsystemExt->NumberOfBatteries = 0;
|
||
// subsystemExt->SelectorPresent = FALSE;
|
||
// subsystemExt->Selector = NULL;
|
||
// subsystemExt->WorkerActive = 0;
|
||
|
||
|
||
KeInitializeSpinLock (&subsystemExt->AlarmListLock);
|
||
InitializeListHead (&subsystemExt->AlarmList);
|
||
subsystemExt->WorkerThread = IoAllocateWorkItem (fdo);
|
||
|
||
|
||
//
|
||
// Layer our FDO on top of the ACPI PDO.
|
||
//
|
||
|
||
subsystemExt->LowerDevice = IoAttachDeviceToDeviceStack (fdo,PDO);
|
||
|
||
if (!subsystemExt->LowerDevice) {
|
||
BattPrint(BAT_ERROR, ("SmbBattNewDevice: Error attaching subsystem to device stack.\n"));
|
||
|
||
IoDeleteDevice (fdo);
|
||
|
||
return(status);
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// Zero out the battery PDO list
|
||
// This is already zeroed by the RtlZeroMemory above.
|
||
//
|
||
// RtlZeroMemory(
|
||
// &subsystemExt->BatteryPdoList[0],
|
||
// sizeof(PDEVICE_OBJECT) * MAX_SMART_BATTERIES_SUPPORTED
|
||
// );
|
||
|
||
|
||
//
|
||
// Device is ready for use
|
||
//
|
||
|
||
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// This is a battery PDO. Create the FDO to layer on top of it.
|
||
//
|
||
|
||
pdoExt = (PSMB_BATT_PDO) PDO->DeviceExtension;
|
||
subsystemExt = (PSMB_BATT_SUBSYSTEM) pdoExt->SubsystemFdo->DeviceExtension;
|
||
|
||
//
|
||
// Allocate space for the paged portion of the device extension
|
||
//
|
||
|
||
SmbBatt = ExAllocatePoolWithTag (PagedPool, sizeof(SMB_BATT), SMB_BATTERY_TAG);
|
||
if (!SmbBatt) {
|
||
BattPrint(BAT_ERROR, ("SmbBattNewDevice: Can't allocate Smart Battery data\n"));
|
||
return STATUS_INSUFFICIENT_RESOURCES;
|
||
}
|
||
|
||
RtlZeroMemory (SmbBatt, sizeof(SMB_BATT));
|
||
|
||
|
||
//
|
||
// Create the device object
|
||
//
|
||
|
||
status = IoCreateDevice(
|
||
DriverObject,
|
||
sizeof (SMB_NP_BATT),
|
||
NULL,
|
||
FILE_DEVICE_BATTERY,
|
||
0,
|
||
FALSE,
|
||
&fdo
|
||
);
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
BattPrint(BAT_ERROR, ("SmbBattNewDevice: error creating Fdo: %x\n", status));
|
||
|
||
ExFreePool (SmbBatt);
|
||
return(status);
|
||
}
|
||
|
||
|
||
//
|
||
// Initialize the Fdo
|
||
//
|
||
|
||
fdo->Flags |= DO_BUFFERED_IO;
|
||
fdo->Flags |= DO_POWER_PAGABLE;
|
||
|
||
|
||
//
|
||
// Layer our FDO on top of the PDO.
|
||
//
|
||
|
||
SmbNPBatt = (PSMB_NP_BATT) fdo->DeviceExtension;
|
||
SmbNPBatt->LowerDevice = IoAttachDeviceToDeviceStack (fdo,PDO);
|
||
|
||
if (!SmbNPBatt->LowerDevice) {
|
||
BattPrint(BAT_ERROR, ("SmbBattNewDevice: error attaching to device stack\n"));
|
||
|
||
ExFreePool (SmbBatt);
|
||
|
||
IoDeleteDevice (fdo);
|
||
|
||
return(status);
|
||
}
|
||
|
||
|
||
//
|
||
// Fill in privates
|
||
//
|
||
|
||
SmbNPBatt->Batt = SmbBatt;
|
||
SmbNPBatt->SmbBattFdoType = SmbTypeBattery;
|
||
IoInitializeRemoveLock (&SmbNPBatt->RemoveLock,
|
||
SMB_BATTERY_TAG,
|
||
REMOVE_LOCK_MAX_LOCKED_MINUTES,
|
||
REMOVE_LOCK_HIGH_WATER_MARK);
|
||
|
||
ExInitializeFastMutex (&SmbNPBatt->Mutex);
|
||
|
||
SmbBatt->NP = SmbNPBatt;
|
||
SmbBatt->PDO = PDO;
|
||
SmbBatt->DeviceObject = fdo;
|
||
SmbBatt->SelectorPresent = subsystemExt->SelectorPresent;
|
||
SmbBatt->Selector = subsystemExt->Selector;
|
||
|
||
pdoExt->Fdo = fdo;
|
||
|
||
//
|
||
// Precalculate this batteries SMB_x bit position in the selector status register.
|
||
//
|
||
// Just move it into the lower nibble and any function that needs
|
||
// the bit can shift it left 4 = charger, 8 = power, 12 = smb
|
||
//
|
||
|
||
SmbBatt->SelectorBitPosition = 1;
|
||
if (pdoExt->BatteryNumber > 0) {
|
||
SmbBatt->SelectorBitPosition <<= pdoExt->BatteryNumber;
|
||
}
|
||
|
||
|
||
//
|
||
// Have class driver allocate a new SMB miniport device
|
||
//
|
||
|
||
RtlZeroMemory (&BattInit, sizeof(BattInit));
|
||
BattInit.MajorVersion = SMB_BATTERY_MAJOR_VERSION;
|
||
BattInit.MinorVersion = SMB_BATTERY_MINOR_VERSION;
|
||
BattInit.Context = SmbBatt;
|
||
BattInit.QueryTag = SmbBattQueryTag;
|
||
BattInit.QueryInformation = SmbBattQueryInformation;
|
||
BattInit.SetInformation = SmbBattSetInformation;
|
||
BattInit.QueryStatus = SmbBattQueryStatus;
|
||
BattInit.SetStatusNotify = SmbBattSetStatusNotify;
|
||
BattInit.DisableStatusNotify = SmbBattDisableStatusNotify;
|
||
|
||
BattInit.Pdo = PDO;
|
||
BattInit.DeviceName = NULL;
|
||
|
||
status = BatteryClassInitializeDevice (
|
||
&BattInit,
|
||
&SmbNPBatt->Class
|
||
);
|
||
|
||
if (status != STATUS_SUCCESS) {
|
||
BattPrint(BAT_ERROR, ("SmbBattNewDevice: error initializing battery: %x\n", status));
|
||
|
||
ExFreePool (SmbBatt);
|
||
|
||
IoDetachDevice (SmbNPBatt->LowerDevice);
|
||
IoDeleteDevice (fdo);
|
||
|
||
return(status);
|
||
}
|
||
|
||
//
|
||
// Register WMI support.
|
||
//
|
||
status = SmbBattWmiRegistration(SmbNPBatt);
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
//
|
||
// WMI support is not critical to operation. Just log an error.
|
||
//
|
||
|
||
BattPrint(BAT_ERROR,
|
||
("SmbBattNewDevice: Could not register as a WMI provider, status = %Lx\n", status));
|
||
}
|
||
|
||
|
||
//
|
||
// Device is ready for use
|
||
//
|
||
|
||
fdo->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
|
||
|
||
|
||
} // else (we have a battery PDO)
|
||
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SmbBattUnload(
|
||
IN PDRIVER_OBJECT DriverObject
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Cleanup all devices and unload the driver
|
||
|
||
Arguments:
|
||
|
||
DriverObject - Driver object for unload
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattUnload: ENTERING\n"));
|
||
|
||
//
|
||
// Should check here to make sure all DO's are gone.
|
||
//
|
||
|
||
ExFreePool (GlobalRegistryPath.Buffer);
|
||
// This is listed as an error so I'll always see when it is unloaded...
|
||
BattPrint(BAT_ERROR, ("SmbBattUnload: Smbbatt.sys unloaded successfully.\n"));
|
||
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattCreate(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PSMB_NP_BATT SmbNPBatt = (PSMB_NP_BATT) DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
NTSTATUS status;
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattCreate: ENTERING\n"));
|
||
|
||
|
||
status = IoAcquireRemoveLock (&SmbNPBatt->RemoveLock, IrpSp->FileObject);
|
||
|
||
//
|
||
// Complete the request and return status.
|
||
//
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattCreate: EXITING (status = 0x%08x\n", status));
|
||
return(status);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattClose(
|
||
IN PDEVICE_OBJECT DeviceObject,
|
||
IN PIRP Irp
|
||
)
|
||
{
|
||
PSMB_NP_BATT SmbNPBatt = (PSMB_NP_BATT) DeviceObject->DeviceExtension;
|
||
PIO_STACK_LOCATION IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattClose: ENTERING\n"));
|
||
|
||
IoReleaseRemoveLock (&SmbNPBatt->RemoveLock, IrpSp->FileObject);
|
||
|
||
//
|
||
// Complete the request and return status.
|
||
//
|
||
Irp->IoStatus.Status = STATUS_SUCCESS;
|
||
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattClose: EXITING\n"));
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattIoctl(
|
||
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
|
||
|
||
--*/
|
||
{
|
||
PSMB_NP_BATT SmbNPBatt;
|
||
PSMB_BATT SmbBatt;
|
||
ULONG InputLen, OutputLen;
|
||
PVOID IOBuffer;
|
||
PIO_STACK_LOCATION IrpSp;
|
||
|
||
BOOLEAN complete = TRUE;
|
||
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattIoctl: ENTERING\n"));
|
||
|
||
IrpSp = IoGetCurrentIrpStackLocation(Irp);
|
||
SmbNPBatt = (PSMB_NP_BATT) DeviceObject->DeviceExtension;
|
||
|
||
status = IoAcquireRemoveLock (&SmbNPBatt->RemoveLock, Irp);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
if (SmbNPBatt->SmbBattFdoType == SmbTypePdo) {
|
||
status = STATUS_NOT_SUPPORTED;
|
||
} else if (SmbNPBatt->SmbBattFdoType == SmbTypeSubsystem) {
|
||
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_SMBBATT_DATA) {
|
||
|
||
//
|
||
// Direct Access Irp
|
||
//
|
||
|
||
IOBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
InputLen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
||
OutputLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
||
status = SmbBattDirectDataAccess (
|
||
(PSMB_NP_BATT) DeviceObject->DeviceExtension,
|
||
(PSMBBATT_DATA_STRUCT) IOBuffer,
|
||
InputLen,
|
||
OutputLen
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
Irp->IoStatus.Information = OutputLen;
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
|
||
} else {
|
||
status = STATUS_NOT_SUPPORTED;
|
||
}
|
||
} else {
|
||
ASSERT (SmbNPBatt->SmbBattFdoType == SmbTypeBattery);
|
||
|
||
//
|
||
// Check to see if this is one of the private Ioctls we handle
|
||
//
|
||
|
||
switch (IrpSp->Parameters.DeviceIoControl.IoControlCode) {
|
||
case IOCTL_SMBBATT_DATA:
|
||
IOBuffer = Irp->AssociatedIrp.SystemBuffer;
|
||
InputLen = IrpSp->Parameters.DeviceIoControl.InputBufferLength;
|
||
OutputLen = IrpSp->Parameters.DeviceIoControl.OutputBufferLength;
|
||
|
||
//
|
||
// This one is only handled by the battery subsystem
|
||
//
|
||
|
||
status = SmbBattDirectDataAccess (
|
||
(PSMB_NP_BATT) DeviceObject->DeviceExtension,
|
||
(PSMBBATT_DATA_STRUCT) IOBuffer,
|
||
InputLen,
|
||
OutputLen
|
||
);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
Irp->IoStatus.Information = OutputLen;
|
||
} else {
|
||
Irp->IoStatus.Information = 0;
|
||
}
|
||
break;
|
||
default:
|
||
//
|
||
// Not IOCTL for us, see if it's for the battery
|
||
//
|
||
|
||
SmbBatt = SmbNPBatt->Batt;
|
||
status = BatteryClassIoctl (SmbNPBatt->Class, Irp);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
//
|
||
// The Irp was completed by the batery class. Don't
|
||
// touch the Irp. Simply release the lock and return.
|
||
//
|
||
|
||
IoReleaseRemoveLock (&SmbNPBatt->RemoveLock, Irp);
|
||
BattPrint(BAT_TRACE, ("SmbBattIoctl: EXITING (was battery IOCTL)\n", status));
|
||
return status;
|
||
}
|
||
|
||
break;
|
||
|
||
} // switch (IrpSp->Parameters.DeviceIoControl.IoControlCode)
|
||
}
|
||
|
||
IoReleaseRemoveLock (&SmbNPBatt->RemoveLock, Irp);
|
||
}
|
||
|
||
Irp->IoStatus.Status = status;
|
||
IoCompleteRequest (Irp, IO_NO_INCREMENT);
|
||
BattPrint(BAT_TRACE, ("SmbBattIoctl: EXITING (status = 0x%08x)\n", status));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattQueryTag (
|
||
IN PVOID Context,
|
||
OUT PULONG BatteryTag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called by the class driver to retrieve the batteries current tag value
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
|
||
BatteryTag - Pointer to return current tag
|
||
|
||
|
||
Return Value:
|
||
|
||
Success if there is a battery currently installed, else no such device.
|
||
|
||
--*/
|
||
{
|
||
//PSMB_BATT_SUBSYSTEM subsystemExt;
|
||
NTSTATUS status;
|
||
PSMB_BATT SmbBatt;
|
||
ULONG oldSelectorState;
|
||
|
||
PAGED_CODE();
|
||
BattPrint(BAT_TRACE, ("SmbBattQueryTag: ENTERING\n"));
|
||
|
||
//
|
||
// Get device lock and make sure the selector is set up to talk to us.
|
||
// Since multiple people may be doing this, always lock the selector
|
||
// first followed by the battery.
|
||
//
|
||
|
||
SmbBatt = (PSMB_BATT) Context;
|
||
SmbBattLockSelector (SmbBatt->Selector);
|
||
SmbBattLockDevice (SmbBatt);
|
||
|
||
status = SmbBattSetSelectorComm (SmbBatt, &oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattQueryTag: can't set selector communications path\n"));
|
||
} else {
|
||
|
||
//
|
||
// If the tag is not valid, check for one
|
||
//
|
||
|
||
if (SmbBatt->Info.Tag == BATTERY_TAG_INVALID) {
|
||
SmbBatt->Info.Valid = 0;
|
||
}
|
||
|
||
//
|
||
// Insure the static information regarding the battery up to date
|
||
//
|
||
|
||
SmbBattVerifyStaticInfo (SmbBatt, 0);
|
||
|
||
//
|
||
// If theres a battery return it's tag
|
||
//
|
||
|
||
if (SmbBatt->Info.Tag != BATTERY_TAG_INVALID) {
|
||
*BatteryTag = SmbBatt->Info.Tag;
|
||
status = STATUS_SUCCESS;
|
||
} else {
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Done, unlock the device and reset the selector state
|
||
//
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
status = SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattQueryTag: can't reset selector communications path\n"));
|
||
}
|
||
} else {
|
||
//
|
||
// Ignore the return value from ResetSelectorComm because we already
|
||
// have an error here.
|
||
//
|
||
|
||
SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
}
|
||
|
||
|
||
SmbBattUnlockDevice (SmbBatt);
|
||
SmbBattUnlockSelector (SmbBatt->Selector);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattQueryTag: EXITING\n"));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattQueryInformation (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
IN BATTERY_QUERY_INFORMATION_LEVEL Level,
|
||
IN LONG AtRate OPTIONAL,
|
||
OUT PVOID Buffer,
|
||
IN ULONG BufferLength,
|
||
OUT PULONG ReturnedLength
|
||
)
|
||
{
|
||
PSMB_BATT SmbBatt;
|
||
ULONG ResultData;
|
||
BOOLEAN IoCheck;
|
||
NTSTATUS status, st;
|
||
PVOID ReturnBuffer;
|
||
ULONG ReturnBufferLength;
|
||
WCHAR scratchBuffer[SMB_MAX_DATA_SIZE+1]; // +1 for UNICODE_NULL
|
||
UNICODE_STRING unicodeString;
|
||
UNICODE_STRING tmpUnicodeString;
|
||
ANSI_STRING ansiString;
|
||
ULONG oldSelectorState;
|
||
BATTERY_REPORTING_SCALE granularity;
|
||
|
||
PAGED_CODE();
|
||
BattPrint(BAT_TRACE, ("SmbBattQueryInformation: ENTERING\n"));
|
||
|
||
|
||
if (BatteryTag == BATTERY_TAG_INVALID) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
//
|
||
// Get device lock and make sure the selector is set up to talk to us.
|
||
// Since multiple people may be doing this, always lock the selector
|
||
// first followed by the battery.
|
||
//
|
||
|
||
SmbBatt = (PSMB_BATT) Context;
|
||
SmbBattLockSelector (SmbBatt->Selector);
|
||
SmbBattLockDevice (SmbBatt);
|
||
|
||
status = SmbBattSetSelectorComm (SmbBatt, &oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattQueryInformation: can't set selector communications path\n"));
|
||
} else {
|
||
|
||
do {
|
||
ResultData = 0;
|
||
ReturnBuffer = NULL;
|
||
ReturnBufferLength = 0;
|
||
status = STATUS_SUCCESS;
|
||
|
||
|
||
//
|
||
// If no device, or caller has the wrong ID give an error
|
||
//
|
||
|
||
if (BatteryTag != SmbBatt->Info.Tag) {
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Get the info requested
|
||
//
|
||
|
||
switch (Level) {
|
||
case BatteryInformation:
|
||
ReturnBuffer = &SmbBatt->Info.Info;
|
||
ReturnBufferLength = sizeof (SmbBatt->Info.Info);
|
||
break;
|
||
|
||
case BatteryGranularityInformation:
|
||
SmbBattRW(SmbBatt, BAT_FULL_CHARGE_CAPACITY, &granularity.Capacity);
|
||
granularity.Capacity *= SmbBatt->Info.PowerScale;
|
||
granularity.Granularity = SmbBatt->Info.PowerScale;
|
||
ReturnBuffer = &granularity;
|
||
ReturnBufferLength = sizeof (granularity);
|
||
break;
|
||
|
||
case BatteryTemperature:
|
||
SmbBattRW(SmbBatt, BAT_TEMPERATURE, &ResultData);
|
||
ReturnBuffer = &ResultData;
|
||
ReturnBufferLength = sizeof(ULONG);
|
||
break;
|
||
|
||
case BatteryEstimatedTime:
|
||
|
||
//
|
||
// If an AtRate has been specified, then we will use the AtRate
|
||
// functions to get this information (AtRateTimeToEmpty()).
|
||
// Otherwise, we will return the AVERAGE_TIME_TO_EMPTY.
|
||
//
|
||
|
||
BattPrint(BAT_DATA, ("SmbBattQueryInformation: EstimatedTime: AtRate: %08x\n", AtRate));
|
||
|
||
if (AtRate != 0) {
|
||
//
|
||
// Currently we only support the time to empty functions.
|
||
//
|
||
|
||
ASSERT (AtRate < 0);
|
||
|
||
//
|
||
// The smart battery input value for AtRate is in 10mW increments
|
||
//
|
||
|
||
AtRate /= (LONG)SmbBatt->Info.PowerScale;
|
||
BattPrint(BAT_DATA, ("SmbBattQueryInformation: EstimatedTime: AtRate scaled to: %08x\n", AtRate));
|
||
SmbBattWW(SmbBatt, BAT_AT_RATE, AtRate);
|
||
SmbBattRW(SmbBatt, BAT_RATE_TIME_TO_EMPTY, &ResultData);
|
||
BattPrint(BAT_DATA, ("SmbBattQueryInformation: EstimatedTime: AT_RATE_TIME_TO_EMPTY: %08x\n", ResultData));
|
||
|
||
} else {
|
||
|
||
SmbBattRW(SmbBatt, BAT_AVERAGE_TIME_TO_EMPTY, &ResultData);
|
||
BattPrint(BAT_DATA, ("SmbBattQueryInformation: EstimatedTime: AVERAGE_TIME_TO_EMPTY: %08x\n", ResultData));
|
||
}
|
||
|
||
if (ResultData == 0xffff) {
|
||
ResultData = BATTERY_UNKNOWN_TIME;
|
||
} else {
|
||
ResultData *= 60;
|
||
}
|
||
BattPrint(BAT_DATA, ("SmbBattQueryInformation: (%01x) EstimatedTime: %08x seconds\n", SmbBatt->SelectorBitPosition, ResultData));
|
||
|
||
ReturnBuffer = &ResultData;
|
||
ReturnBufferLength = sizeof(ULONG);
|
||
break;
|
||
|
||
case BatteryDeviceName:
|
||
//
|
||
// This has to be returned as a WCHAR string but is kept internally
|
||
// as a character string. Have to convert it.
|
||
//
|
||
|
||
unicodeString.Buffer = Buffer;
|
||
unicodeString.MaximumLength = BufferLength > (USHORT)-1 ? (USHORT) -1 : (USHORT)BufferLength;
|
||
|
||
ansiString.Length = SmbBatt->Info.DeviceNameLength;
|
||
ansiString.MaximumLength = sizeof(SmbBatt->Info.DeviceName);
|
||
ansiString.Buffer = SmbBatt->Info.DeviceName;
|
||
status = RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
|
||
if (NT_SUCCESS(status)) {
|
||
ReturnBuffer = Buffer;
|
||
ReturnBufferLength = unicodeString.Length;
|
||
}
|
||
break;
|
||
|
||
case BatteryManufactureDate:
|
||
ReturnBuffer = &SmbBatt->Info.ManufacturerDate;
|
||
ReturnBufferLength = sizeof (SmbBatt->Info.ManufacturerDate);
|
||
break;
|
||
|
||
case BatteryManufactureName:
|
||
//
|
||
// This has to be returned as a WCHAR string but is kept internally
|
||
// as a character string. Have to convert it.
|
||
//
|
||
|
||
unicodeString.Buffer = Buffer;
|
||
unicodeString.MaximumLength = BufferLength > (USHORT)-1 ? (USHORT) -1 : (USHORT)BufferLength;
|
||
|
||
ansiString.Length = SmbBatt->Info.ManufacturerNameLength;
|
||
ansiString.MaximumLength = sizeof(SmbBatt->Info.ManufacturerName);
|
||
ansiString.Buffer = SmbBatt->Info.ManufacturerName;
|
||
status = RtlAnsiStringToUnicodeString (&unicodeString, &ansiString, FALSE);
|
||
if (NT_SUCCESS(status)) {
|
||
ReturnBuffer = Buffer;
|
||
ReturnBufferLength = unicodeString.Length;
|
||
}
|
||
break;
|
||
|
||
case BatteryUniqueID:
|
||
//
|
||
// The unique ID is a character string consisting of the serial
|
||
// number, the manufacturer name, and the device name.
|
||
//
|
||
|
||
unicodeString.Buffer = Buffer;
|
||
unicodeString.MaximumLength = BufferLength > (USHORT)-1 ? (USHORT) -1 : (USHORT)BufferLength;
|
||
|
||
tmpUnicodeString.Buffer = scratchBuffer;
|
||
tmpUnicodeString.MaximumLength = sizeof (scratchBuffer);
|
||
|
||
RtlIntegerToUnicodeString(SmbBatt->Info.SerialNumber, 10, &unicodeString);
|
||
|
||
ansiString.Length = SmbBatt->Info.ManufacturerNameLength;
|
||
ansiString.MaximumLength = sizeof(SmbBatt->Info.ManufacturerName);
|
||
ansiString.Buffer = SmbBatt->Info.ManufacturerName;
|
||
status = RtlAnsiStringToUnicodeString (&tmpUnicodeString, &ansiString, FALSE);
|
||
if (!NT_SUCCESS(status)) break;
|
||
status = RtlAppendUnicodeStringToString (&unicodeString, &tmpUnicodeString);
|
||
if (!NT_SUCCESS(status)) break;
|
||
|
||
ansiString.Length = SmbBatt->Info.DeviceNameLength;
|
||
ansiString.MaximumLength = sizeof(SmbBatt->Info.DeviceName);
|
||
ansiString.Buffer = SmbBatt->Info.DeviceName;
|
||
status = RtlAnsiStringToUnicodeString (&tmpUnicodeString, &ansiString, FALSE);
|
||
if (!NT_SUCCESS(status)) break;
|
||
status = RtlAppendUnicodeStringToString (&unicodeString, &tmpUnicodeString);
|
||
if (!NT_SUCCESS(status)) break;
|
||
|
||
ReturnBuffer = Buffer;
|
||
ReturnBufferLength = unicodeString.Length;
|
||
break;
|
||
|
||
}
|
||
|
||
//
|
||
// Re-verify static info in case there's been an IO error
|
||
//
|
||
|
||
//IoCheck = SmbBattVerifyStaticInfo (SmbBatt, BatteryTag);
|
||
IoCheck = FALSE;
|
||
|
||
} while (IoCheck);
|
||
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
//
|
||
// Done, return buffer if needed
|
||
//
|
||
|
||
*ReturnedLength = ReturnBufferLength;
|
||
|
||
if (ReturnBuffer != Buffer) {
|
||
// ReturnBuffer == Buffer indicates that data is already copied.
|
||
//
|
||
if (BufferLength < ReturnBufferLength) {
|
||
status = STATUS_BUFFER_TOO_SMALL;
|
||
}
|
||
|
||
if (NT_SUCCESS(status) && ReturnBuffer) {
|
||
memcpy (Buffer, ReturnBuffer, ReturnBufferLength);
|
||
}
|
||
}
|
||
|
||
//
|
||
// Unlock the device and reset the selector state
|
||
//
|
||
|
||
st = SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
if (!NT_SUCCESS (st)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattQueryInformation: can't reset selector communications path\n"));
|
||
status = st;
|
||
}
|
||
} else {
|
||
*ReturnedLength = 0;
|
||
|
||
//
|
||
// Ignore the return value from ResetSelectorComm because we already
|
||
// have an error here.
|
||
//
|
||
|
||
SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
}
|
||
|
||
SmbBattUnlockDevice (SmbBatt);
|
||
SmbBattUnlockSelector (SmbBatt->Selector);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattQueryInformation: EXITING\n"));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattSetInformation (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
IN BATTERY_SET_INFORMATION_LEVEL Level,
|
||
IN PVOID Buffer OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called by the class driver to set the battery's charge/discharge state.
|
||
The smart battery does not support the critical bias function of this
|
||
call.
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
|
||
BatteryTag - Tag of current battery
|
||
|
||
Level - Action being asked for
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
PSMB_BATT SmbBatt;
|
||
ULONG newSelectorState;
|
||
ULONG selectorState;
|
||
UCHAR smbStatus;
|
||
ULONG tmp;
|
||
|
||
NTSTATUS status = STATUS_NOT_SUPPORTED;
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattSetInformation: ENTERING\n"));
|
||
|
||
|
||
SmbBatt = (PSMB_BATT) Context;
|
||
|
||
//
|
||
// See if this is for our battery
|
||
//
|
||
|
||
if ((BatteryTag == BATTERY_TAG_INVALID) || (BatteryTag != SmbBatt->Info.Tag)) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
|
||
//
|
||
// We can only do this if there is a selector in the system
|
||
//
|
||
|
||
if ((SmbBatt->SelectorPresent) && (SmbBatt->Selector)) {
|
||
|
||
//
|
||
// Get a lock on the selector
|
||
//
|
||
|
||
SmbBattLockSelector (SmbBatt->Selector);
|
||
|
||
switch (Level) {
|
||
|
||
case BatteryCharge:
|
||
BattPrint(BAT_IRPS, ("SmbBattSetInformation: Got SetInformation for BatteryCharge\n"));
|
||
|
||
//
|
||
// Set the appropriate bit in the selector state charge nibble
|
||
//
|
||
|
||
newSelectorState = SELECTOR_SET_CHARGE_MASK;
|
||
newSelectorState |= (SmbBatt->SelectorBitPosition << SELECTOR_SHIFT_CHARGE);
|
||
|
||
//
|
||
// Write the new selector state, then read it back. The system
|
||
// may or may not let us do this.
|
||
//
|
||
|
||
smbStatus = SmbBattGenericWW (
|
||
SmbBatt->SmbHcFdo,
|
||
SmbBatt->Selector->SelectorAddress,
|
||
SmbBatt->Selector->SelectorStateCommand,
|
||
newSelectorState
|
||
);
|
||
|
||
if (smbStatus != SMB_STATUS_OK) {
|
||
BattPrint(BAT_ERROR,
|
||
("SmbBattSetInformation: couldn't write selector state - %x\n",
|
||
smbStatus)
|
||
);
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
smbStatus = SmbBattGenericRW (
|
||
SmbBatt->SmbHcFdo,
|
||
SmbBatt->Selector->SelectorAddress,
|
||
SmbBatt->Selector->SelectorStateCommand,
|
||
&selectorState
|
||
);
|
||
|
||
if ((smbStatus != SMB_STATUS_OK)) {
|
||
BattPrint(BAT_ERROR,
|
||
("SmbBattSetInformation: couldn't read selector state - %x\n",
|
||
smbStatus)
|
||
);
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Check the status that was read versus what we wrote
|
||
// to see if the operation was successful
|
||
//
|
||
|
||
// To support simultaneous charging of more than one battery,
|
||
// we can't check the charge nibble to see if it is equal to
|
||
// what we wrote, but we can check to see if the battery
|
||
// we specified to charge is now set to charge.
|
||
|
||
tmp = (selectorState & SELECTOR_STATE_CHARGE_MASK) >> SELECTOR_SHIFT_CHARGE;
|
||
if (SmbBattReverseLogic(SmbBatt->Selector, tmp)) {
|
||
tmp ^= SELECTOR_STATE_PRESENT_MASK;
|
||
}
|
||
|
||
if (tmp & SmbBatt->SelectorBitPosition) {
|
||
|
||
BattPrint(BAT_IRPS, ("SmbBattSetInformation: successfully set charging battery\n"));
|
||
|
||
//
|
||
// Success! Save the new selector state in the cache
|
||
//
|
||
|
||
SmbBatt->Selector->SelectorState = selectorState;
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
BattPrint(BAT_ERROR, ("SmbBattSetInformation: couldn't set charging battery\n"));
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
break;
|
||
|
||
case BatteryDischarge:
|
||
|
||
BattPrint(BAT_IRPS, ("SmbBattSetInformation: Got SetInformation for BatteryDischarge\n"));
|
||
|
||
//
|
||
// Set the appropriate bit in the selector state power by nibble
|
||
//
|
||
|
||
newSelectorState = SELECTOR_SET_POWER_BY_MASK;
|
||
newSelectorState |= (SmbBatt->SelectorBitPosition << SELECTOR_SHIFT_POWER);
|
||
|
||
//
|
||
// Write the new selector state, then read it back. The system
|
||
// may or may not let us do this.
|
||
//
|
||
|
||
smbStatus = SmbBattGenericWW (
|
||
SmbBatt->SmbHcFdo,
|
||
SmbBatt->Selector->SelectorAddress,
|
||
SmbBatt->Selector->SelectorStateCommand,
|
||
newSelectorState
|
||
);
|
||
|
||
if (smbStatus != SMB_STATUS_OK) {
|
||
BattPrint(BAT_ERROR,
|
||
("SmbBattSetInformation: couldn't write selector state - %x\n",
|
||
smbStatus)
|
||
);
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
smbStatus = SmbBattGenericRW (
|
||
SmbBatt->SmbHcFdo,
|
||
SmbBatt->Selector->SelectorAddress,
|
||
SmbBatt->Selector->SelectorStateCommand,
|
||
&selectorState
|
||
);
|
||
|
||
if ((smbStatus != SMB_STATUS_OK)) {
|
||
BattPrint(BAT_ERROR,
|
||
("SmbBattSetInformation: couldn't read selector state - %x\n",
|
||
smbStatus)
|
||
);
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
break;
|
||
}
|
||
|
||
|
||
//
|
||
// Check the status that was read versus what we wrote
|
||
// to see if the operation was successful
|
||
//
|
||
|
||
// To support simultaneous powering of more than one battery,
|
||
// we can't check the power nibble to see if it is equal to
|
||
// what we wrote, but we can check to see if the battery
|
||
// we specified to power by is now set to power the system.
|
||
|
||
tmp = (selectorState & SELECTOR_STATE_POWER_BY_MASK) >> SELECTOR_SHIFT_POWER;
|
||
if (SmbBattReverseLogic(SmbBatt->Selector, tmp)) {
|
||
tmp ^= SELECTOR_STATE_PRESENT_MASK;
|
||
}
|
||
|
||
if (tmp & SmbBatt->SelectorBitPosition) {
|
||
|
||
BattPrint(BAT_IRPS, ("SmbBattSetInformation: successfully set powering battery\n"));
|
||
|
||
//
|
||
// Success! Save the new selector state in the cache
|
||
//
|
||
|
||
SmbBatt->Selector->SelectorState = selectorState;
|
||
status = STATUS_SUCCESS;
|
||
|
||
} else {
|
||
BattPrint(BAT_ERROR, ("SmbBattSetInformation: couldn't set powering battery\n"));
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
break;
|
||
|
||
} // switch (Level)
|
||
|
||
//
|
||
// Release the lock on the selector
|
||
//
|
||
|
||
SmbBattUnlockSelector (SmbBatt->Selector);
|
||
|
||
} // if (SmbBatt->Selector->SelectorPresent)
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattSetInformation: EXITING\n"));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattGetPowerState (
|
||
IN PSMB_BATT SmbBatt,
|
||
OUT PULONG PowerState,
|
||
OUT PLONG Current
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Returns the current state of AC power. There are several cases which
|
||
make this far more complex than it really ought to be.
|
||
|
||
NOTE: the selector must be locked before entering this routine.
|
||
|
||
Arguments:
|
||
|
||
SmbBatt - Miniport context value for battery
|
||
|
||
AcConnected - Pointer to a boolean where the AC status is returned
|
||
|
||
Return Value:
|
||
|
||
NTSTATUS
|
||
|
||
--*/
|
||
{
|
||
ULONG tmp;
|
||
ULONG chargeBattery;
|
||
ULONG powerBattery;
|
||
NTSTATUS status;
|
||
UCHAR smbStatus;
|
||
|
||
PAGED_CODE();
|
||
|
||
status = STATUS_SUCCESS;
|
||
*PowerState = 0;
|
||
|
||
//
|
||
// Is there a selector in the system? if not, go read directly from the charger
|
||
//
|
||
|
||
if ((SmbBatt->SelectorPresent) && (SmbBatt->Selector)) {
|
||
|
||
//
|
||
// There is a selector, we will examine the CHARGE nibble of the state register
|
||
//
|
||
|
||
SmbBattGenericRW(
|
||
SmbBatt->SmbHcFdo,
|
||
SMB_SELECTOR_ADDRESS,
|
||
SELECTOR_SELECTOR_STATE,
|
||
&SmbBatt->Selector->SelectorState);
|
||
|
||
chargeBattery = (SmbBatt->Selector->SelectorState & SELECTOR_STATE_CHARGE_MASK) >> SELECTOR_SHIFT_CHARGE;
|
||
powerBattery = (SmbBatt->Selector->SelectorState & SELECTOR_STATE_POWER_BY_MASK) >> SELECTOR_SHIFT_POWER;
|
||
|
||
|
||
//
|
||
// If the bits in the CHARGE_X nibble of the selector state register are in the
|
||
// reverse logic state, then AC is connected, otherwise AC is not connected.
|
||
//
|
||
// NOTE: This code depends on every selector implementing this. If it turns out
|
||
// that this is optional, we can no longer depend on this, and must enable the
|
||
// code below it.
|
||
//
|
||
|
||
if (SmbBattReverseLogic(SmbBatt->Selector, chargeBattery)) {
|
||
*PowerState |= BATTERY_POWER_ON_LINE;
|
||
}
|
||
|
||
//
|
||
// Look at Charge Indicator if it is supported
|
||
//
|
||
|
||
if (*PowerState & BATTERY_POWER_ON_LINE) {
|
||
|
||
if (SmbBatt->Selector->SelectorInfo & SELECTOR_INFO_CHARGING_INDICATOR_BIT) {
|
||
if (SmbBattReverseLogic(SmbBatt->Selector, powerBattery)) {
|
||
*PowerState |= BATTERY_CHARGING;
|
||
}
|
||
}
|
||
|
||
if (*Current > 0) {
|
||
*PowerState |= BATTERY_CHARGING;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
if (*Current <= 0) {
|
||
|
||
//
|
||
// There is some small leakage on some systems, even when AC
|
||
// is present. So, if AC is present, and the draw
|
||
// is below this "noise" level we will not report as discharging
|
||
// and zero this out.
|
||
//
|
||
|
||
if (*Current < -25) {
|
||
*PowerState |= BATTERY_DISCHARGING;
|
||
|
||
} else {
|
||
*Current = 0;
|
||
|
||
}
|
||
}
|
||
|
||
//else {
|
||
// *PowerState |= BATTERY_CHARGING;
|
||
//
|
||
//}
|
||
|
||
// If we don't report as discharging, then the AC adapter removal
|
||
// might cause a PowerState of 0 to return, which PowerMeter assumes
|
||
// means, don't change anything
|
||
|
||
*PowerState |= BATTERY_DISCHARGING;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// There is no selector, so we'll try to read from the charger.
|
||
//
|
||
|
||
smbStatus = SmbBattGenericRW (
|
||
SmbBatt->SmbHcFdo,
|
||
SMB_CHARGER_ADDRESS,
|
||
CHARGER_STATUS,
|
||
&tmp
|
||
);
|
||
|
||
if (smbStatus != SMB_STATUS_OK) {
|
||
BattPrint (
|
||
BAT_ERROR,
|
||
("SmbBattGetPowerState: Trying to get charging info, couldn't read from charger at %x, status %x\n",
|
||
SMB_CHARGER_ADDRESS,
|
||
smbStatus)
|
||
);
|
||
|
||
*PowerState = 0;
|
||
|
||
status = STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
// Read Charger Successful
|
||
|
||
else {
|
||
|
||
if (tmp & CHARGER_STATUS_AC_PRESENT_BIT) {
|
||
*PowerState = BATTERY_POWER_ON_LINE;
|
||
|
||
if (*Current > 0) {
|
||
*PowerState |= BATTERY_CHARGING;
|
||
}
|
||
|
||
|
||
} else {
|
||
|
||
if (*Current <= 0) {
|
||
|
||
//
|
||
// There is some small leakage on some systems, even when AC
|
||
// is present. So, if AC is present, and the draw
|
||
// is below this "noise" level we will not report as discharging
|
||
// and zero this out.
|
||
//
|
||
|
||
if (*Current < -25) {
|
||
*PowerState |= BATTERY_DISCHARGING;
|
||
} else {
|
||
*Current = 0;
|
||
}
|
||
}
|
||
|
||
// If we don't report as discharging, then the AC adapter removal
|
||
// might cause a PowerState of 0 to return, which PowerMeter assumes
|
||
// means, don't change anything
|
||
*PowerState |= BATTERY_DISCHARGING;
|
||
|
||
}
|
||
}
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattQueryStatus (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
OUT PBATTERY_STATUS BatteryStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Called by the class driver to retrieve the batteries current status
|
||
|
||
N.B. the battery class driver will serialize all requests it issues to
|
||
the miniport for a given battery. However, this miniport implements
|
||
a lock on the battery device as it needs to serialize to the smb
|
||
battery selector device as well.
|
||
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
PSMB_BATT SmbBatt;
|
||
NTSTATUS status;
|
||
BOOLEAN IoCheck;
|
||
LONG Current;
|
||
ULONG oldSelectorState;
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattQueryStatus: ENTERING\n"));
|
||
|
||
|
||
if (BatteryTag == BATTERY_TAG_INVALID) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Get device lock and make sure the selector is set up to talk to us.
|
||
// Since multiple people may be doing this, always lock the selector
|
||
// first followed by the battery.
|
||
//
|
||
|
||
SmbBatt = (PSMB_BATT) Context;
|
||
SmbBattLockSelector (SmbBatt->Selector);
|
||
SmbBattLockDevice (SmbBatt);
|
||
|
||
status = SmbBattSetSelectorComm (SmbBatt, &oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattQueryStatus: can't set selector communications path\n"));
|
||
} else {
|
||
|
||
do {
|
||
if (BatteryTag != SmbBatt->Info.Tag) {
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
break;
|
||
}
|
||
|
||
SmbBattRW(SmbBatt, BAT_VOLTAGE, &BatteryStatus->Voltage);
|
||
BatteryStatus->Voltage *= SmbBatt->Info.VoltageScale;
|
||
|
||
SmbBattRW(SmbBatt, BAT_REMAINING_CAPACITY, &BatteryStatus->Capacity);
|
||
BatteryStatus->Capacity *= SmbBatt->Info.PowerScale;
|
||
|
||
SmbBattRSW(SmbBatt, BAT_CURRENT, &Current);
|
||
Current *= SmbBatt->Info.CurrentScale;
|
||
|
||
BattPrint(BAT_DATA,
|
||
("SmbBattQueryStatus: (%01x)\n"
|
||
"------- Remaining Capacity - %x\n"
|
||
"------- Voltage - %x\n"
|
||
"------- Current - %x\n",
|
||
SmbBatt->SelectorBitPosition,
|
||
BatteryStatus->Capacity,
|
||
BatteryStatus->Voltage,
|
||
Current)
|
||
);
|
||
|
||
BatteryStatus->Rate = (Current * ((LONG)BatteryStatus->Voltage))/1000;
|
||
|
||
//
|
||
// Check to see if we are currently connected to AC.
|
||
//
|
||
|
||
status = SmbBattGetPowerState (SmbBatt, &BatteryStatus->PowerState, &Current);
|
||
if (!NT_SUCCESS (status)) {
|
||
|
||
BatteryStatus->PowerState = 0;
|
||
}
|
||
|
||
//
|
||
// Re-verify static info in case there's been an IO error
|
||
//
|
||
|
||
IoCheck = SmbBattVerifyStaticInfo (SmbBatt, BatteryTag);
|
||
|
||
} while (IoCheck);
|
||
|
||
}
|
||
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
//
|
||
// Set batteries current power state & capacity
|
||
//
|
||
|
||
SmbBatt->Info.PowerState = BatteryStatus->PowerState;
|
||
SmbBatt->Info.Capacity = BatteryStatus->Capacity;
|
||
|
||
//
|
||
// Done, unlock the device and reset the selector state
|
||
//
|
||
|
||
status = SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattQueryStatus: can't reset selector communications path\n"));
|
||
}
|
||
} else {
|
||
//
|
||
// Ignore the return value from ResetSelectorComm because we already
|
||
// have an error here.
|
||
//
|
||
|
||
SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
}
|
||
|
||
SmbBattUnlockDevice (SmbBatt);
|
||
SmbBattUnlockSelector (SmbBatt->Selector);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattQueryStatus: EXITING\n"));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattSetStatusNotify (
|
||
IN PVOID Context,
|
||
IN ULONG BatteryTag,
|
||
IN PBATTERY_NOTIFY Notify
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
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.
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
|
||
BatteryTag - Tag of current battery
|
||
|
||
BatteryNotify - The notification setting
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
PSMB_BATT SmbBatt;
|
||
NTSTATUS status;
|
||
BOOLEAN UpdateAlarm;
|
||
ULONG Target, NewAlarm;
|
||
LONG DeltaAdjustment, Attempt, i;
|
||
ULONG oldSelectorState;
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattSetStatusNotify: ENTERING\n"));
|
||
|
||
if (BatteryTag == BATTERY_TAG_INVALID) {
|
||
return STATUS_NO_SUCH_DEVICE;
|
||
}
|
||
|
||
if ((Notify->HighCapacity == BATTERY_UNKNOWN_CAPACITY) ||
|
||
(Notify->LowCapacity == BATTERY_UNKNOWN_CAPACITY)) {
|
||
BattPrint(BAT_WARN, ("SmbBattSetStatusNotify: Failing because of BATTERY_UNKNOWN_CAPACITY.\n"));
|
||
return STATUS_NOT_SUPPORTED;
|
||
}
|
||
|
||
status = STATUS_SUCCESS;
|
||
|
||
//
|
||
// Get device lock and make sure the selector is set up to talk to us.
|
||
// Since multiple people may be doing this, always lock the selector
|
||
// first followed by the battery.
|
||
//
|
||
|
||
SmbBatt = (PSMB_BATT) Context;
|
||
|
||
BattPrint(BAT_DATA, ("SmbBattSetStatusNotify: (%01x): Called with LowCapacity = %08x\n",
|
||
SmbBatt->SelectorBitPosition, Notify->LowCapacity));
|
||
|
||
SmbBattLockSelector (SmbBatt->Selector);
|
||
SmbBattLockDevice (SmbBatt);
|
||
|
||
status = SmbBattSetSelectorComm (SmbBatt, &oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattSetStatusNotify: can't set selector communications path\n"));
|
||
|
||
} else {
|
||
|
||
// Target (10*PS*mWh) = Lowcapacity (mWh) / 10*PS (1/(10*PS))
|
||
Target = Notify->LowCapacity / SmbBatt->Info.PowerScale;
|
||
DeltaAdjustment = 0;
|
||
|
||
BattPrint(BAT_DATA, ("SmbBattSetStatusNotify: (%01x): Last set to: %08x\n",
|
||
SmbBatt->SelectorBitPosition, SmbBatt->AlarmLow.Setting));
|
||
|
||
//
|
||
// Some batteries are messed up and won't just take an alarm setting. Fortunately,
|
||
// the error is off in some linear fashion, so this code attempts to hone in on the
|
||
// adjustment needed to get the proper setting, along with an "allowable fudge value",
|
||
// since sometimes the desired setting can never be obtained.
|
||
//
|
||
|
||
for (; ;) {
|
||
if (BatteryTag != SmbBatt->Info.Tag) {
|
||
status = STATUS_NO_SUCH_DEVICE;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the status is charging we can't detect. Let the OS poll
|
||
//
|
||
|
||
if (SmbBatt->Info.PowerState & (BATTERY_CHARGING | BATTERY_POWER_ON_LINE)) {
|
||
status = STATUS_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the current capacity is below the target, fire an alarm and we're done
|
||
//
|
||
|
||
if (SmbBatt->Info.Capacity < Target) {
|
||
BatteryClassStatusNotify (SmbBatt->NP->Class);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the target setting is the Skip value then there was an error attempting
|
||
// this value last time.
|
||
//
|
||
|
||
if (Target == SmbBatt->AlarmLow.Skip) {
|
||
status = STATUS_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the current setting is above the current capacity then we need to
|
||
// program the alarm
|
||
//
|
||
|
||
UpdateAlarm = FALSE;
|
||
if (Target < SmbBatt->AlarmLow.Setting) {
|
||
UpdateAlarm = TRUE;
|
||
}
|
||
|
||
//
|
||
// If the target alarm is above the current setting, and the current setting
|
||
// is off by more then AllowedFudge then it needs updated
|
||
//
|
||
|
||
if (Target > SmbBatt->AlarmLow.Setting &&
|
||
Target - SmbBatt->AlarmLow.Setting > (ULONG) SmbBatt->AlarmLow.AllowedFudge) {
|
||
|
||
UpdateAlarm = TRUE;
|
||
}
|
||
|
||
//
|
||
// If alarm doesn't need updated, done
|
||
//
|
||
|
||
if (!UpdateAlarm) {
|
||
BattPrint(BAT_DATA, ("SmbBattSetStatusNotify: (%01x) NOT Updating Alarm.\n", SmbBatt->SelectorBitPosition));
|
||
break;
|
||
}
|
||
BattPrint(BAT_DATA, ("SmbBattSetStatusNotify: (%01x) Updating Alarm.\n", SmbBatt->SelectorBitPosition));
|
||
|
||
//
|
||
// If this is not the first time, then delta is not good enough. Let's start
|
||
// adjusting it
|
||
//
|
||
|
||
if (DeltaAdjustment) {
|
||
|
||
//
|
||
// If delta is positive subtract off 1/2 of fudge
|
||
//
|
||
|
||
if (DeltaAdjustment > 0) {
|
||
DeltaAdjustment -= SmbBatt->AlarmLow.AllowedFudge / 2 + (SmbBatt->AlarmLow.AllowedFudge & 1);
|
||
// too much - don't handle it
|
||
if (DeltaAdjustment > 50) {
|
||
status = STATUS_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
} else {
|
||
// too much - don't handle it
|
||
if (DeltaAdjustment < -50) {
|
||
status = STATUS_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
}
|
||
|
||
SmbBatt->AlarmLow.Delta += DeltaAdjustment;
|
||
}
|
||
|
||
//
|
||
// If attempt is less then 1, then we can't set it
|
||
//
|
||
|
||
Attempt = Target + SmbBatt->AlarmLow.Delta;
|
||
if (Attempt < 1) {
|
||
// battery class driver needs to poll for it
|
||
status = STATUS_NOT_SUPPORTED;
|
||
break;
|
||
}
|
||
|
||
//
|
||
// Perform IOs to update & read back the alarm. Use VerifyStaticInfo after
|
||
// IOs in case there's an IO error and the state is lost.
|
||
//
|
||
|
||
SmbBattWW(SmbBatt, BAT_REMAINING_CAPACITY_ALARM, Attempt);
|
||
|
||
// verify in case there was an IO error
|
||
|
||
//if (SmbBattVerifyStaticInfo (SmbBatt, BatteryTag)) {
|
||
// DeltaAdjustment = 0;
|
||
// continue;
|
||
//}
|
||
|
||
SmbBattRW(SmbBatt, BAT_REMAINING_CAPACITY_ALARM, &NewAlarm);
|
||
|
||
// verify in case there was an IO error
|
||
|
||
//if (SmbBattVerifyStaticInfo (SmbBatt, BatteryTag)) {
|
||
// DeltaAdjustment = 0;
|
||
// continue;
|
||
//}
|
||
|
||
BattPrint(BAT_DATA,
|
||
("SmbBattSetStatusNotify: (%01x) Want %X, Had %X, Got %X, CurrentCap %X, Delta %d, Fudge %d\n",
|
||
SmbBatt->SelectorBitPosition,
|
||
Target,
|
||
SmbBatt->AlarmLow.Setting,
|
||
NewAlarm,
|
||
SmbBatt->Info.Capacity / SmbBatt->Info.PowerScale,
|
||
SmbBatt->AlarmLow.Delta,
|
||
SmbBatt->AlarmLow.AllowedFudge
|
||
));
|
||
|
||
//
|
||
// If DeltaAdjustment was applied to Delta but the setting
|
||
// moved by more then DeltaAdjustment, then increase the
|
||
// allowed fudge.
|
||
//
|
||
|
||
if (DeltaAdjustment) {
|
||
i = NewAlarm - SmbBatt->AlarmLow.Setting - DeltaAdjustment;
|
||
if (DeltaAdjustment < 0) {
|
||
DeltaAdjustment = -DeltaAdjustment;
|
||
i = -i;
|
||
}
|
||
if (i > SmbBatt->AlarmLow.AllowedFudge) {
|
||
SmbBatt->AlarmLow.AllowedFudge = i;
|
||
BattPrint(BAT_DATA, ("SmbBattSetStatusNotify: Fudge increased to %x\n", SmbBatt->AlarmLow.AllowedFudge));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Current setting
|
||
//
|
||
|
||
SmbBatt->AlarmLow.Setting = NewAlarm;
|
||
|
||
//
|
||
// Compute next delta adjustment
|
||
//
|
||
|
||
DeltaAdjustment = Target - SmbBatt->AlarmLow.Setting;
|
||
}
|
||
|
||
//
|
||
// If there was an attempt to set the alarm but it failed, set the
|
||
// skip value so we don't keep trying to set an alarm for this value
|
||
// which isn't working
|
||
//
|
||
|
||
if (!NT_SUCCESS(status) && DeltaAdjustment) {
|
||
SmbBatt->AlarmLow.Skip = Target;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Done, unlock the device and reset the selector state
|
||
//
|
||
|
||
if (NT_SUCCESS (status)) {
|
||
status = SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattSetStatusNotify: can't reset selector communications path\n"));
|
||
}
|
||
} else {
|
||
//
|
||
// Ignore the return value from ResetSelectorComm because we already
|
||
// have an error here.
|
||
//
|
||
|
||
SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
}
|
||
|
||
SmbBattUnlockDevice (SmbBatt);
|
||
SmbBattUnlockSelector (SmbBatt->Selector);
|
||
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattSetStatusNotify: EXITING\n"));
|
||
return status;
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
SmbBattDisableStatusNotify (
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
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 SmbBattSetStatusNotify.
|
||
|
||
Arguments:
|
||
|
||
Context - Miniport context value for battery
|
||
|
||
Return Value:
|
||
|
||
Status
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
PSMB_BATT SmbBatt;
|
||
ULONG oldSelectorState;
|
||
|
||
PAGED_CODE();
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattDisableStatusNotify: ENTERING\n"));
|
||
|
||
SmbBatt = (PSMB_BATT) Context;
|
||
|
||
SmbBattLockSelector (SmbBatt->Selector);
|
||
SmbBattLockDevice (SmbBatt);
|
||
|
||
status = SmbBattSetSelectorComm (SmbBatt, &oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattDisableStatusNotify: can't set selector communications path\n"));
|
||
|
||
} else {
|
||
|
||
SmbBatt->AlarmLow.Setting = 0;
|
||
SmbBattWW(SmbBatt, BAT_REMAINING_CAPACITY_ALARM, 0);
|
||
|
||
//
|
||
// Done, reset the selector state.
|
||
//
|
||
status = SmbBattResetSelectorComm (SmbBatt, oldSelectorState);
|
||
if (!NT_SUCCESS (status)) {
|
||
BattPrint(BAT_ERROR, ("SmbBattDisableStatusNotify: can't reset selector communications path\n"));
|
||
}
|
||
}
|
||
|
||
//
|
||
// Done, unlock the device
|
||
//
|
||
|
||
|
||
SmbBattUnlockDevice (SmbBatt);
|
||
SmbBattUnlockSelector (SmbBatt->Selector);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattDisableStatusNotify: EXITING\n"));
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
SmbBattVerifyStaticInfo (
|
||
IN PSMB_BATT SmbBatt,
|
||
IN ULONG BatteryTag
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Reads any non-valid cached battery info and set Info.Valid accordingly.
|
||
Performs a serial number check after reading in battery info in order
|
||
to detect verify the data is from the same battery. If the value does
|
||
not match what is expect, the cached info is reset and the function
|
||
iterates until a consistent snapshot is obtained.
|
||
|
||
Arguments:
|
||
|
||
SmbBatt - Battery to read
|
||
|
||
BatteryTag - Tag of battery as expected by the caller
|
||
|
||
Return Value:
|
||
|
||
Returns a boolean to indicate to the caller that IO was performed.
|
||
This allows the caller to iterate on changes it may be making until
|
||
the battery state is correct.
|
||
|
||
--*/
|
||
{
|
||
ULONG BatteryMode;
|
||
ULONG ManufacturerDate;
|
||
UCHAR Buffer[SMB_MAX_DATA_SIZE];
|
||
UCHAR BufferLength;
|
||
BOOLEAN IoCheck;
|
||
STATIC_BAT_INFO NewInfo;
|
||
ULONG tmp;
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattVerifyStaticInfo: ENTERING\n"));
|
||
|
||
IoCheck = FALSE;
|
||
|
||
//
|
||
// Loop until state doesn't change
|
||
//
|
||
|
||
do {
|
||
|
||
//
|
||
// If device name and serial # not known, get them.
|
||
//
|
||
|
||
if (!(SmbBatt->Info.Valid & VALID_TAG_DATA)) {
|
||
|
||
IoCheck = TRUE;
|
||
SmbBatt->Info.Valid |= VALID_TAG_DATA;
|
||
|
||
RtlZeroMemory (&NewInfo, sizeof(NewInfo));
|
||
SmbBattRW(SmbBatt, BAT_SERIAL_NUMBER, &NewInfo.SerialNumber);
|
||
BattPrint(BAT_DATA,
|
||
("SmbBattVerifyStaticInfo: serial number = %x\n",
|
||
NewInfo.SerialNumber)
|
||
);
|
||
|
||
//
|
||
// If SerialNumber was read without a problem, read the rest
|
||
//
|
||
|
||
if (SmbBatt->Info.Valid & VALID_TAG_DATA) {
|
||
|
||
|
||
BattPrint(BAT_IRPS, ("SmbBattVerifyStaticInfo: reading manufacturer name\n"));
|
||
SmbBattRB (
|
||
SmbBatt,
|
||
BAT_MANUFACTURER_NAME,
|
||
NewInfo.ManufacturerName,
|
||
&NewInfo.ManufacturerNameLength
|
||
);
|
||
|
||
BattPrint(BAT_IRPS, ("SmbBattVerifyStaticInfo: reading device name\n"));
|
||
SmbBattRB (
|
||
SmbBatt,
|
||
BAT_DEVICE_NAME,
|
||
NewInfo.DeviceName,
|
||
&NewInfo.DeviceNameLength
|
||
);
|
||
|
||
//
|
||
// See if battery ID has changed
|
||
//
|
||
|
||
if (SmbBatt->Info.SerialNumber != NewInfo.SerialNumber ||
|
||
SmbBatt->Info.ManufacturerNameLength != NewInfo.ManufacturerNameLength ||
|
||
memcmp (SmbBatt->Info.ManufacturerName, NewInfo.ManufacturerName, NewInfo.ManufacturerNameLength) ||
|
||
SmbBatt->Info.DeviceNameLength != NewInfo.DeviceNameLength ||
|
||
memcmp (SmbBatt->Info.DeviceName, NewInfo.DeviceName, NewInfo.DeviceNameLength)) {
|
||
|
||
//
|
||
// This is a new battery, reread all information
|
||
//
|
||
|
||
SmbBatt->Info.Valid = VALID_TAG_DATA;
|
||
|
||
//
|
||
// Pickup ID info
|
||
//
|
||
|
||
SmbBatt->Info.SerialNumber = NewInfo.SerialNumber;
|
||
SmbBatt->Info.ManufacturerNameLength = NewInfo.ManufacturerNameLength;
|
||
memcpy (SmbBatt->Info.ManufacturerName, NewInfo.ManufacturerName, NewInfo.ManufacturerNameLength);
|
||
SmbBatt->Info.DeviceNameLength = NewInfo.DeviceNameLength;
|
||
memcpy (SmbBatt->Info.DeviceName, NewInfo.DeviceName, NewInfo.DeviceNameLength);
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// No battery, set cached info for no battery
|
||
//
|
||
|
||
SmbBatt->Info.Valid = VALID_TAG | VALID_TAG_DATA;
|
||
SmbBatt->Info.Tag = BATTERY_TAG_INVALID;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If the battery tag is valid, and it's NO_BATTERY there is no other
|
||
// cached info to read
|
||
//
|
||
|
||
if (SmbBatt->Info.Valid & VALID_TAG && SmbBatt->Info.Tag == BATTERY_TAG_INVALID) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// If the mode has not been verified, do it now
|
||
//
|
||
|
||
if (!(SmbBatt->Info.Valid & VALID_MODE)) {
|
||
|
||
SmbBatt->Info.Valid |= VALID_MODE;
|
||
SmbBattRW(SmbBatt, BAT_BATTERY_MODE, &BatteryMode);
|
||
BattPrint(BAT_DATA, ("SmbBattVerifyStaticInfo:(%01x) Was set to report in %s (BatteryMode = %04x)\n",
|
||
SmbBatt->SelectorBitPosition,
|
||
(BatteryMode & CAPACITY_WATTS_MODE)? "10mWH" : "mAH", BatteryMode));
|
||
|
||
if (!(BatteryMode & CAPACITY_WATTS_MODE)) {
|
||
|
||
//
|
||
// Battery not in watts mode, clear valid_mode bit and
|
||
// set the battery into watts mode now
|
||
//
|
||
|
||
BattPrint(BAT_DATA, ("SmbBattVerifyStaticInfo:(%01x) Setting battery to report in 10mWh\n",
|
||
SmbBatt->SelectorBitPosition));
|
||
|
||
SmbBatt->Info.Valid &= ~VALID_MODE;
|
||
BatteryMode |= CAPACITY_WATTS_MODE;
|
||
SmbBattWW(SmbBatt, BAT_BATTERY_MODE, BatteryMode);
|
||
continue; // re-read mode
|
||
}
|
||
}
|
||
|
||
//
|
||
// If other static manufacturer info not known, get it
|
||
//
|
||
|
||
if (!(SmbBatt->Info.Valid & VALID_OTHER)) {
|
||
IoCheck = TRUE;
|
||
SmbBatt->Info.Valid |= VALID_OTHER;
|
||
|
||
SmbBatt->Info.Info.Capabilities = (BATTERY_SYSTEM_BATTERY |
|
||
BATTERY_SET_CHARGE_SUPPORTED |
|
||
BATTERY_SET_DISCHARGE_SUPPORTED);
|
||
SmbBatt->Info.Info.Technology = 1; // secondary cell type
|
||
|
||
// Read chemistry
|
||
SmbBattRB (SmbBatt, BAT_CHEMISTRY, Buffer, &BufferLength);
|
||
if (BufferLength > MAX_CHEMISTRY_LENGTH) {
|
||
ASSERT (BufferLength > MAX_CHEMISTRY_LENGTH);
|
||
BufferLength = MAX_CHEMISTRY_LENGTH;
|
||
}
|
||
RtlZeroMemory (SmbBatt->Info.Info.Chemistry, MAX_CHEMISTRY_LENGTH);
|
||
memcpy (SmbBatt->Info.Info.Chemistry, Buffer, BufferLength);
|
||
|
||
//
|
||
// Voltage and Current scaling information
|
||
//
|
||
|
||
SmbBattRW (SmbBatt, BAT_SPECITICATION_INFO, &tmp);
|
||
BattPrint(BAT_DATA, ("SmbBattVerifyStaticInfo: (%04x) specification info = %x\n",
|
||
SmbBatt->SelectorBitPosition, tmp));
|
||
|
||
switch ((tmp & BATTERY_VSCALE_MASK) >> BATTERY_VSCALE_SHIFT) {
|
||
|
||
case 1:
|
||
SmbBatt->Info.VoltageScale = BSCALE_FACTOR_1;
|
||
break;
|
||
|
||
case 2:
|
||
SmbBatt->Info.VoltageScale = BSCALE_FACTOR_2;
|
||
break;
|
||
|
||
case 3:
|
||
SmbBatt->Info.VoltageScale = BSCALE_FACTOR_3;
|
||
break;
|
||
|
||
case 0:
|
||
default:
|
||
SmbBatt->Info.VoltageScale = BSCALE_FACTOR_0;
|
||
break;
|
||
}
|
||
|
||
switch ((tmp & BATTERY_IPSCALE_MASK) >> BATTERY_IPSCALE_SHIFT) {
|
||
case 1:
|
||
SmbBatt->Info.CurrentScale = BSCALE_FACTOR_1;
|
||
break;
|
||
|
||
case 2:
|
||
SmbBatt->Info.CurrentScale = BSCALE_FACTOR_2;
|
||
break;
|
||
|
||
case 3:
|
||
SmbBatt->Info.CurrentScale = BSCALE_FACTOR_3;
|
||
break;
|
||
|
||
case 0:
|
||
default:
|
||
SmbBatt->Info.CurrentScale = BSCALE_FACTOR_0;
|
||
break;
|
||
}
|
||
|
||
SmbBatt->Info.PowerScale = SmbBatt->Info.CurrentScale * SmbBatt->Info.VoltageScale * 10;
|
||
|
||
//
|
||
// Read DesignCapacity & FullChargeCapacity and multiply them by the
|
||
// scaling factor
|
||
//
|
||
|
||
SmbBattRW(SmbBatt, BAT_DESIGN_CAPACITY, &SmbBatt->Info.Info.DesignedCapacity);
|
||
BattPrint(BAT_DATA, ("SmbBattVerifyStaticInfo: (%01x) DesignCapacity = %04x ... PowerScale = %08x\n",
|
||
SmbBatt->SelectorBitPosition, SmbBatt->Info.Info.DesignedCapacity, SmbBatt->Info.PowerScale));
|
||
SmbBatt->Info.Info.DesignedCapacity *= SmbBatt->Info.PowerScale;
|
||
|
||
SmbBattRW(SmbBatt, BAT_FULL_CHARGE_CAPACITY, &SmbBatt->Info.Info.FullChargedCapacity);
|
||
BattPrint(BAT_DATA, ("SmbBattVerifyStaticInfo: (%01x) FullChargedCapacity = %04x ... PowerScale = %08x\n",
|
||
SmbBatt->SelectorBitPosition, SmbBatt->Info.Info.FullChargedCapacity, SmbBatt->Info.PowerScale));
|
||
SmbBatt->Info.Info.FullChargedCapacity *= SmbBatt->Info.PowerScale;
|
||
|
||
|
||
//
|
||
// Smart batteries have no Use the RemainingCapacityAlarm from the smart battery for the alert values
|
||
//
|
||
|
||
SmbBatt->Info.Info.DefaultAlert1 = 0;
|
||
SmbBatt->Info.Info.DefaultAlert2 = 0;
|
||
|
||
// Critical bias is 0 for smart batteries.
|
||
SmbBatt->Info.Info.CriticalBias = 0;
|
||
|
||
// Manufacturer date
|
||
SmbBattRW (SmbBatt, BAT_MANUFACTURER_DATE, &ManufacturerDate);
|
||
SmbBatt->Info.ManufacturerDate.Day = (UCHAR) ManufacturerDate & 0x1f; // day
|
||
SmbBatt->Info.ManufacturerDate.Month = (UCHAR) (ManufacturerDate >> 5) & 0xf; // month
|
||
SmbBatt->Info.ManufacturerDate.Year = (USHORT) (ManufacturerDate >> 9) + 1980;
|
||
}
|
||
|
||
//
|
||
// If cycle count is not known, read it
|
||
//
|
||
|
||
if (!(SmbBatt->Info.Valid & VALID_CYCLE_COUNT)) {
|
||
IoCheck = TRUE;
|
||
SmbBatt->Info.Valid |= VALID_CYCLE_COUNT;
|
||
|
||
SmbBattRW(SmbBatt, BAT_CYCLE_COUNT, &SmbBatt->Info.Info.CycleCount);
|
||
}
|
||
|
||
//
|
||
// If redundant serial # read hasn't been done, do it now
|
||
//
|
||
|
||
if (!(SmbBatt->Info.Valid & VALID_SANITY_CHECK)) {
|
||
SmbBatt->Info.Valid |= VALID_SANITY_CHECK;
|
||
SmbBattRW(SmbBatt, BAT_SERIAL_NUMBER, &NewInfo.SerialNumber);
|
||
if (SmbBatt->Info.SerialNumber != NewInfo.SerialNumber) {
|
||
SmbBatt->Info.Valid &= ~VALID_TAG_DATA;
|
||
}
|
||
}
|
||
|
||
//
|
||
// If cached info isn't complete, loop
|
||
//
|
||
|
||
} while ((SmbBatt->Info.Valid & VALID_ALL) != VALID_ALL) ;
|
||
|
||
//
|
||
// If the tag isn't assigned, assign it
|
||
//
|
||
|
||
if (!(SmbBatt->Info.Valid & VALID_TAG)) {
|
||
SmbBatt->TagCount += 1;
|
||
SmbBatt->Info.Tag = SmbBatt->TagCount;
|
||
SmbBatt->Info.Valid |= VALID_TAG;
|
||
SmbBatt->AlarmLow.Setting = 0; // assume not set
|
||
SmbBatt->AlarmLow.Skip = 0;
|
||
SmbBatt->AlarmLow.Delta = 0;
|
||
SmbBatt->AlarmLow.AllowedFudge = 0;
|
||
}
|
||
|
||
//
|
||
// If callers BatteryTag does not match current tag, let caller know
|
||
//
|
||
|
||
if (SmbBatt->Info.Tag != BatteryTag) {
|
||
IoCheck = TRUE;
|
||
}
|
||
|
||
//
|
||
// Let caller know if there's been an IoCheck
|
||
//
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattVerifyStaticInfo: EXITING\n"));
|
||
return IoCheck;
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SmbBattInvalidateTag (
|
||
PSMB_BATT_SUBSYSTEM SubsystemExt,
|
||
ULONG BatteryIndex,
|
||
BOOLEAN NotifyClient
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine processes battery insertion/removal by invalidating the
|
||
tag information and then notifies the client of the change.
|
||
|
||
Arguments:
|
||
|
||
SubsystemExt - Device extension for the smart battery subsystem
|
||
|
||
BatteryIndex - Index of Battery to Process Changes for
|
||
- Power and Charge
|
||
|
||
NotifyClient - Whether or not to Notify Client
|
||
|
||
Return Value:
|
||
|
||
None
|
||
--*/
|
||
|
||
{
|
||
PDEVICE_OBJECT batteryPdo;
|
||
PSMB_BATT_PDO batteryPdoExt;
|
||
PDEVICE_OBJECT batteryFdo;
|
||
PSMB_NP_BATT smbNpBatt;
|
||
PSMB_BATT smbBatt;
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattInvalidateTag: ENTERING for battery %x\n", BatteryIndex));
|
||
|
||
batteryPdo = SubsystemExt->BatteryPdoList[BatteryIndex];
|
||
|
||
if (batteryPdo) {
|
||
batteryPdoExt = (PSMB_BATT_PDO) batteryPdo->DeviceExtension;
|
||
batteryFdo = batteryPdoExt->Fdo;
|
||
|
||
if (batteryFdo) {
|
||
|
||
//
|
||
// Invalidate this battery's tag data
|
||
//
|
||
|
||
BattPrint (
|
||
BAT_ALARM,
|
||
("SmbBattInvalidateTag: Battery present status change, invalidating battery %x\n",
|
||
BatteryIndex)
|
||
);
|
||
|
||
smbNpBatt = (PSMB_NP_BATT) batteryFdo->DeviceExtension;
|
||
smbBatt = smbNpBatt->Batt;
|
||
|
||
SmbBattLockDevice (smbBatt);
|
||
|
||
smbBatt->Info.Valid = 0;
|
||
smbBatt->Info.Tag = BATTERY_TAG_INVALID;
|
||
|
||
SmbBattUnlockDevice (smbBatt);
|
||
|
||
//
|
||
// Notify the class driver
|
||
//
|
||
|
||
if (NotifyClient) {
|
||
BattPrint(BAT_ALARM, ("SmbBattInvalidateTag: Status Change notification for battery %x\n", BatteryIndex));
|
||
SmbBattNotifyClassDriver (SubsystemExt, BatteryIndex);
|
||
}
|
||
}
|
||
}
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattInvalidateTag: EXITING\n"));
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SmbBattAlarm (
|
||
IN PVOID Context,
|
||
IN UCHAR Address,
|
||
IN USHORT Data
|
||
)
|
||
{
|
||
PSMB_ALARM_ENTRY newAlarmEntry;
|
||
ULONG compState;
|
||
|
||
PSMB_BATT_SUBSYSTEM subsystemExt = (PSMB_BATT_SUBSYSTEM) Context;
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattAlarm: ENTERING\n"));
|
||
BattPrint(BAT_DATA, ("SmbBattAlarm: Alarm - Address %x, Data %x\n", Address, Data));
|
||
|
||
// If we have a selector and the message is from the address that
|
||
// implements the selector, then handle it. If there is no selector
|
||
// and the message is from the charger, then process it. Or, if the
|
||
// message is from the battery, then process it. Or, in other words,
|
||
// if the message is from the charger and we have a stand-alone selector,
|
||
// then ignore the message.
|
||
|
||
if (Address != SMB_BATTERY_ADDRESS) {
|
||
if ((subsystemExt->SelectorPresent) && (subsystemExt->Selector)) {
|
||
|
||
// If a Selector is implemented and the Alarm Message came from
|
||
// something besides the Selector or a Battery, then ignore it.
|
||
|
||
if (Address != subsystemExt->Selector->SelectorAddress) {
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Allocate a new alarm list structure. This has to be from non-paged pool
|
||
// because we are being called at Dispatch level.
|
||
//
|
||
|
||
newAlarmEntry = ExAllocatePoolWithTag (NonPagedPool, sizeof (SMB_ALARM_ENTRY), 'StaB');
|
||
if (!newAlarmEntry) {
|
||
BattPrint (BAT_ERROR, ("SmbBattAlarm: couldn't allocate alarm structure\n"));
|
||
return;
|
||
}
|
||
|
||
newAlarmEntry->Data = Data;
|
||
newAlarmEntry->Address = Address;
|
||
|
||
//
|
||
// Add this alarm to the alarm queue
|
||
//
|
||
|
||
ExInterlockedInsertTailList(
|
||
&subsystemExt->AlarmList,
|
||
&newAlarmEntry->Alarms,
|
||
&subsystemExt->AlarmListLock
|
||
);
|
||
|
||
//
|
||
// Add 1 to the WorkerActive value, if this is the first count
|
||
// queue a worker thread
|
||
//
|
||
|
||
if (InterlockedIncrement(&subsystemExt->WorkerActive) == 1) {
|
||
IoQueueWorkItem (subsystemExt->WorkerThread, SmbBattWorkerThread, DelayedWorkQueue, subsystemExt);
|
||
}
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattAlarm: EXITING\n"));
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SmbBattWorkerThread (
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PVOID Context
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine handles the alarms for the batteries.
|
||
|
||
Arguments:
|
||
|
||
Context - Non-paged extension for the battery subsystem FDO
|
||
|
||
Return Value:
|
||
|
||
None
|
||
--*/
|
||
{
|
||
PSMB_ALARM_ENTRY alarmEntry;
|
||
PLIST_ENTRY nextEntry;
|
||
ULONG selectorState;
|
||
ULONG batteryIndex;
|
||
|
||
BOOLEAN charging = FALSE;
|
||
BOOLEAN acOn = FALSE;
|
||
|
||
PSMB_BATT_SUBSYSTEM subsystemExt = (PSMB_BATT_SUBSYSTEM) Context;
|
||
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattWorkerThread: ENTERING\n"));
|
||
|
||
do {
|
||
|
||
//
|
||
// Check to see if we have more alarms to process. If so retrieve
|
||
// the next one and decrement the worker active count.
|
||
//
|
||
|
||
nextEntry = ExInterlockedRemoveHeadList(
|
||
&subsystemExt->AlarmList,
|
||
&subsystemExt->AlarmListLock
|
||
);
|
||
|
||
//
|
||
//It should only get here if there is an entry in the list
|
||
//
|
||
ASSERT (nextEntry != NULL);
|
||
|
||
alarmEntry = CONTAINING_RECORD (nextEntry, SMB_ALARM_ENTRY, Alarms);
|
||
|
||
BattPrint(
|
||
BAT_ALARM,
|
||
("SmbBattWorkerThread: processing alarm, address = %x, data = %x\n",
|
||
alarmEntry->Address,
|
||
alarmEntry->Data)
|
||
);
|
||
|
||
//
|
||
// Get last Selector State Cached Value (update cache with new value)
|
||
//
|
||
|
||
if (subsystemExt->SelectorPresent) {
|
||
if (subsystemExt->Selector) {
|
||
selectorState = subsystemExt->Selector->SelectorState;
|
||
} else {
|
||
// We're not initialized enough to handle a message
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Determine Source of Alarm Message and then Process it
|
||
|
||
switch (alarmEntry->Address) {
|
||
|
||
case SMB_CHARGER_ADDRESS:
|
||
|
||
//
|
||
// Handle Charger Message - If Charger/Selector Combo, then
|
||
// fall through and handle it as a Selector. If no Selector,
|
||
// then attempt processing as a Charger. Ignore Charger messages
|
||
// if Selector is present.
|
||
//
|
||
|
||
if (!subsystemExt->SelectorPresent) {
|
||
|
||
SmbBattProcessChargerAlarm (subsystemExt, alarmEntry->Data);
|
||
break;
|
||
|
||
} else {
|
||
|
||
// If SelectorPresent, but no Selector Structure, Ignore message
|
||
if (!subsystemExt->Selector) {
|
||
break;
|
||
} else {
|
||
if (subsystemExt->Selector->SelectorAddress != SMB_CHARGER_ADDRESS) {
|
||
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Fall through to Selector Procesing for integrated Charger/Selector
|
||
//
|
||
|
||
case SMB_SELECTOR_ADDRESS:
|
||
|
||
if (!subsystemExt->SelectorPresent) {
|
||
BattPrint (
|
||
BAT_BIOS_ERROR,
|
||
("SmbBattProcessSelectorAlarm: Received alarm from selector address, but BIOS reports no selector is present.\n")
|
||
);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// This is a selector alarm indicating a change in the selector state.
|
||
// There are four different areas that could change: SMB, POWER_BY,
|
||
// CHARGE, PRESENT. First try to determine which ones changed.
|
||
//
|
||
|
||
SmbBattLockSelector (subsystemExt->Selector);
|
||
BattPrint (
|
||
BAT_DATA,
|
||
("SmbBattProcessSelectorAlarm: New SelectorState being written as - %x\n",
|
||
alarmEntry->Data)
|
||
);
|
||
subsystemExt->Selector->SelectorState = alarmEntry->Data;
|
||
SmbBattUnlockSelector (subsystemExt->Selector);
|
||
|
||
SmbBattProcessSelectorAlarm (subsystemExt, selectorState, alarmEntry->Data);
|
||
break;
|
||
|
||
case SMB_BATTERY_ADDRESS:
|
||
|
||
//
|
||
// Send notifications to all batteries.
|
||
// It seems we get notifications even for batteries not currently selected.
|
||
//
|
||
|
||
for (batteryIndex = 0; batteryIndex < subsystemExt->NumberOfBatteries; batteryIndex++) {
|
||
BattPrint(BAT_ALARM, ("SmbBattWorkerThread: Notification to battery %x\n", batteryIndex));
|
||
SmbBattNotifyClassDriver (subsystemExt, batteryIndex);
|
||
}
|
||
break;
|
||
|
||
} // switch (alarmEntry->Address)
|
||
|
||
//
|
||
// Free the alarm structure
|
||
//
|
||
|
||
ExFreePool (alarmEntry);
|
||
|
||
} while (InterlockedDecrement (&subsystemExt->WorkerActive) != 0);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattWorkerThread: EXITING\n"));
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SmbBattProcessSelectorAlarm (
|
||
IN PSMB_BATT_SUBSYSTEM SubsystemExt,
|
||
IN ULONG OldSelectorState,
|
||
IN ULONG NewSelectorState
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine process alarms generated by the selector. We will only get
|
||
here is there is a change in the SelectorState for one or more of the
|
||
following state changes:
|
||
|
||
- PowerBy nibble change caused by the power source changing to a different
|
||
battery or to AC adapter.
|
||
|
||
- PowerBy nbble change caused by optional Charge Indicator state change
|
||
when the charger starts or stops charging a battery.
|
||
|
||
- ChargeWhich nibble change caused by a change in the current battery
|
||
connected to the Charger.
|
||
|
||
- ChargeWhich nibble change caused by AC adapter insertion/removal.
|
||
|
||
- BatteryPresent nibble change caused by insertion/removal of 1 or more batteries.
|
||
|
||
Arguments:
|
||
|
||
SubsystemExt - Device extension for the smart battery subsystem
|
||
|
||
NewSelectorState - Data value sent by the selector alarm, which is
|
||
New SelectorState.
|
||
|
||
Return Value:
|
||
|
||
None
|
||
--*/
|
||
{
|
||
ULONG tmp;
|
||
ULONG BatteryMask;
|
||
ULONG NotifyMask;
|
||
ULONG ChangeSelector;
|
||
ULONG index;
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattProcessSelectorAlarm: ENTERING\n"));
|
||
|
||
//
|
||
// Determine SelectorState changes and combine them together
|
||
//
|
||
|
||
ChangeSelector = NewSelectorState ^ OldSelectorState;
|
||
|
||
if (!(ChangeSelector & ~SELECTOR_STATE_SMB_MASK)) {
|
||
//
|
||
// If the only change is in SMB nibble, no nothing.
|
||
//
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Check for change to Batteries Present nibble. Invalidate and Notify
|
||
// each battery that changes state.
|
||
//
|
||
BatteryMask = ChangeSelector & SELECTOR_STATE_PRESENT_MASK;
|
||
NotifyMask = BatteryMask;
|
||
|
||
//
|
||
// Check for change to Charge Which nibble. If all bits set, then the nibble
|
||
// just inverted state indicating an AC adapter Insertion/Removal. Notify
|
||
// all batteries of change. If only one or two bits changed, notify just
|
||
// the batteries that changed status.
|
||
//
|
||
|
||
tmp = (ChangeSelector >> SELECTOR_SHIFT_CHARGE) & SELECTOR_STATE_PRESENT_MASK;
|
||
if (tmp) {
|
||
|
||
// If nibble inverted state, then the AC Adapter changed state
|
||
|
||
if (tmp == SELECTOR_STATE_PRESENT_MASK) {
|
||
|
||
BattPrint(BAT_DATA, ("SmbBattProcessSelectorAlarm: AC Adapter was inserted/removed\n"));
|
||
NotifyMask |= tmp;
|
||
|
||
// Battery Charge Which changed
|
||
|
||
} else {
|
||
//if (SmbBattReverseLogic(SubsystemExt->Selector, tmp)) {
|
||
// tmp ^= SELECTOR_STATE_PRESENT_MASK;
|
||
//}
|
||
|
||
// Let's just notify all batteries to be safe
|
||
|
||
tmp = SELECTOR_STATE_PRESENT_MASK;
|
||
BattPrint(BAT_DATA, ("SmbBattProcessSelectorAlarm: Charger Nibble changed status\n"));
|
||
NotifyMask |= tmp;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check for change in Power By nibble. Notify all batteries of any change.
|
||
//
|
||
|
||
tmp = (ChangeSelector >> SELECTOR_SHIFT_POWER) & SELECTOR_STATE_PRESENT_MASK;
|
||
if (tmp) {
|
||
|
||
// If nibble inverted state, then the check for Charge Indicator change
|
||
// and notify the battery that started/stopped charging
|
||
|
||
if (tmp == SELECTOR_STATE_PRESENT_MASK) {
|
||
if (SubsystemExt->Selector->SelectorInfo & SELECTOR_INFO_CHARGING_INDICATOR_BIT) {
|
||
tmp = (NewSelectorState >> SELECTOR_SHIFT_CHARGE) & SELECTOR_STATE_PRESENT_MASK;
|
||
if (SmbBattReverseLogic(SubsystemExt->Selector, tmp)) {
|
||
tmp ^= SELECTOR_STATE_PRESENT_MASK;
|
||
}
|
||
BattPrint(BAT_DATA, ("SmbBattProcessSelectorAlarm: Charging Indicator changed status\n"));
|
||
NotifyMask |= tmp;
|
||
} else {
|
||
// If not Charging Indicator, then all battery power by nibbles inverted
|
||
BattPrint(BAT_DATA, ("SmbBattProcessSelectorAlarm: Power By inverted state, without supporting Charge Indication\n"));
|
||
NotifyMask |= SELECTOR_STATE_PRESENT_MASK;
|
||
}
|
||
|
||
} else {
|
||
|
||
// Let's notify all batteries if the Power By nibble changes
|
||
|
||
BattPrint(BAT_DATA, ("SmbBattProcessSelectorAlarm: Power By Nibble changed status\n"));
|
||
NotifyMask |= SELECTOR_STATE_PRESENT_MASK;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Notify all batteries of change in Present Status
|
||
//
|
||
|
||
tmp = BATTERY_A_PRESENT;
|
||
for (index = 0; index < SubsystemExt->NumberOfBatteries; index++) {
|
||
if (BatteryMask & tmp) {
|
||
if (!(NewSelectorState & tmp)) {
|
||
BattPrint(BAT_DATA, ("SmbBattProcessSelectorAlarm: Invalidating battery %x\n", index));
|
||
SmbBattInvalidateTag (SubsystemExt, index, FALSE);
|
||
}
|
||
}
|
||
tmp <<= 1;
|
||
}
|
||
|
||
// Don't notify batteries already notified
|
||
//NotifyMask &= ~BatteryMask;
|
||
|
||
//
|
||
// Process Notifications Now for changes to SelectorState for Power, Charge, etc.
|
||
//
|
||
|
||
tmp = BATTERY_A_PRESENT;
|
||
for (index = 0; index < SubsystemExt->NumberOfBatteries; index++) {
|
||
if (NotifyMask & tmp) {
|
||
BattPrint(BAT_DATA, ("SmbBattProcessSelectorAlarm: Status Change notification for battery %x\n", index));
|
||
SmbBattNotifyClassDriver (SubsystemExt, index);
|
||
}
|
||
tmp <<= 1;
|
||
}
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattProcessSelectorAlarm: EXITING\n"));
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SmbBattProcessChargerAlarm (
|
||
IN PSMB_BATT_SUBSYSTEM SubsystemExt,
|
||
IN ULONG ChargerStatus
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine process alarms generated by the charger. We will only get
|
||
here is there is no selector in the system and the alarm is the one to
|
||
tell us when AC and batteries come and go.
|
||
|
||
Arguments:
|
||
|
||
SubsystemExt - Device extension for the smart battery subsystem
|
||
|
||
ChargerStatus - Data value sent by the charger alarm, which is
|
||
charger status register
|
||
|
||
Return Value:
|
||
|
||
None
|
||
--*/
|
||
{
|
||
BattPrint(BAT_TRACE, ("SmbBattProcessChargeAlarm: ENTERING\n"));
|
||
|
||
//
|
||
// There should only be two reasons we get called: a change in AC, or
|
||
// a change in the battery present. We are really only interested in
|
||
// whether or not there is a battery in the system. If there isn't, we
|
||
// will invalidate battery 0. If it was already gone this re-invalidation
|
||
// won't matter.
|
||
//
|
||
|
||
if (!(ChargerStatus & CHARGER_STATUS_BATTERY_PRESENT_BIT)) {
|
||
SmbBattInvalidateTag (SubsystemExt, BATTERY_A, TRUE);
|
||
}
|
||
|
||
// Notify Change
|
||
|
||
SmbBattNotifyClassDriver (SubsystemExt, 0);
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattProcessChargerAlarm: EXITING\n"));
|
||
}
|
||
|
||
|
||
|
||
|
||
VOID
|
||
SmbBattNotifyClassDriver (
|
||
IN PSMB_BATT_SUBSYSTEM SubsystemExt,
|
||
IN ULONG BatteryIndex
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine gets the FDO for the battery indicated by index from the
|
||
smart battery subsystem and notifies the class driver there has been
|
||
a status change.
|
||
|
||
Arguments:
|
||
|
||
SubsystemExt - Device extension for the smart battery subsystem
|
||
|
||
BatteryIndex - Index for the battery in the subsystem battery
|
||
list
|
||
|
||
Return Value:
|
||
|
||
None
|
||
--*/
|
||
{
|
||
PDEVICE_OBJECT batteryPdo;
|
||
PSMB_BATT_PDO batteryPdoExt;
|
||
PDEVICE_OBJECT batteryFdo;
|
||
PSMB_NP_BATT smbNpBatt;
|
||
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattNotifyClassDriver: ENTERING\n"));
|
||
|
||
batteryPdo = SubsystemExt->BatteryPdoList[BatteryIndex];
|
||
if (batteryPdo) {
|
||
batteryPdoExt = (PSMB_BATT_PDO) batteryPdo->DeviceExtension;
|
||
batteryFdo = batteryPdoExt->Fdo;
|
||
|
||
if (batteryFdo) {
|
||
BattPrint (
|
||
BAT_IRPS,
|
||
("SmbBattNotifyClassDriver: Calling BatteryClassNotify for battery - %x\n",
|
||
batteryFdo)
|
||
);
|
||
|
||
smbNpBatt = (PSMB_NP_BATT) batteryFdo->DeviceExtension;
|
||
|
||
BatteryClassStatusNotify (smbNpBatt->Class);
|
||
}
|
||
} else {
|
||
BattPrint (
|
||
BAT_ERROR,
|
||
("SmbBattNotifyClassDriver: No PDO for device.\n")
|
||
);
|
||
}
|
||
|
||
BattPrint(BAT_TRACE, ("SmbBattNotifyClassDriver: EXITING\n"));
|
||
}
|