/*++

Copyright (c) 1995 Microsoft Corporation

Module Name:

    initunlo.c

Abstract:

    This module contains the code that is very specific to initialization
    and unload operations in the irenum driver

Author:

    Brian Lieuallen, 7-13-2000

Environment:

    Kernel mode

Revision History :

--*/

#include "internal.h"


#pragma alloc_text(PAGE,IrEnumAddDevice)
#pragma alloc_text(PAGE,IrEnumPnP)
#pragma alloc_text(PAGE,IrEnumPower)
#pragma alloc_text(PAGE,IrEnumWmi)

NTSTATUS
IrEnumAddDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN PDEVICE_OBJECT Pdo
    )

{
    NTSTATUS       Status;

    PDEVICE_OBJECT Fdo = NULL;

    PDEVICE_OBJECT LowerDevice=NULL;

    //
    // Pointer to the device extension created for this
    // device
    //
    PFDO_DEVICE_EXTENSION DeviceExtension = NULL;

    D_PNP(DbgPrint("IRENUM: AddDevice\n");)

    //
    // Create the device object for this device.
    //

    Status = IoCreateDevice(
                 DriverObject,
                 sizeof(FDO_DEVICE_EXTENSION),
                 NULL,
                 FILE_DEVICE_NULL,
                 FILE_AUTOGENERATED_DEVICE_NAME,
                 FALSE,
                 &Fdo
                 );

    if (!NT_SUCCESS(Status)) {

        goto CleanUp;
    }

    LowerDevice=IoAttachDeviceToDeviceStack(
        Fdo,
        Pdo
        );

    if (LowerDevice == NULL) {

        D_ERROR(DbgPrint("IRENUM: Could not attach to PDO\n");)

        Status=STATUS_INSUFFICIENT_RESOURCES;

        goto CleanUp;
    }

    Fdo->Flags |= LowerDevice->Flags;
    Fdo->Flags &= ~DO_DEVICE_INITIALIZING;

    Fdo->StackSize=LowerDevice->StackSize+1;

    DeviceExtension=Fdo->DeviceExtension;

    DeviceExtension->DoType=DO_TYPE_FDO;

    DeviceExtension->DeviceObject = Fdo;
    DeviceExtension->Pdo=Pdo;
    DeviceExtension->LowerDevice=LowerDevice;

    DeviceExtension->CreateStaticDevice= (EnumStaticDevice != 0) ;

    CreateEnumObject(Fdo,&DeviceExtension->EnumHandle,DeviceExtension->CreateStaticDevice);

    return STATUS_SUCCESS;

CleanUp:

    IoDeleteDevice(Fdo);

    return Status;

}

NTSTATUS
IrEnumPnP(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

{

    PFDO_DEVICE_EXTENSION   DeviceExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION      irpSp = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS                status;
    ULONG                   i;


    if ((DeviceExtension->DoType==DO_TYPE_PDO) || (DeviceExtension->DoType==DO_TYPE_DEL_PDO)) {
        //
        //  this one is for the child
        //
        return IrEnumPdoPnp(
                   DeviceObject,
                   Irp
                   );
    }

    if (DeviceExtension->DoType != DO_TYPE_FDO) {

        DbgPrint("IRENUM: IrEnumPnp: Bad DevObj\n");

        Irp->IoStatus.Status = STATUS_SUCCESS;

        IoCompleteRequest(
            Irp,
            IO_NO_INCREMENT
            );


        return STATUS_SUCCESS;

    }


    switch (irpSp->MinorFunction) {

        case IRP_MN_START_DEVICE:

            Irp->IoStatus.Status = STATUS_SUCCESS;

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);



        case IRP_MN_QUERY_STOP_DEVICE:

            D_PNP(DbgPrint("IRENUM: IRP_MN_QUERY_STOP_DEVICE\n");)

            Irp->IoStatus.Status = STATUS_SUCCESS;

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);


        case IRP_MN_CANCEL_STOP_DEVICE:

            D_PNP(DbgPrint("IRENUM: IRP_MN_CANCEL_STOP_DEVICE\n");)

            Irp->IoStatus.Status = STATUS_SUCCESS;

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);


        case IRP_MN_STOP_DEVICE:

            D_PNP(DbgPrint("IRENUM: IRP_MN_STOP_DEVICE\n");)

            Irp->IoStatus.Status = STATUS_SUCCESS;

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);


        case IRP_MN_QUERY_DEVICE_RELATIONS: {

            PDEVICE_RELATIONS    CurrentRelations=(PDEVICE_RELATIONS)Irp->IoStatus.Information;
            PDEVICE_RELATIONS    NewRelations=NULL;

            D_PNP(DbgPrint("IRENUM: IRP_MN_QUERY_DEVICE_RELATIONS type=%d\n",irpSp->Parameters.QueryDeviceRelations.Type);)
            D_PNP(DbgPrint("                                     Information=%p\n",Irp->IoStatus.Information);)


            switch (irpSp->Parameters.QueryDeviceRelations.Type ) {

                case BusRelations: {

                    if (DeviceExtension->EnumHandle != NULL) {

                        status=GetDeviceList(
                            DeviceExtension->EnumHandle,
                            Irp
                            );

                        Irp->IoStatus.Status = status;

                        if (!NT_SUCCESS(status)) {

                            IoCompleteRequest(
                                Irp,
                                IO_NO_INCREMENT
                                );

                            return status;
                        }

                    }

                    return ForwardIrp(DeviceExtension->LowerDevice, Irp);
                }

                case TargetDeviceRelation:
                default: {

                    return ForwardIrp(DeviceExtension->LowerDevice, Irp);
                }
            }

            break;

        }


        case IRP_MN_QUERY_REMOVE_DEVICE:

            D_PNP(DbgPrint("IRENUM: IRP_MN_QUERY_REMOVE_DEVICE\n");)

            Irp->IoStatus.Status = STATUS_SUCCESS;

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);


        case IRP_MN_CANCEL_REMOVE_DEVICE:

            D_PNP(DbgPrint("IRENUM: IRP_MN_CANCEL_REMOVE_DEVICE\n");)

            Irp->IoStatus.Status = STATUS_SUCCESS;

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);

        case IRP_MN_SURPRISE_REMOVAL: {

            D_PNP(DbgPrint("IRENUM: IRP_MN_SURPRISE_REMOVAL\n");)

            Irp->IoStatus.Status = STATUS_SUCCESS;

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);

        }
        break;


        case IRP_MN_REMOVE_DEVICE: {

            ULONG    NewReferenceCount;

            D_PNP(DbgPrint("IRENUM: IRP_MN_REMOVE_DEVICE\n");)
            //
            //  removing now for sure
            //
            DeviceExtension->Removing=TRUE;
            DeviceExtension->Removed=TRUE;


            IoCopyCurrentIrpStackLocationToNext(Irp);

            status=IoCallDriver(DeviceExtension->LowerDevice, Irp);

            //
            //  detach from the driver below
            //
            IoDetachDevice(DeviceExtension->LowerDevice);

            DeviceExtension->DoType=DO_TYPE_DEL_FDO;

            if (DeviceExtension->EnumHandle != NULL) {

                CloseEnumObject(DeviceExtension->EnumHandle);
            }

            //
            //  delete our device object
            //
            IoDeleteDevice(DeviceObject);

            D_PNP(DbgPrint("IRENUM: IRP_MN_REMOVE_DEVICE exit, %08lx\n",status);)

            return status;

        }


        default:
            D_PNP(DbgPrint("IRENUM: Sending to PDO PnP IRP, MN func=%d\n",irpSp->MinorFunction);)

            return ForwardIrp(DeviceExtension->LowerDevice, Irp);
    }



    IoCompleteRequest(Irp,IO_NO_INCREMENT);

    return STATUS_SUCCESS;

}

NTSTATUS
IrEnumPower(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

{
    PFDO_DEVICE_EXTENSION   DeviceExtension = DeviceObject->DeviceExtension;

    if ((DeviceExtension->DoType==DO_TYPE_PDO) || (DeviceExtension->DoType==DO_TYPE_DEL_PDO)) {
        //
        //  this one is for the child
        //
        return IrEnumPdoPower(
                   DeviceObject,
                   Irp
                   );
    }


    PoStartNextPowerIrp(Irp);

    IoSkipCurrentIrpStackLocation(Irp);

    return PoCallDriver(DeviceExtension->LowerDevice, Irp);

}

NTSTATUS
IrEnumWmi(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )

{
    PFDO_DEVICE_EXTENSION   DeviceExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION      irpSp=IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS                Status;

    if ((DeviceExtension->DoType==DO_TYPE_PDO) || (DeviceExtension->DoType==DO_TYPE_DEL_PDO)) {

        return IrEnumPdoWmi(
                    DeviceObject,
                    Irp
                    );
    }

    if (irpSp->Parameters.WMI.ProviderId == (ULONG_PTR)DeviceObject) {
        //
        //  The irp was targeted at this device, but we don't support wmi
        //
        Status = Irp->IoStatus.Status;

        IoCompleteRequest(Irp,IO_NO_INCREMENT);

    } else {
        //
        //  the irp is targeted at another device object in the stack
        //
        Status=ForwardIrp(DeviceExtension->LowerDevice, Irp);
    }

    return Status;

}
