/*++

Copyright (c) 1996-2000 Microsoft Corporation

Module Name:

    dispatch.c

Abstract:

    This module contains dispatch code for PCI.SYS.

Author:

    Ken Reneris (kenr) 4-Dec-1997

Revision History:

--*/

#include "pcip.h"

#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PciLocalDeviceUsage)
#pragma alloc_text(PAGE, PciPdoDeviceUsage)
#endif


NTSTATUS
PciLocalDeviceUsage (
    IN PPCI_POWER_STATE     PowerState,
    IN PIRP                 Irp
    )
{
    PIO_STACK_LOCATION  irpSp;
    PLONG               Addend;
    LONG                Increment;
    LONG                Junk;

    PAGED_CODE();

    irpSp = IoGetCurrentIrpStackLocation(Irp);

    Increment = irpSp->Parameters.UsageNotification.InPath  ? 1 : -1;
    switch (irpSp->Parameters.UsageNotification.Type) {
        case DeviceUsageTypePaging:         Addend = &PowerState->Paging;      break;
        case DeviceUsageTypeHibernation:    Addend = &PowerState->Hibernate;   break;
        case DeviceUsageTypeDumpFile:       Addend = &PowerState->CrashDump;   break;
        default:
            return STATUS_NOT_SUPPORTED;
    }

    Junk = InterlockedExchangeAdd (Addend, Increment);

#if DBG

    if (Increment == -1) {
        ASSERT(Junk > 0);
    }

#endif

    return STATUS_SUCCESS;
}

NTSTATUS
PciPdoDeviceUsage (
    IN PPCI_PDO_EXTENSION   pdoExtension,
    IN PIRP             Irp
    )
{
    PDEVICE_OBJECT      ParentFdo;
    PIO_STACK_LOCATION  IrpSp;
    PIO_STACK_LOCATION  NewIrpSp;
    PIRP                NewIrp;
    KEVENT              Event;
    NTSTATUS            Status = STATUS_SUCCESS;
    IO_STATUS_BLOCK     LocalIoStatus;

    PAGED_CODE();

    //
    // Do we have a parent that we must notify?
    //
    if (pdoExtension->ParentFdoExtension != NULL &&
        pdoExtension->ParentFdoExtension->PhysicalDeviceObject != NULL) {

        //
        // Get a referenced object to the parent
        //
        ParentFdo = IoGetAttachedDeviceReference(
            pdoExtension->ParentFdoExtension->PhysicalDeviceObject
            );
        if (ParentFdo == NULL) {

            Status = STATUS_NO_SUCH_DEVICE;
            goto PciPdoDeviceUsageExit;

        }

        //
        // Initialize the event to wait on
        //
        KeInitializeEvent( &Event, SynchronizationEvent, FALSE );

        //
        // Build an Irp
        //
        NewIrp = IoBuildSynchronousFsdRequest(
            IRP_MJ_PNP,
            ParentFdo,
            NULL,
            0,
            NULL,
            &Event,
            &LocalIoStatus
            );
        if (NewIrp == NULL) {

            Status = STATUS_INSUFFICIENT_RESOURCES;
            ObDereferenceObject( ParentFdo );
            goto PciPdoDeviceUsageExit;

        }

        //
        // Get the top of the stacks
        //
        NewIrpSp = IoGetNextIrpStackLocation( NewIrp );
        IrpSp = IoGetCurrentIrpStackLocation(Irp);

        //
        // Set the top of stack
        //
        *NewIrpSp = *IrpSp;

        //
        // Clear any completion routines from the new stack
        //
        IoSetCompletionRoutine(
            NewIrp,
            NULL,
            NULL,
            FALSE,
            FALSE,
            FALSE
            );

        NewIrp->IoStatus.Status = STATUS_NOT_SUPPORTED;

        //
        // Send the request down
        //
        Status = IoCallDriver( ParentFdo, NewIrp );
        if (Status == STATUS_PENDING) {

            //
            // Wait for the request to be done
            //
            KeWaitForSingleObject(
                &Event,
                Executive,
                KernelMode,
                FALSE,
                NULL
                );
            Status = LocalIoStatus.Status;

        }

        //
        // Deference the target
        //
        ObDereferenceObject( ParentFdo );

    }


PciPdoDeviceUsageExit:

    //
    // If we succeeded, then apply the usages locally
    //
    if (NT_SUCCESS(Status)) {

        //
        // Apply the usage locally
        //

        Status = PciLocalDeviceUsage(&pdoExtension->PowerState, Irp);

    }

    //
    // Done
    //
    return Status;
}