/*++

Copyright (c) 1996-1998  Microsoft Corporation

Module Name:

    pnp.c

Abstract: Human Input Device (HID) minidriver that creates an example
        device.

--*/
#include <WDM.H>
#include <USBDI.H>

#include <HIDPORT.H>
#include <HIDMINI.H>


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

Routine Description:

    Process the PnP IRPs sent to this device.

Arguments:

    DeviceObject - pointer to a device object.

    Irp - pointer to an I/O Request Packet.

Return Value:

    NT status code.

--*/
{
    NTSTATUS ntStatus;
    PIO_STACK_LOCATION IrpStack;
    PIO_STACK_LOCATION NextStack;
    PDEVICE_EXTENSION DeviceExtension;

    DBGPrint(("HIDMINI.SYS: HidMiniPlugnPlay Enter\n"));

    DBGPrint(("HIDMINI.SYS: DeviceObject = %x\n", DeviceObject));

    //
    // Get a pointer to the device extension
    //

    DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);

    DBGPrint(("HIDMINI.SYS: DeviceExtension = %x\n", DeviceExtension));

    //
    // Get a pointer to the current location in the Irp
    //

    IrpStack = IoGetCurrentIrpStackLocation (Irp);

    DBGPrint(("HIDMINI.SYS: IoGetCurrentIrpStackLocation (Irp) = %x\n", IrpStack));

    switch (IrpStack->MinorFunction)
    {
    case IRP_MN_START_DEVICE:
        DBGPrint(("HIDMINI.SYS: IRP_MN_START_DEVICE\n"));

        ntStatus = HidMiniStartDevice(DeviceObject);

        break;

    case IRP_MN_STOP_DEVICE:
        DBGPrint(("HIDMINI.SYS: IRP_MN_STOP_DEVICE\n"));

        ntStatus = HidMiniStopDevice(DeviceObject);

        break;

    case IRP_MN_REMOVE_DEVICE:
        DBGPrint(("HIDMINI.SYS: IRP_MN_REMOVE_DEVICE\n"));

        ntStatus = HidMiniRemoveDevice(DeviceObject);

        break;

    case IRP_MN_QUERY_ID:
        DBGPrint(("HIDMINI.SYS: IRP_MN_QUERY_ID\n"));

        (PWCHAR)Irp->IoStatus.Information = NULL;
        ntStatus = STATUS_SUCCESS;

        break;

    default:
        ntStatus = STATUS_SUCCESS;
        DBGPrint(("HIDMINI.SYS: Unknown PNP IRP Parameter (%lx)\n", IrpStack->MinorFunction));
    }

    //
    // Set the status of the Irp
    //

    Irp->IoStatus.Status = ntStatus;

    if (NT_SUCCESS(ntStatus))
    {

        //
        // Set next stack location
        //

        NextStack = IoGetNextIrpStackLocation(Irp);

        ASSERT(NextStack != NULL);

        //
        // Copy the Irp to the next stack location
        //

        RtlCopyMemory(NextStack, IrpStack, sizeof(IO_STACK_LOCATION));

        IoMarkIrpPending(Irp);

        //
        // Set our own completion routine or disable completion
        // routine of caller
        //

        switch (IrpStack->MinorFunction)
        {
        case IRP_MN_START_DEVICE:
            IoSetCompletionRoutine( Irp,
                                    HidMiniStartCompletion,
                                    DeviceExtension,            // reference data
                                    TRUE,                       // call on success
                                    TRUE,                       // call on failure
                                    TRUE );                     // call on cancel
            break;

        case IRP_MN_STOP_DEVICE:
            IoSetCompletionRoutine( Irp,
                                    HidMiniStopCompletion,
                                    DeviceExtension,            // reference data
                                    TRUE,                       // call on success
                                    TRUE,                       // call on failure
                                    TRUE );                     // call on cancel
            break;

        case IRP_MN_QUERY_ID:
            IoSetCompletionRoutine( Irp,
                                    HidMiniQueryIDCompletion,
                                    DeviceExtension,            // reference data
                                    TRUE,                       // call on success
                                    TRUE,                       // call on failure
                                    TRUE );                     // call on cancel
            break;

        default:
            NextStack->Control = 0;
            break;
        }

        //
        // Pass it down to the Next Device Object
        //

        DBGPrint(("HIDMINI.SYS: Passing PnP Irp down to next object\n"));

        ntStatus = IoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp);   

    }
    else
    {

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        //
        // NOTE: Real status returned in Irp->IoStatus.Status 
        //

        ntStatus = STATUS_SUCCESS;
    }

    DBGPrint(("HIDMINI.SYS: HidMiniPlugnPlay Exit = %x\n", ntStatus));

    return ntStatus;
}

NTSTATUS
HidMiniStartDevice(
                  IN  PDEVICE_OBJECT DeviceObject
                  )
/*++

Routine Description:

    Begins initialization a given instance of a HID device.  Work done here occurs before
    the parent node gets to do anything.

Arguments:

    DeviceObject - pointer to the device object for this instance.

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION DeviceExtension;
    NTSTATUS ntStatus;
    PUSB_DEVICE_DESCRIPTOR DeviceDesc = NULL;

    DBGPrint(("HIDMINI.SYS: HidMiniStartDevice Enter\n"));

    //
    // Get a pointer to the device extension
    //

    DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);

    DBGPrint(("HIDMINI.SYS: DeviceExtension = %x\n", DeviceExtension));

    //
    // Start the device
    //

    DeviceExtension->DeviceState = DEVICE_STATE_STARTING; 

    ntStatus = STATUS_SUCCESS;

    DBGPrint(("HIDMINI.SYS: HidMiniStartDevice Exit = %x\n", ntStatus));

    return ntStatus;
}

NTSTATUS
HidMiniStartCompletion(
                      IN PDEVICE_OBJECT DeviceObject,
                      IN PIRP           Irp,
                      IN PVOID          Context
                      )
/*++

Routine Description:

    Completes initialization a given instance of a HID device.  Work done here occurs
    after the parent node has done its StartDevice.

Arguments:

    DeviceObject - pointer to the device object for this instance.

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION DeviceExtension;
    NTSTATUS ntStatus;
    PUSB_DEVICE_DESCRIPTOR DeviceDesc = NULL;

    DBGPrint(("HIDMINI.SYS: HidMiniStartCompletion Enter\n"));

    //
    // Get a pointer to the device extension
    //

    DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);

    DBGPrint(("HIDMINI.SYS: DeviceExtension = %x\n", DeviceExtension));

    ntStatus = Irp->IoStatus.Status;

    if (NT_SUCCESS(ntStatus))
    {
        DeviceExtension->DeviceState = DEVICE_STATE_RUNNING; 
        IsRunning = TRUE;

        DBGPrint(("HIDMINI.SYS: DeviceObject (%x) was started!\n", DeviceObject));

        ntStatus = HidMiniInitDevice(DeviceObject);

        if (NT_SUCCESS(ntStatus))
        {
            DBGPrint(("HIDMINI.SYS: DeviceObject (%x) was configured!\n", DeviceObject));
        }
        else
        {
            DBGPrint(("HIDMINI.SYS: DeviceObject (%x) configuration failed!\n", DeviceObject));
            DeviceExtension->DeviceState = DEVICE_STATE_STOPPING;
            IsRunning = FALSE;
        }


    }
    else
    {
        //
        // The PnP call failed!
        //

        DeviceExtension->DeviceState = DEVICE_STATE_STOPPING;
        IsRunning = FALSE;

        DBGPrint(("HIDMINI.SYS: DeviceObject (%x) failed to start!\n", DeviceObject));
    }

    DBGPrint(("HIDMINI.SYS: HidMiniStartCompletion Exit = %x\n", ntStatus));

    return ntStatus;
}




NTSTATUS
HidMiniInitDevice(
                 IN PDEVICE_OBJECT DeviceObject
                 )
/*++

Routine Description:

    Get the device information and attempt to initialize a configuration
    for a device.  If we cannot identify this as a valid HID device or
    configure the device, our start device function is failed.

Arguments:

    DeviceObject - pointer to a device object.

Return Value:

    NT status code.

--*/
{
    NTSTATUS ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION   DeviceExtension;

    DBGPrint(("HIDMINI.SYS: HidMiniInitDevice Entry\n"));   

    //
    // Get a pointer to the device extension
    //

    DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);

    //
    // Get config, hid, etc. descriptors
    //

    DeviceExtension->HidDescriptor      = MyHidDescriptor;
    DeviceExtension->StringDescriptor   = MyStringDescriptor;
    DeviceExtension->PhysicalDescriptor = MyPhysicalDescriptor;
    DeviceExtension->ReportDescriptor   = MyReportDescriptor;

    InitializeListHead(&HidMini_ReadIrpHead);
    InitializeListHead(&HidMini_WriteIrpHead);

    DBGPrint(("HIDMINI.SYS: HidMiniInitDevice Exit = 0x%x\n", ntStatus));

    return ntStatus;
}


NTSTATUS
HidMiniStopDevice(
                 IN PDEVICE_OBJECT DeviceObject
                 )
/*++

Routine Description:

    Stops a given instance of a device.  Work done here occurs before the parent
    does its stop device.

Arguments:

    DeviceObject - pointer to the device object.

Return Value:

    NT status code

--*/
{
    NTSTATUS    ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION DeviceExtension;

    DBGPrint(("HIDMINI.SYS: HidMiniStopDevice Enter\n"));   

    //
    // Get a pointer to the device extension
    //

    DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);

    DBGPrint(("HIDMINI.SYS: DeviceExtension = %x\n", DeviceExtension));

    DeviceExtension->DeviceState = DEVICE_STATE_STOPPING;
    IsRunning = FALSE;

    //
    // Stop the device
    //

    //
    // Perform a synchronous abort of all pending requests for this
    // device.
    //

    HidMiniAbortPendingRequests( DeviceObject );

    DBGPrint(("HIDMINI.SYS: HidMiniStopDevice = %x\n", ntStatus));

    return ntStatus;
}

NTSTATUS
HidMiniAbortPendingRequests(
                           IN PDEVICE_OBJECT DeviceObject
                           )
{
    PNODE Node;

    //
    //  Dispose of any Irps, free up all the NODEs waiting in the queues.
    //

    while ((Node = (PNODE)ExInterlockedRemoveHeadList(&HidMini_ReadIrpHead, &HidMini_IrpReadLock)))
    {
        Node->Irp->IoStatus.Status = STATUS_NO_SUCH_DEVICE;
        IoCompleteRequest(Node->Irp, IO_NO_INCREMENT);
        ExFreePool(Node);
    }

    return STATUS_SUCCESS;
}

NTSTATUS
HidMiniStopCompletion(
                     IN PDEVICE_OBJECT DeviceObject,
                     IN PIRP           Irp,
                     IN PVOID          Context
                     )
/*++

Routine Description:

    Stops a given instance of a device.  Work done here occurs after the parent
    has done its stop device.

Arguments:

    DeviceObject - pointer to the device object.

Return Value:

    NT status code

--*/
{
    PDEVICE_EXTENSION DeviceExtension;
    NTSTATUS ntStatus;
    PUSB_DEVICE_DESCRIPTOR DeviceDesc = NULL;

    DBGPrint(("HIDMINI.SYS: HidMiniStopCompletion Enter\n"));

    //
    // Get a pointer to the device extension
    //

    DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);

    DBGPrint(("HIDMINI.SYS: DeviceExtension = %x\n", DeviceExtension));

    ntStatus = Irp->IoStatus.Status;

    if (!NT_SUCCESS(ntStatus))
    {

        DBGPrint(("HIDMINI.SYS: DeviceObject (%x) was stopped!\n", DeviceObject));

    }
    else
    {
        //
        // The PnP call failed!
        //

        DBGPrint(("HIDMINI.SYS: DeviceObject (%x) failed to stop!\n", DeviceObject));
    }

    DeviceExtension->DeviceState = DEVICE_STATE_STOPPED;
    IsRunning = FALSE;

    DBGPrint(("HIDMINI.SYS: HidMiniStopCompletion Exit = %x\n", ntStatus));

    return ntStatus;
}



NTSTATUS
HidMiniQueryIDCompletion(
                        IN PDEVICE_OBJECT DeviceObject,
                        IN PIRP Irp,
                        IN PVOID Context
                        )
/*++

Routine Description:

    Fills in a dummy ID

Arguments:

    DeviceObject - pointer to the device object.

Return Value:

    NT status code

--*/
{
    NTSTATUS ntStatus;

    DBGPrint(("HIDMINI.SYS: HidMiniQueryIDCompletion Enter\n"));

    //
    //  If this wasn't filled in below us, fill it in with a dummy value
    //

    if ((PWCHAR)Irp->IoStatus.Information == NULL)
    {

        //
        //  Here's the dummy value, allocate a buffer to copy it to.
        //
        static WCHAR MyBusID[] = L"HIDMINI_Device\0";

        PWCHAR Buffer = (PWCHAR)ExAllocatePool(NonPagedPool, sizeof(MyBusID));

        if (Buffer)
        {

            //
            //  Do the copy, store the buffer in the Irp
            //
            RtlCopyMemory(Buffer, MyBusID, sizeof(MyBusID));
            Irp->IoStatus.Information = (ULONG)Buffer;
            ntStatus = Irp->IoStatus.Status = STATUS_SUCCESS;
        }
        else
        {

            //
            //  No memory
            //
            ntStatus = Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
        }
    }
    else
    {

        //
        // Return with whatever we got below us.
        //
        ntStatus = Irp->IoStatus.Status;
    }

    DBGPrint(("HIDMINI.SYS: HidMiniQueryIDCompletion Exit = %x\n", ntStatus));

    return ntStatus;
}


NTSTATUS
HidMiniRemoveDevice(
                   IN PDEVICE_OBJECT DeviceObject
                   )
/*++

Routine Description:

    Removes a given instance of a device.

Arguments:

    DeviceObject - pointer to the device object.

Return Value:

    NT status code

--*/
{
    NTSTATUS    ntStatus = STATUS_SUCCESS;
    PDEVICE_EXTENSION DeviceExtension;
    ULONG oldDeviceState;


    DBGPrint(("HIDMINI.SYS: HidMiniRemoveDevice Enter\n"));   

    //
    // Get a pointer to the device extension
    //

    DeviceExtension = GET_MINIDRIVER_DEVICE_EXTENSION(DeviceObject);

    DBGPrint(("HIDMINI.SYS: DeviceExtension = %x\n", DeviceExtension));

    oldDeviceState = DeviceExtension->DeviceState;
    DeviceExtension->DeviceState = DEVICE_STATE_REMOVING;
    IsRunning = FALSE;

    //
    // Cancel any outstanding IRPs if the device was running
    //

    if (oldDeviceState == DEVICE_STATE_RUNNING)
    {
        ntStatus = HidMiniAbortPendingRequests( DeviceObject );

        DBGPrint(("HIDMINI.SYS: HidMiniAbortPendingRequests() = %x\n", ntStatus));

    }
    else
    {

        ASSERT( DeviceExtension->NumPendingRequests == 0 );
    }

    ntStatus = STATUS_SUCCESS;

    DBGPrint(("HIDSAMHIDMINI.SYS: HidMiniRemoveDevice = %x\n", ntStatus));

    return ntStatus;
}


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

Routine Description:

    Process the Power IRPs sent to this device.

Arguments:

    DeviceObject - pointer to a device object.

    Irp - pointer to an I/O Request Packet.

Return Value:

    NT status code.

--*/
{
    NTSTATUS ntStatus;

    IoCopyCurrentIrpStackLocationToNext(Irp);
    PoStartNextPowerIrp(Irp);
    ntStatus = PoCallDriver(GET_NEXT_DEVICE_OBJECT(DeviceObject), Irp); 

    return ntStatus;
}
