/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    pnp.c

Abstract: NULL filter driver -- boilerplate code

Author:

    ervinp

Environment:

    Kernel mode

Revision History:


--*/

#include <WDM.H>

#include "filter.h"


#ifdef ALLOC_PRAGMA
        #pragma alloc_text(PAGE, VA_PnP)
        #pragma alloc_text(PAGE, GetDeviceCapabilities)
#endif

            
NTSTATUS VA_PnP(struct DEVICE_EXTENSION *devExt, PIRP irp)
/*++

Routine Description:

    Dispatch routine for PnP IRPs (MajorFunction == IRP_MJ_PNP)

Arguments:

    devExt - device extension for the targetted device object
    irp - IO Request Packet

Return Value:

    NT status code

--*/
{
    PIO_STACK_LOCATION irpSp;
    NTSTATUS status = STATUS_SUCCESS;
    BOOLEAN completeIrpHere = FALSE;
    BOOLEAN justReturnStatus = FALSE;

    PAGED_CODE();

    irpSp = IoGetCurrentIrpStackLocation(irp);

    DBGOUT(("VA_PnP, minorFunc = %d ", (ULONG)irpSp->MinorFunction)); 

    switch (irpSp->MinorFunction){

        case IRP_MN_START_DEVICE:
            DBGOUT(("START_DEVICE")); 

            devExt->state = STATE_STARTING;

            /*
             *  First, send the START_DEVICE irp down the stack
             *  synchronously to start the lower stack.
             *  We cannot do anything with our device object
             *  before propagating the START_DEVICE this way.
             */
            IoCopyCurrentIrpStackLocationToNext(irp);
            status = CallNextDriverSync(devExt, irp);

            if (NT_SUCCESS(status)){
                /*
                 *  Now that the lower stack is started,
                 *  do any initialization required by this device object.
                 */
                status = GetDeviceCapabilities(devExt);
                if (NT_SUCCESS(status)){
                    devExt->state = STATE_STARTED;
                    /*
                     * Now that device is started, register with WMI
                    */
                    IoWMIRegistrationControl(devExt->filterDevObj,
                                             WMIREG_ACTION_REGISTER);
                }
                else {
                    devExt->state = STATE_START_FAILED;
                }
            }
            else {
                devExt->state = STATE_START_FAILED;
            }
            completeIrpHere = TRUE;
            break;

        case IRP_MN_QUERY_STOP_DEVICE:
            break;

        case IRP_MN_STOP_DEVICE:
            if (devExt->state == STATE_SUSPENDED){
                status = STATUS_DEVICE_POWER_FAILURE;
                completeIrpHere = TRUE;
            }
            else {
                /*
                 *  Only set state to STOPPED if the device was
                 *  previously started successfully.
                 */
                if (devExt->state == STATE_STARTED){
                    devExt->state = STATE_STOPPED;
                }
            }
            break;
      
        case IRP_MN_QUERY_REMOVE_DEVICE:
            /*
             *  We will pass this IRP down the driver stack.
             *  However, we need to change the default status
             *  from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
             */
            irp->IoStatus.Status = STATUS_SUCCESS;
            break;

        case IRP_MN_SURPRISE_REMOVAL:
            DBGOUT(("SURPRISE_REMOVAL")); 

            /*
             *  We will pass this IRP down the driver stack.
             *  However, we need to change the default status
             *  from STATUS_NOT_SUPPORTED to STATUS_SUCCESS.
             */
            irp->IoStatus.Status = STATUS_SUCCESS;

            /*
             *  For now just set the STATE_REMOVING state so that
             *  we don't do any more IO.  We are guaranteed to get
             *  IRP_MN_REMOVE_DEVICE soon; we'll do the rest of
             *  the remove processing there.
             */
            devExt->state = STATE_REMOVING;

            break;

        case IRP_MN_REMOVE_DEVICE:
            /*
             *  Check the current state to guard against multiple
             *  REMOVE_DEVICE IRPs.
             */
            DBGOUT(("REMOVE_DEVICE")); 
            if (devExt->state != STATE_REMOVED){

                devExt->state = STATE_REMOVED;

                /*
                 *  Send the REMOVE IRP down the stack asynchronously.
                 *  Do not synchronize sending down the REMOVE_DEVICE
                 *  IRP, because the REMOVE_DEVICE IRP must be sent
                 *  down and completed all the way back up to the sender
                 *  before we continue.
                 */
                IoCopyCurrentIrpStackLocationToNext(irp);
                status = IoCallDriver(devExt->physicalDevObj, irp);
                justReturnStatus = TRUE;

                DBGOUT(("REMOVE_DEVICE - waiting for %d irps to complete...",
                        devExt->pendingActionCount));  

                /*
                 *  We must for all outstanding IO to complete before
                 *  completing the REMOVE_DEVICE IRP.
                 *
                 *  First do an extra decrement on the pendingActionCount.
                 *  This will cause pendingActionCount to eventually
                 *  go to -1 once all asynchronous actions on this
                 *  device object are complete.
                 *  Then wait on the event that gets set when the
                 *  pendingActionCount actually reaches -1.
                 */
                DecrementPendingActionCount(devExt);
                KeWaitForSingleObject(  &devExt->removeEvent,
                                        Executive,      // wait reason
                                        KernelMode,
                                        FALSE,          // not alertable
                                        NULL );         // no timeout

                DBGOUT(("REMOVE_DEVICE - ... DONE waiting. ")); 

                /*
                 *  Now that the device is going away unregister with WMI
                 *  Note that we wait until all WMI irps are completed
                 *  before unregistering since unregistering will block
                 *  until all WMI irps are completed.
                 */
                FilterWmiCleanup(devExt);
                IoWMIRegistrationControl(devExt->filterDevObj,
                                             WMIREG_ACTION_DEREGISTER);
	
                /*
                 *  Detach our device object from the lower 
                 *  device object stack.
                 */
                IoDetachDevice(devExt->topDevObj);

                /*
                 *  Delete our device object.
                 *  This will also delete the associated device extension.
                 */
                IoDeleteDevice(devExt->filterDevObj);
            }
            break;

        case IRP_MN_QUERY_DEVICE_RELATIONS:
        case IRP_MN_QUERY_PNP_DEVICE_STATE:
        default:
            break;


    }

    if (justReturnStatus){
        /*
         *  We've already sent this IRP down the stack asynchronously.
         */
    }
    else if (completeIrpHere){
        irp->IoStatus.Status = status;
        IoCompleteRequest(irp, IO_NO_INCREMENT);
    }
    else {
        IoCopyCurrentIrpStackLocationToNext(irp);
        status = IoCallDriver(devExt->physicalDevObj, irp);
    }

    return status;
}





NTSTATUS GetDeviceCapabilities(struct DEVICE_EXTENSION *devExt)
/*++

Routine Description:

    Function retrieves the DEVICE_CAPABILITIES descriptor from the device

Arguments:

    devExt - device extension for targetted device object

Return Value:

    NT status code

--*/
{
    NTSTATUS status;
    PIRP irp;

    PAGED_CODE();

    irp = IoAllocateIrp(devExt->physicalDevObj->StackSize, FALSE);
    if (irp){
        PIO_STACK_LOCATION nextSp = IoGetNextIrpStackLocation(irp);

        nextSp->MajorFunction = IRP_MJ_PNP;
        nextSp->MinorFunction = IRP_MN_QUERY_CAPABILITIES;
        RtlZeroMemory(  &devExt->deviceCapabilities, 
                        sizeof(DEVICE_CAPABILITIES));
        nextSp->Parameters.DeviceCapabilities.Capabilities = 
                        &devExt->deviceCapabilities;

        /*
         *  For any IRP you create, you must set the default status
         *  to STATUS_NOT_SUPPORTED before sending it.
         */
        irp->IoStatus.Status = STATUS_NOT_SUPPORTED;

        status = CallNextDriverSync(devExt, irp);

        IoFreeIrp(irp);
    }
    else {
        status = STATUS_INSUFFICIENT_RESOURCES;
    }

    ASSERT(NT_SUCCESS(status));
    return status;
}