/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    cmhndlr.c

Abstract:

    Control Method Battery handlers

Author:

    Bob Moore

Environment:

    Kernel mode

Revision History:

--*/

#include "CmBattp.h"


VOID
CmBattPowerCallBack(
    IN  PVOID   CallBackContext,
    IN  PVOID   Argument1,
    IN  PVOID   Argument2
    )
/*++

Routine Description:

    This routine is called when the system changes power states

Arguments:

    CallBackContext - The device extension for the root device
    Argument1

--*/
{

    PDRIVER_OBJECT  CmBattDriver = (PDRIVER_OBJECT) CallBackContext;
    ULONG           action = PtrToUlong( Argument1 );
    ULONG           value  = PtrToUlong( Argument2 );
    BOOLEAN         timerCanceled;
    PDEVICE_OBJECT  CmBattDevice;
    PCM_BATT        CmBatt;

    CmBattPrint (CMBATT_POWER, ("CmBattPowerCallBack: action: %d, value: %d \n", action, value));

    //
    // We are looking for a PO_CB_SYSTEM_STATE_LOCK
    //
    if (action != PO_CB_SYSTEM_STATE_LOCK) {
        return;
    }

    switch (value) {
    case 0:
        CmBattPrint (CMBATT_POWER, ("CmBattPowerCallBack: Delaying Notifications\n"));
        //
        // Get the head of the DeviceObject list
        //

        CmBattDevice = CmBattDriver->DeviceObject;

        while (CmBattDevice) {
            CmBatt = CmBattDevice->DeviceExtension;

            //
            // Cause all notifications to be delayed.
            //
            CmBatt->Sleeping = TRUE;

            CmBattDevice = CmBattDevice->NextDevice;

        }
        break;

    case 1:
        CmBattPrint (CMBATT_POWER, ("CmBattPowerCallBack: Calling CmBattWakeDpc after 10 seconds.\n"));
        timerCanceled = KeSetTimer (&CmBattWakeDpcTimerObject,
                    CmBattWakeDpcDelay,
                    &CmBattWakeDpcObject);
        CmBattPrint (CMBATT_POWER, ("CmBattPowerCallBack: timerCanceled = %d.\n", timerCanceled));
        break;

    default:
        CmBattPrint (CMBATT_POWER, ("CmBattPowerCallBack: unknown argument2 = %08x\n", value));


    }

}

VOID
CmBattWakeDpc (
    IN  PKDPC   Dpc,
    IN  PVOID   DeferredContext,
    IN  PVOID   SystemArgument1,
    IN  PVOID   SystemArgument2
    )
/*++

Routine Description:

    This routine is called X seconds after the system wakes to proces
    all delayed battery notifications.

Arguments:

    CmBattDriver - Driver object

Return Value:

    None

--*/

{
    PDRIVER_OBJECT  CmBattDriver = (PDRIVER_OBJECT) DeferredContext;
    BOOLEAN         notifyAll = FALSE;
    PDEVICE_OBJECT  CmBattDevice;
    PCM_BATT        CmBatt;

    CmBattPrint (CMBATT_TRACE, ("CmBattWakeDpc: Entered.\n"));
    //
    // Get the head of the DeviceObject list
    //

    CmBattDevice = CmBattDriver->DeviceObject;

    while (CmBattDevice) {
        CmBatt = CmBattDevice->DeviceExtension;

        //
        // We will now process all delayed notifications.
        // For effeiciency, we must go through the devices twice:
        // first to see if any AC devices have been notified, and
        // then to send notifications to all battery devices if necessary.
        //
        CmBatt->Sleeping = FALSE;

        if ((CmBatt->Type == AC_ADAPTER_TYPE) &&
            (CmBatt->ActionRequired & CMBATT_AR_NOTIFY)) {

            //
            // If any AC adapter devices have notified,
            // then we need to notify all battery devices
            //
            CmBattPrint (CMBATT_PNP, ("CmBattWakeDpc: AC adapter notified\n"));
            notifyAll = TRUE;
            CmBatt->ActionRequired = CMBATT_AR_NO_ACTION;
        }

        CmBattDevice = CmBattDevice->NextDevice;

    }

    //
    // Get the head of the DeviceObject list
    //

    CmBattDevice = CmBattDriver->DeviceObject;

    // Walk the list
    while (CmBattDevice) {
        CmBatt = CmBattDevice->DeviceExtension;

        if (CmBatt->Type == CM_BATTERY_TYPE) {
            CmBattPrint (CMBATT_PNP, ("CmBattWakeDpc: Performing delayed ARs: %01x\n", CmBatt->ActionRequired));

            if (CmBatt->ActionRequired & CMBATT_AR_INVALIDATE_CACHE) {
                InterlockedExchange (&CmBatt->CacheState, 0);
            }
            if (CmBatt->ActionRequired & CMBATT_AR_INVALIDATE_TAG) {
                CmBatt->Info.Tag = BATTERY_TAG_INVALID;
            }
            if ((CmBatt->ActionRequired & CMBATT_AR_NOTIFY) || notifyAll) {
                BatteryClassStatusNotify (CmBatt->Class);
            }
        }

        CmBattDevice = CmBattDevice->NextDevice;

    }


}

VOID
CmBattNotifyHandler (
    IN PVOID            Context,
    IN ULONG            NotifyValue
    )
/*++

Routine Description:

    This routine fields battery device notifications from the ACPI driver.

Arguments:


Return Value:

    None

--*/
{
    PCM_BATT            CmBatt = Context;
    PDRIVER_OBJECT      CmBatteryDriver;
    PDEVICE_OBJECT      CmBatteryDevice;
    PCM_BATT            CmBatteryExtension;

    CmBattPrint ((CMBATT_PNP | CMBATT_BIOS), ("CmBattNotifyHandler: CmBatt 0x%08x Type %d Number %d Notify Value: %x\n",
                                CmBatt, CmBatt->Type, CmBatt->DeviceNumber, NotifyValue));

    switch (NotifyValue) {

        case BATTERY_DEVICE_CHECK:
            //
            // A new battery was inserted in the system.
            //

            CmBatt->ActionRequired |= CMBATT_AR_NOTIFY;
            CmBatt->ActionRequired |= CMBATT_AR_INVALIDATE_CACHE;

            //
            // This notification is only received when a battery is inserted.
            // It also occurs after restart from hibernation on some machines.
            // Invalidate battery tag.
            //

            if (CmBatt->Info.Tag != BATTERY_TAG_INVALID) {
                CmBattPrint ((CMBATT_ERROR),
                   ("CmBattNotifyHandler: Received battery #%x insertion, but tag was not invalid.\n",
                    CmBatt->DeviceNumber));
            }

            break;


        case BATTERY_EJECT:
            //
            // A battery was removed from the system
            //

            CmBatt->ActionRequired |= CMBATT_AR_NOTIFY;

            //
            // Invalidate the battery tag and all cached informaion
            // whenever this message is received.
            //
            CmBatt->ActionRequired |= CMBATT_AR_INVALIDATE_CACHE;
            CmBatt->ActionRequired |= CMBATT_AR_INVALIDATE_TAG;

            break;

        case BATTERY_STATUS_CHANGE:                 // Status change only
            CmBatt->ActionRequired |= CMBATT_AR_NOTIFY;

            break;

        case BATTERY_INFO_CHANGE:                   // Info & status change
            CmBatt->ActionRequired |= CMBATT_AR_NOTIFY;
            CmBatt->ActionRequired |= CMBATT_AR_INVALIDATE_CACHE;
            CmBatt->ActionRequired |= CMBATT_AR_INVALIDATE_TAG;
            break;

        default:

            CmBattPrint (CMBATT_PNP, ("CmBattNotifyHandler: Unknown Notify Value: %x\n", NotifyValue));
            break;

    }

    if (CmBatt->Sleeping) {
        CmBattPrint (CMBATT_PNP, ("CmBattNotifyHandler: Notification delayed: ARs = %01x\n", CmBatt->ActionRequired));
    } else {
        CmBattPrint (CMBATT_PNP, ("CmBattNotifyHandler: Performing ARs: %01x\n", CmBatt->ActionRequired));
        if (CmBatt->Type == CM_BATTERY_TYPE) {
            
            //
            // Invalidate last trip point set on battery.
            //
            CmBatt->Alarm.Setting = CM_ALARM_INVALID;
            
            if (CmBatt->ActionRequired & CMBATT_AR_INVALIDATE_CACHE) {
                InterlockedExchange (&CmBatt->CacheState, 0);
            }
            if (CmBatt->ActionRequired & CMBATT_AR_INVALIDATE_TAG) {
                CmBatt->Info.Tag = BATTERY_TAG_INVALID;
            }
            if (CmBatt->ActionRequired & CMBATT_AR_NOTIFY) {
                CmBatt->ReCheckSta = TRUE;
                BatteryClassStatusNotify (CmBatt->Class);                
            }

        } else if ((CmBatt->Type == AC_ADAPTER_TYPE) &&
                    (CmBatt->ActionRequired & CMBATT_AR_NOTIFY)) {

            //
            // Get the Driver Object
            //

            CmBatteryDriver = CmBatt->Fdo->DriverObject;

            //
            // Get the head of the DeviceObject list
            //

            CmBatteryDevice = CmBatteryDriver->DeviceObject;

            //
            // Walk the DeviceObject list to notify the class driver on all batteries
            //
            while (CmBatteryDevice) {

                CmBatteryExtension = CmBatteryDevice->DeviceExtension;

                if (CmBatteryExtension->Type == CM_BATTERY_TYPE) {
                    CmBatteryExtension->ReCheckSta = TRUE;
                    BatteryClassStatusNotify (CmBatteryExtension->Class);
                }

                CmBatteryDevice = CmBatteryDevice->NextDevice;
            }
        }

        CmBatt->ActionRequired = CMBATT_AR_NO_ACTION;
    }
}