/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    battmisc.c

Abstract:

    Miscellaneous functions needed by the composite battery to talk to
    the batteries in the system.

Author:

    Scott Brenden

Environment:

Notes:


Revision History:

--*/

#include "compbatt.h"



#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, BatteryIoctl)
#pragma alloc_text(PAGE, CompBattGetDeviceObjectPointer)
#endif



NTSTATUS
BattIoctlComplete (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{
    PKEVENT         Event;

    Event = (PKEVENT) Context;
    KeSetEvent (Event, 0, FALSE);
    return STATUS_MORE_PROCESSING_REQUIRED;
}




NTSTATUS
BatteryIoctl(
    IN ULONG            Ioctl,
    IN PDEVICE_OBJECT   DeviceObject,
    IN PVOID            InputBuffer,
    IN ULONG            InputBufferLength,
    IN PVOID            OutputBuffer,
    IN ULONG            OutputBufferLength,
    IN BOOLEAN          PrivateIoctl
    )
/*++

Routine Description:

    The routine Creates an IRP and does Ioctl to the device object passed in.

Arguments:

    Ioctl               - code for the Ioctl

    DeviceObject        - Device object to send the ioctl to

    InputBuffer         - Input buffer for the ioctl

    InputBufferLength   - length of the input buffer

    OutputBuffer        - Buffer for containing the results of the ioctl

    OutputBufferLength  - Length of the output buffer

    PrivateIoctl        - TRUE if this is to be an IRP_MJ_DEVICE_CONTROL, FALSE
                          if this is to be an IRP_MJ_INTERNAL_DEVICE_CONTROL.

Return Value:

    Status returned by the Ioctl

--*/
{
    NTSTATUS                status;
    IO_STATUS_BLOCK         IOSB;
    PIRP                    irp;
    KEVENT                 event;
    // PUCHAR                  buffer;
    // ULONG                   bufferSize;
    // PIO_STACK_LOCATION      irpSp;


    PAGED_CODE();

    BattPrint (BATT_TRACE, ("CompBatt: ENTERING BatteryIoctl\n"));

    //
    // Set the event object to the unsignaled state.
    // It will be used to signal request completion.
    //

    KeInitializeEvent(&event, SynchronizationEvent, FALSE);

    //
    // Build synchronous request with no transfer.
    //

    irp = IoBuildDeviceIoControlRequest(
                Ioctl,
                DeviceObject,
                InputBuffer,
                InputBufferLength,
                OutputBuffer,
                OutputBufferLength,
                PrivateIoctl,
                &event,
                &IOSB
                   );

    if (irp == NULL) {
        BattPrint (BATT_ERROR, ("BatteryIoctl: couldn't create Irp\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }


    //
    // Pass request to port driver and wait for request to complete.
    //

    status = IoCallDriver(DeviceObject, irp);

    if (status == STATUS_PENDING) {
        KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
        status = IOSB.Status;
    }

    if (!NT_SUCCESS(status)) {
        BattPrint (BATT_ERROR, ("BatteryIoctl: Irp failed - %x\n", status));
    }

    BattPrint (BATT_TRACE, ("CompBatt: EXITING BatteryIoctl\n"));

    return status;
}




BOOLEAN
IsBatteryAlreadyOnList(
    IN PUNICODE_STRING      SymbolicLinkName,
    IN PCOMPOSITE_BATTERY   CompBatt
    )
/*++

Routine Description:

    The routine runs through the list of batteries the composite keeps and checks
    to see if the symbolic link name passed in matches one of them.

Arguments:

    SymbolicLinkName    - Name for battery to check if already on list


Return Value:

    TRUE if the SymbolicLinkName belongs to a battery already on the list, FALSE
    otherwise.
--*/
{
    PCOMPOSITE_ENTRY        batt;
    PLIST_ENTRY             entry;

    BattPrint (BATT_TRACE, ("CompBatt: ENTERING IsBatteryAlreadyOnList\n"));

    //
    // Run through the list of batteries looking for new batteries
    //

    ExAcquireFastMutex (&CompBatt->ListMutex);
    for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries;  entry = entry->Flink) {

        batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);

        if (!RtlCompareUnicodeString(SymbolicLinkName, &batt->BattName, TRUE)) {
            //
            // The battery is already on the list
            //

            ExReleaseFastMutex (&CompBatt->ListMutex);
            return TRUE;
        }
    }

    BattPrint (BATT_TRACE, ("CompBatt: EXITING IsBatteryAlreadyOnList\n"));

    ExReleaseFastMutex (&CompBatt->ListMutex);

    return FALSE;
}

PCOMPOSITE_ENTRY
RemoveBatteryFromList(
    IN PUNICODE_STRING      SymbolicLinkName,
    IN PCOMPOSITE_BATTERY   CompBatt
    )
/*++

Routine Description:

    The routine runs through the list of batteries the composite keeps and checks
    to see if the symbolic link name passed in matches one of them.  If a match is found,
    the entry is removed from the list of batteries

Arguments:

    SymbolicLinkName    - Name for battery to check if already on list


Return Value:

    TRUE if the SymbolicLinkName was found and deleted. FALSE otherwise.
--*/
{
    PCOMPOSITE_ENTRY        batt;
    PLIST_ENTRY             entry;

    BattPrint (BATT_TRACE, ("CompBatt: ENTERING RemoveBatteryFromList\n"));

    //
    // Run through the list of batteries looking for new batteries
    //

    ExAcquireFastMutex (&CompBatt->ListMutex);
    for (entry = CompBatt->Batteries.Flink; entry != &CompBatt->Batteries;  entry = entry->Flink) {

        batt = CONTAINING_RECORD (entry, COMPOSITE_ENTRY, Batteries);

        if (!RtlCompareUnicodeString(SymbolicLinkName, &batt->BattName, TRUE)) {
            //
            // The battery is on the list, remove
            //

            //
            // Wait until no one else is looking at this battery before removing it.
            //

            CompbattAcquireDeleteLock (&batt->DeleteLock);

            ExReleaseFastMutex (&CompBatt->ListMutex);

            CompbattReleaseDeleteLockAndWait (&batt->DeleteLock);


            ExAcquireFastMutex (&CompBatt->ListMutex);
            RemoveEntryList(entry);
            ExReleaseFastMutex (&CompBatt->ListMutex);

            return batt;
        }
    }

    ExReleaseFastMutex (&CompBatt->ListMutex);

    BattPrint (BATT_TRACE, ("CompBatt: EXITING RemoveBatteryFromList\n"));

    return NULL;
}

NTSTATUS
CompBattGetDeviceObjectPointer(
    IN PUNICODE_STRING ObjectName,
    IN ACCESS_MASK DesiredAccess,
    OUT PFILE_OBJECT *FileObject,
    OUT PDEVICE_OBJECT *DeviceObject
    )

/*++

Routine Description:

    This routine is essentially a copy from ntos\io\iosubs.c
    The reason for this is that we need to open the device with shared access
    rather than exclusive access.  In addition  ZwCreateFile was used instead of
    ZwOpenFile becuase that didn't seem to complie right when only wdm.h instead
    of ntddk.h was included.

    This routine returns a pointer to the device object specified by the
    object name.  It also returns a pointer to the referenced file object
    that has been opened to the device that ensures that the device cannot
    go away.

    To close access to the device, the caller should dereference the file
    object pointer.

Arguments:

    ObjectName - Name of the device object for which a pointer is to be
        returned.

    DesiredAccess - Access desired to the target device object.

    FileObject - Supplies the address of a variable to receive a pointer
        to the file object for the device.

    DeviceObject - Supplies the address of a variable to receive a pointer
        to the device object for the specified device.

Return Value:

    The function value is a referenced pointer to the specified device
    object, if the device exists.  Otherwise, NULL is returned.

--*/

{
    PFILE_OBJECT fileObject;
    OBJECT_ATTRIBUTES objectAttributes;
    HANDLE fileHandle;
    IO_STATUS_BLOCK ioStatus;
    NTSTATUS status;

    PAGED_CODE();

    //
    // Initialize the object attributes to open the device.
    //

    InitializeObjectAttributes( &objectAttributes,
                                ObjectName,
                                0,
                                (HANDLE) NULL,
                                (PSECURITY_DESCRIPTOR) NULL );

    status = ZwCreateFile (
                    &fileHandle,
                    DesiredAccess,     // desired access
                    &objectAttributes,
                    &ioStatus,
                    (PLARGE_INTEGER) NULL,
                    0L,
                    FILE_SHARE_READ | FILE_SHARE_WRITE, // share access
                    FILE_OPEN,
                    0,
                    NULL,
                    0);

    if (NT_SUCCESS( status )) {

        //
        // The open operation was successful.  Dereference the file handle
        // and obtain a pointer to the device object for the handle.
        //

        status = ObReferenceObjectByHandle( fileHandle,
                                            0,
                                            *IoFileObjectType,
                                            KernelMode,
                                            (PVOID *) &fileObject,
                                            NULL );
        if (NT_SUCCESS( status )) {

            *FileObject = fileObject;

            //
            // Get a pointer to the device object for this file.
            //
            *DeviceObject = IoGetRelatedDeviceObject( fileObject );
        }

        (VOID) ZwClose( fileHandle );
    }

    return status;
}

//
// Delete Lock routines from io\remlock.c
//

VOID
CompbattInitializeDeleteLock (
        IN PCOMPBATT_DELETE_LOCK Lock
        )
{
    Lock->Deleted = FALSE;
    Lock->RefCount = 1;
    KeInitializeEvent(&Lock->DeleteEvent,
                      SynchronizationEvent,
                      FALSE);

}

NTSTATUS
CompbattAcquireDeleteLock (
        IN PCOMPBATT_DELETE_LOCK Lock
        )
{
    LONG        lockValue;

    lockValue = InterlockedIncrement(&Lock->RefCount);

    if (Lock->Deleted) {
        if (0 == InterlockedDecrement (&Lock->RefCount)) {
            KeSetEvent (&Lock->DeleteEvent, 0, FALSE);
        }

        return STATUS_DELETE_PENDING;
    }

    return STATUS_SUCCESS;

}

VOID
CompbattReleaseDeleteLock (
        IN PCOMPBATT_DELETE_LOCK Lock
        )
{
    if (0 == InterlockedDecrement(&Lock->RefCount)) {

        KeSetEvent(&Lock->DeleteEvent,
                   IO_NO_INCREMENT,
                   FALSE);
    }
}

VOID
CompbattReleaseDeleteLockAndWait (
        IN PCOMPBATT_DELETE_LOCK Lock
        )
{
    Lock->Deleted = TRUE;

    InterlockedDecrement (&Lock->RefCount);

    if (0 < InterlockedDecrement (&Lock->RefCount)) {
        KeWaitForSingleObject (&Lock->DeleteEvent,
                               Executive,
                               KernelMode,
                               FALSE,
                               NULL);
    }
}