/*++

Copyright (c) 1996  Microsoft Corporation

Module Name:

    filter.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(INIT, DriverEntry)
        #pragma alloc_text(PAGE, VA_AddDevice)
        #pragma alloc_text(PAGE, VA_DriverUnload)
#endif


NTSTATUS DriverEntry(
                        IN PDRIVER_OBJECT DriverObject, 
                        IN PUNICODE_STRING RegistryPath
                    )
/*++

Routine Description:

    Installable driver initialization entry point.
    This entry point is called directly by the I/O system.

Arguments:

    DriverObject - pointer to the driver object

    RegistryPath - pointer to a unicode string representing the path,
                   to driver-specific key in the registry.

Return Value:

    STATUS_SUCCESS if successful,
    STATUS_UNSUCCESSFUL otherwise

--*/
{
    ULONG i;

    PAGED_CODE();

    UNREFERENCED_PARAMETER(RegistryPath);

    DBGOUT(("DriverEntry")); 

    /*
     *  Route all IRPs on device objects created by this driver
     *  to our IRP dispatch routine.
     */
    for (i = 0; i <= IRP_MJ_MAXIMUM_FUNCTION; i++){
        DriverObject->MajorFunction[i] = VA_Dispatch; 
    }

    DriverObject->DriverExtension->AddDevice = VA_AddDevice;
    DriverObject->DriverUnload = VA_DriverUnload;

    return STATUS_SUCCESS;
}


NTSTATUS VA_AddDevice(
                        IN PDRIVER_OBJECT driverObj, 
                        IN PDEVICE_OBJECT physicalDevObj
                     )
/*++

Routine Description:

    The PlugPlay subsystem is handing us a brand new 
    PDO (Physical Device Object), for which we
    (by means of INF registration) have been asked to filter.

    We need to determine if we should attach or not.
    Create a filter device object to attach to the stack
    Initialize that device object
    Return status success.

    Remember: we can NOT actually send ANY non pnp IRPS to the given driver
    stack, UNTIL we have received an IRP_MN_START_DEVICE.

Arguments:

    driverObj - pointer to a device object.

    physicalDevObj -    pointer to a physical device object pointer 
                        created by the  underlying bus driver.

Return Value:

    NT status code.

--*/

{
    NTSTATUS status;
    PDEVICE_OBJECT filterDevObj = NULL;
    
    PAGED_CODE();

    DBGOUT(("VA_AddDevice: drvObj=%ph, pdo=%ph", driverObj, physicalDevObj)); 

    status = IoCreateDevice(    driverObj, 
                                sizeof(struct DEVICE_EXTENSION),
                                NULL,           // name for this device
                                FILE_DEVICE_UNKNOWN, 
                                FILE_AUTOGENERATED_DEVICE_NAME,                // device characteristics
                                FALSE,          // not exclusive
                                &filterDevObj); // our device object

    if (NT_SUCCESS(status)){
        struct DEVICE_EXTENSION *devExt;

        ASSERT(filterDevObj);

        /*
         *  Initialize device extension for new device object
         */
        devExt = (struct DEVICE_EXTENSION *)filterDevObj->DeviceExtension;
        RtlZeroMemory(devExt, sizeof(struct DEVICE_EXTENSION));
        devExt->signature = DEVICE_EXTENSION_SIGNATURE;
        devExt->state = STATE_INITIALIZED;
        devExt->filterDevObj = filterDevObj;
        devExt->physicalDevObj = physicalDevObj;
        
        devExt->pendingActionCount = 0;
        KeInitializeEvent(&devExt->removeEvent, NotificationEvent, FALSE);

        /*
         *  Clear the initializing bit from the new device object's flags.
         */
        filterDevObj->Flags &= ~DO_DEVICE_INITIALIZING;

        /*
         *  The DO_POWER_PAGABLE bit of a device object
         *  indicates to the kernel that the power-handling
         *  code of the corresponding driver is pageable, and
         *  so must be called at IRQL 0.
         *  As a filter driver, we do not want to change the power
         *  behavior of the driver stack in any way; therefore,
         *  we copy this bit from the lower device object.
         */
        ASSERT(!(filterDevObj->Flags & DO_POWER_PAGABLE)); 
        filterDevObj->Flags |= (physicalDevObj->Flags & DO_POWER_PAGABLE);

        /*
         *  Attach the new device object to the top of the device stack.
         */
        devExt->topDevObj = IoAttachDeviceToDeviceStack(filterDevObj, physicalDevObj);

        ASSERT(devExt->topDevObj);
        DBGOUT(("created filterDevObj %ph attached to %ph.", filterDevObj, devExt->topDevObj));

        /*
         *  This is a do-nothing call to a sample function which
         *  demonstrates how to read the device's registry area.
         *  Note that you cannot make this call on devExt->filterDevObj
         *  because a filter device object does not have a devNode.
         *  We pass devExt->physicalDevObj, which is the device object
         *  for which this driver is a filter driver.
         */
        RegistryAccessSample(devExt->physicalDevObj);
    } 

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


VOID VA_DriverUnload(IN PDRIVER_OBJECT DriverObject)
/*++

Routine Description:

    Free all the allocated resources, etc.

    Note:  Although the DriverUnload function often does nothing,
           the driver must set a DriverUnload function in 
           DriverEntry; otherwise, the kernel will never unload
           the driver.

Arguments:

    DriverObject - pointer to a driver object.

Return Value:

    VOID.

--*/
{
    PAGED_CODE();

    DBGOUT(("VA_DriverUnload")); 
}


NTSTATUS VA_Dispatch(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp)
/*++

Routine Description:

    Common entrypoint for all Io Request Packets

Arguments:

    DeviceObject - pointer to a device object.
    Irp - Io Request Packet

Return Value:

    NT status code.

--*/

{
    struct DEVICE_EXTENSION *devExt;
    PIO_STACK_LOCATION irpSp;
    BOOLEAN passIrpDown = TRUE;
    UCHAR majorFunc, minorFunc;
    NTSTATUS status;

    devExt = DeviceObject->DeviceExtension;
    ASSERT(devExt->signature == DEVICE_EXTENSION_SIGNATURE);

    irpSp = IoGetCurrentIrpStackLocation(Irp);

    /*
     *  Get major/minor function codes in private variables
     *  so we can access them after the IRP is completed.
     */
    majorFunc = irpSp->MajorFunction;
    minorFunc = irpSp->MinorFunction;

    DBGOUT(("VA_Dispatch: majorFunc=%d, minorFunc=%d", 
            (ULONG)majorFunc, (ULONG)minorFunc)); 

    /*
     *  For all IRPs except REMOVE, we increment the PendingActionCount
     *  across the dispatch routine in order to prevent a race condition with
     *  the REMOVE_DEVICE IRP (without this increment, if REMOVE_DEVICE
     *  preempted another IRP, device object and extension might get
     *  freed while the second thread was still using it).
     */
    if (!((majorFunc == IRP_MJ_PNP) && (minorFunc == IRP_MN_REMOVE_DEVICE))){
        IncrementPendingActionCount(devExt);
    }

    if ((majorFunc != IRP_MJ_PNP) &&
        (majorFunc != IRP_MJ_CLOSE) &&
        ((devExt->state == STATE_REMOVING) ||
         (devExt->state == STATE_REMOVED))){

        /*
         *  While the device is being removed, 
         *  we only pass down the PNP and CLOSE IRPs.
         *  We fail all other IRPs.
         */
        status = Irp->IoStatus.Status = STATUS_DELETE_PENDING;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        passIrpDown = FALSE;
    }
    else {
        switch (majorFunc){

            case IRP_MJ_PNP:
                status = VA_PnP(devExt, Irp);
                passIrpDown = FALSE;
                break;

            case IRP_MJ_POWER:
                status = VA_Power(devExt, Irp);
                passIrpDown = FALSE;
                break;

            case IRP_MJ_CREATE:
            case IRP_MJ_CLOSE:
            case IRP_MJ_DEVICE_CONTROL:
            case IRP_MJ_SYSTEM_CONTROL:
            case IRP_MJ_INTERNAL_DEVICE_CONTROL:
            default:
                /*
                 *  For unsupported IRPs, we simply send the IRP
                 *  down the driver stack.
                 */
                break;
        }
    }

    if (passIrpDown){
        IoCopyCurrentIrpStackLocationToNext(Irp);
        status = IoCallDriver(devExt->physicalDevObj, Irp);
    }

    /*
     *  Balance the increment to PendingActionCount above.
     */
    if (!((majorFunc == IRP_MJ_PNP) && (minorFunc == IRP_MN_REMOVE_DEVICE))){
        DecrementPendingActionCount(devExt);
    }

    return status;
}


