/*++

Copyright (c) 2013 Minoca Corp. All Rights Reserved

Module Name:

    usbhub.c

Abstract:

    This module implements support for the USB Hub driver.

Author:

    Evan Green 16-Jan-2013

Environment:

    Kernel

--*/

//
// ------------------------------------------------------------------- Includes
//

#include <minoca/kernel/driver.h>
#include <minoca/usb/usbhost.h>
#include "usbhub.h"

//
// --------------------------------------------------------------------- Macros
//

//
// ---------------------------------------------------------------- Definitions
//

//
// ------------------------------------------------------ Data Type Definitions
//

/*++

Structure Description:

    This structure stores context about a USB Hub.

Members:

    UsbCoreHandle - Stores the handle to the hub as identified by the USB core
        library.

    UsbHub - Stores an opaque pointer to the USB hub-specific context.

--*/

typedef struct _USB_HUB_DRIVER_CONTEXT {
    HANDLE UsbCoreHandle;
    PUSB_HUB UsbHub;
} USB_HUB_DRIVER_CONTEXT, *PUSB_HUB_DRIVER_CONTEXT;

//
// ----------------------------------------------- Internal Function Prototypes
//

KSTATUS
UsbHubAddDevice (
    PVOID Driver,
    PSTR DeviceId,
    PSTR ClassId,
    PSTR CompatibleIds,
    PVOID DeviceToken
    );

VOID
UsbHubDispatchStateChange (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    );

VOID
UsbHubDispatchOpen (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    );

VOID
UsbHubDispatchClose (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    );

VOID
UsbHubDispatchIo (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    );

VOID
UsbHubDispatchSystemControl (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    );

KSTATUS
UsbHubpStartDevice (
    PIRP Irp,
    PUSB_HUB_DRIVER_CONTEXT Device
    );

VOID
UsbHubpEnumerateChildren (
    PIRP Irp,
    PUSB_HUB_DRIVER_CONTEXT Device
    );

VOID
UsbHubpRemoveDevice (
    PIRP Irp,
    PUSB_HUB_DRIVER_CONTEXT Device
    );

//
// -------------------------------------------------------------------- Globals
//

PDRIVER UsbHubDriver = NULL;

//
// ------------------------------------------------------------------ Functions
//

KSTATUS
DriverEntry (
    PDRIVER Driver
    )

/*++

Routine Description:

    This routine is the entry point for the USB Hub driver. It registers its
    other dispatch functions, and performs driver-wide initialization.

Arguments:

    Driver - Supplies a pointer to the driver object.

Return Value:

    STATUS_SUCCESS on success.

    Failure code on error.

--*/

{

    DRIVER_FUNCTION_TABLE FunctionTable;
    KSTATUS Status;

    UsbHubDriver = Driver;
    RtlZeroMemory(&FunctionTable, sizeof(DRIVER_FUNCTION_TABLE));
    FunctionTable.Version = DRIVER_FUNCTION_TABLE_VERSION;
    FunctionTable.AddDevice = UsbHubAddDevice;
    FunctionTable.DispatchStateChange = UsbHubDispatchStateChange;
    FunctionTable.DispatchOpen = UsbHubDispatchOpen;
    FunctionTable.DispatchClose = UsbHubDispatchClose;
    FunctionTable.DispatchIo = UsbHubDispatchIo;
    FunctionTable.DispatchSystemControl = UsbHubDispatchSystemControl;
    Status = IoRegisterDriverFunctions(Driver, &FunctionTable);
    return Status;
}

//
// --------------------------------------------------------- Internal Functions
//

KSTATUS
UsbHubAddDevice (
    PVOID Driver,
    PSTR DeviceId,
    PSTR ClassId,
    PSTR CompatibleIds,
    PVOID DeviceToken
    )

/*++

Routine Description:

    This routine is called when a device is detected for which the USB Hub
    driver acts as the function driver. The driver will attach itself to the
    stack.

Arguments:

    Driver - Supplies a pointer to the driver being called.

    DeviceId - Supplies a pointer to a string with the device ID.

    ClassId - Supplies a pointer to a string containing the device's class ID.

    CompatibleIds - Supplies a pointer to a string containing device IDs
        that would be compatible with this device.

    DeviceToken - Supplies an opaque token that the driver can use to identify
        the device in the system. This token should be used when attaching to
        the stack.

Return Value:

    STATUS_SUCCESS on success.

    Failure code if the driver was unsuccessful in attaching itself.

--*/

{

    PUSB_HUB_DRIVER_CONTEXT NewDevice;
    KSTATUS Status;

    //
    // Create the device context and attach to the device.
    //

    NewDevice = MmAllocateNonPagedPool(sizeof(USB_HUB_DRIVER_CONTEXT),
                                       USB_HUB_ALLOCATION_TAG);

    if (NewDevice == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(NewDevice, sizeof(USB_HUB_DRIVER_CONTEXT));
    NewDevice->UsbCoreHandle = INVALID_HANDLE;

    //
    // Attempt to attach to the USB core.
    //

    Status = UsbDriverAttach(DeviceToken,
                             UsbHubDriver,
                             &(NewDevice->UsbCoreHandle));

    if (!KSUCCESS(Status)) {
        goto AddDeviceEnd;
    }

    ASSERT(NewDevice->UsbCoreHandle != INVALID_HANDLE);

    //
    // Allow the USB core to create some hub context with this device.
    //

    Status = UsbCreateHub(NewDevice->UsbCoreHandle, &(NewDevice->UsbHub));
    if (!KSUCCESS(Status)) {
        goto AddDeviceEnd;
    }

    Status = IoAttachDriverToDevice(Driver, DeviceToken, NewDevice);

AddDeviceEnd:
    if (!KSUCCESS(Status)) {
        if (NewDevice != NULL) {
            if (NewDevice->UsbCoreHandle != INVALID_HANDLE) {
                UsbDeviceClose(NewDevice->UsbCoreHandle);
            }

            MmFreeNonPagedPool(NewDevice);
        }
    }

    return Status;
}

VOID
UsbHubDispatchStateChange (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    )

/*++

Routine Description:

    This routine handles State Change IRPs.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    DeviceContext - Supplies the context pointer supplied by the driver when it
        attached itself to the driver stack. Presumably this pointer contains
        driver-specific device context.

    IrpContext - Supplies the context pointer supplied by the driver when
        the IRP was created.

Return Value:

    None.

--*/

{

    PUSB_HUB_DRIVER_CONTEXT Device;
    KSTATUS Status;

    ASSERT(Irp->MajorCode == IrpMajorStateChange);

    Device = (PUSB_HUB_DRIVER_CONTEXT)DeviceContext;

    //
    // If the device structure is NULL, then the USB hub driver is acting as a
    // bus driver. Complete some IRPs by default.
    //

    if (Device == NULL) {
        switch (Irp->MinorCode) {
        case IrpMinorRemoveDevice:
        case IrpMinorQueryResources:
        case IrpMinorStartDevice:
        case IrpMinorQueryChildren:
            if (Irp->Direction == IrpUp) {
                IoCompleteIrp(UsbHubDriver, Irp, STATUS_SUCCESS);
            }

            break;

        default:
            break;
        }

    //
    // If the device is non-NULL, the driver is acting as a function driver for
    // the USB hub.
    //

    } else {
        switch (Irp->MinorCode) {
        case IrpMinorQueryResources:
            if (Irp->Direction == IrpUp) {
                IoCompleteIrp(UsbHubDriver, Irp, STATUS_SUCCESS);
            }

            break;

        case IrpMinorStartDevice:

            //
            // Attempt to fire the thing up if the bus has already started it.
            //

            if (Irp->Direction == IrpUp) {
                Status = UsbHubpStartDevice(Irp, Device);
                if (!KSUCCESS(Status)) {
                    IoCompleteIrp(UsbHubDriver, Irp, Status);
                }
            }

            break;

        case IrpMinorQueryChildren:
            if (Irp->Direction == IrpUp) {
                UsbHubpEnumerateChildren(Irp, Device);
            }

            break;

        case IrpMinorRemoveDevice:
            if (Irp->Direction == IrpUp) {
                UsbHubpRemoveDevice(Irp, Device);
            }

            break;

        //
        // For all other IRPs, do nothing.
        //

        default:
            break;
        }
    }

    return;
}

VOID
UsbHubDispatchOpen (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    )

/*++

Routine Description:

    This routine handles Open IRPs.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    DeviceContext - Supplies the context pointer supplied by the driver when it
        attached itself to the driver stack. Presumably this pointer contains
        driver-specific device context.

    IrpContext - Supplies the context pointer supplied by the driver when
        the IRP was created.

Return Value:

    None.

--*/

{

    return;
}

VOID
UsbHubDispatchClose (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    )

/*++

Routine Description:

    This routine handles Close IRPs.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    DeviceContext - Supplies the context pointer supplied by the driver when it
        attached itself to the driver stack. Presumably this pointer contains
        driver-specific device context.

    IrpContext - Supplies the context pointer supplied by the driver when
        the IRP was created.

Return Value:

    None.

--*/

{

    return;
}

VOID
UsbHubDispatchIo (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    )

/*++

Routine Description:

    This routine handles I/O IRPs.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    DeviceContext - Supplies the context pointer supplied by the driver when it
        attached itself to the driver stack. Presumably this pointer contains
        driver-specific device context.

    IrpContext - Supplies the context pointer supplied by the driver when
        the IRP was created.

Return Value:

    None.

--*/

{

    return;
}

VOID
UsbHubDispatchSystemControl (
    PIRP Irp,
    PVOID DeviceContext,
    PVOID IrpContext
    )

/*++

Routine Description:

    This routine handles System Control IRPs.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    DeviceContext - Supplies the context pointer supplied by the driver when it
        attached itself to the driver stack. Presumably this pointer contains
        driver-specific device context.

    IrpContext - Supplies the context pointer supplied by the driver when
        the IRP was created.

Return Value:

    None.

--*/

{

    ASSERT(Irp->MajorCode == IrpMajorSystemControl);

    //
    // Do no processing on any IRPs. Let them flow.
    //

    return;
}

KSTATUS
UsbHubpStartDevice (
    PIRP Irp,
    PUSB_HUB_DRIVER_CONTEXT Device
    )

/*++

Routine Description:

    This routine starts up the USB Hub.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    Device - Supplies a pointer to this hub device.

Return Value:

    Status code.

--*/

{

    KSTATUS Status;

    Status = UsbStartHub(Device->UsbHub);
    if (!KSUCCESS(Status)) {
        goto StartDeviceEnd;
    }

StartDeviceEnd:
    return Status;
}

VOID
UsbHubpEnumerateChildren (
    PIRP Irp,
    PUSB_HUB_DRIVER_CONTEXT Device
    )

/*++

Routine Description:

    This routine enumerates the USB Hub's children.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    Device - Supplies a pointer to this hub device.

Return Value:

    None.

--*/

{

    KSTATUS Status;

    //
    // Forward this on to the USB core to figure out.
    //

    Status = UsbHubQueryChildren(Irp, Device->UsbHub);
    IoCompleteIrp(UsbHubDriver, Irp, Status);
    return;
}

VOID
UsbHubpRemoveDevice (
    PIRP Irp,
    PUSB_HUB_DRIVER_CONTEXT Device
    )

/*++

Routine Description:

    This routine removes the USB hub device.

Arguments:

    Irp - Supplies a pointer to the I/O request packet.

    Device - Supplies a pointer to this hub device.

Return Value:

    None.

--*/

{

    //
    // Set the device for removal in USB core. This will mark the device as
    // disconnected and cancel all of its transfers.
    //

    UsbDetachDevice(Device->UsbCoreHandle);

    //
    // Destroy the hub. This will remove all of the device's children and
    // destroy any hub-specific state.
    //

    UsbDestroyHub(Device->UsbHub);

    //
    // Release the reference on the USB core handle that was taken when the hub
    // device was added.
    //

    UsbDeviceClose(Device->UsbCoreHandle);

    //
    // Free the hub context.
    //

    MmFreeNonPagedPool(Device);
    return;
}
