/*++

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);
}