/*
 * PROJECT:     ReactOS Universal Serial Bus Bulk Driver Library
 * LICENSE:     GPL - See COPYING in the top level directory
 * FILE:        lib/drivers/libusb/hub_controller.cpp
 * PURPOSE:     USB Common Driver Library.
 * PROGRAMMERS:
 *              Michael Martin (michael.martin@reactos.org)
 *              Johannes Anderwald (johannes.anderwald@reactos.org)
 */

#include "libusb.h"

#define NDEBUG
#include <debug.h>

VOID NTAPI StatusChangeEndpointCallBack(
    PVOID Context);

class CHubController : public IHubController,
                       public IDispatchIrp
{
public:
    STDMETHODIMP QueryInterface( REFIID InterfaceId, PVOID* Interface);

    STDMETHODIMP_(ULONG) AddRef()
    {
        InterlockedIncrement(&m_Ref);
        return m_Ref;
    }
    STDMETHODIMP_(ULONG) Release()
    {
        InterlockedDecrement(&m_Ref);

        if (!m_Ref)
        {
            delete this;
            return 0;
        }
        return m_Ref;
    }

    // IHubController interface functions
    virtual NTSTATUS Initialize(IN PDRIVER_OBJECT DriverObject, IN PHCDCONTROLLER Controller, IN PUSBHARDWAREDEVICE Device, IN BOOLEAN IsRootHubDevice, IN ULONG DeviceAddress);
    virtual NTSTATUS GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject);
    virtual NTSTATUS GetHubControllerSymbolicLink(ULONG BufferLength, PVOID Buffer, PULONG RequiredLength);

    // IDispatchIrp interface functions
    virtual NTSTATUS HandlePnp(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
    virtual NTSTATUS HandlePower(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
    virtual NTSTATUS HandleDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);
    virtual NTSTATUS HandleSystemControl(IN PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp);

    // local functions
    NTSTATUS HandleQueryInterface(PIO_STACK_LOCATION IoStack);
    NTSTATUS SetDeviceInterface(BOOLEAN bEnable);
    NTSTATUS CreatePDO(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT * OutDeviceObject);
    PUSBHARDWAREDEVICE GetUsbHardware();
    ULONG AcquireDeviceAddress();
    VOID ReleaseDeviceAddress(ULONG DeviceAddress);
    BOOLEAN ValidateUsbDevice(PUSBDEVICE UsbDevice);
    NTSTATUS AddUsbDevice(PUSBDEVICE UsbDevice);
    NTSTATUS RemoveUsbDevice(PUSBDEVICE UsbDevice);
    VOID SetNotification(PVOID CallbackContext, PRH_INIT_CALLBACK CallbackRoutine);
    // internal ioctl routines
    NTSTATUS HandleGetDescriptor(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleGetDescriptorFromInterface(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleClassDevice(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleGetStatusFromDevice(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleSelectConfiguration(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleSelectInterface(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleClassOther(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleClassInterface(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleClassEndpoint(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleVendorDevice(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleBulkOrInterruptTransfer(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleIsochronousTransfer(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleClearStall(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleSyncResetAndClearStall(IN OUT PIRP Irp, PURB Urb);
    NTSTATUS HandleAbortPipe(IN OUT PIRP Irp, PURB Urb);

    friend VOID NTAPI StatusChangeEndpointCallBack(PVOID Context);

    // constructor / destructor
    CHubController(IUnknown *OuterUnknown){}
    virtual ~CHubController(){}

protected:
    LONG m_Ref;
    PHCDCONTROLLER m_Controller;
    PUSBHARDWAREDEVICE m_Hardware;
    BOOLEAN m_IsRootHubDevice;
    ULONG m_DeviceAddress;

    BOOLEAN m_InterfaceEnabled;
    UNICODE_STRING m_HubDeviceInterfaceString;

    PDEVICE_OBJECT m_HubControllerDeviceObject;
    PDRIVER_OBJECT m_DriverObject;

    PVOID m_HubCallbackContext;
    PRH_INIT_CALLBACK m_HubCallbackRoutine;

    USB_DEVICE_DESCRIPTOR m_DeviceDescriptor;

    KSPIN_LOCK m_Lock;
    RTL_BITMAP m_DeviceAddressBitmap;
    PULONG m_DeviceAddressBitmapBuffer;
    LIST_ENTRY m_UsbDeviceList;
    PIRP m_PendingSCEIrp;
    LPCSTR m_USBType;


    //Internal Functions
    BOOLEAN QueryStatusChangeEndpoint(PIRP Irp);
};

typedef struct
{
    LIST_ENTRY Entry;
    PUSBDEVICE Device;
}USBDEVICE_ENTRY, *PUSBDEVICE_ENTRY;

/* Lifted from Linux with slight changes */
const UCHAR ROOTHUB2_DEVICE_DESCRIPTOR [] =
{
    0x12,       /*  bLength; */
    USB_DEVICE_DESCRIPTOR_TYPE,       /*  bDescriptorType; Device */
    0x00, 0x20, /*  bcdUSB; v1.1 */
    USB_DEVICE_CLASS_HUB,       /*  bDeviceClass; HUB_CLASSCODE */
    0x01,       /*  bDeviceSubClass; */
    0x00,       /*  bDeviceProtocol; [ low/full speeds only ] */
    0x08,       /*  bMaxPacketSize0; 8 Bytes */
    /* Fill Vendor and Product in when init root hub */
    0x00, 0x00, /*  idVendor; */
    0x00, 0x00, /*  idProduct; */
    0x00, 0x00, /*  bcdDevice */
    0x00,       /*  iManufacturer; */
    0x00,       /*  iProduct; */
    0x00,       /*  iSerialNumber; */
    0x01        /*  bNumConfigurations; */

};

const USB_CONFIGURATION_DESCRIPTOR ROOTHUB2_CONFIGURATION_DESCRIPTOR =
{
    sizeof(USB_CONFIGURATION_DESCRIPTOR),
    USB_CONFIGURATION_DESCRIPTOR_TYPE,
    sizeof(USB_CONFIGURATION_DESCRIPTOR) + sizeof(USB_INTERFACE_DESCRIPTOR) + sizeof(USB_ENDPOINT_DESCRIPTOR),
    1,
    1,
    0,
    0x40, /* self powered */
    0x0
};

const USB_INTERFACE_DESCRIPTOR ROOTHUB2_INTERFACE_DESCRIPTOR =
{
    sizeof(USB_INTERFACE_DESCRIPTOR),                            /* bLength */
    USB_INTERFACE_DESCRIPTOR_TYPE,                               /* bDescriptorType; Interface */
    0,                                                           /* bInterfaceNumber; */
    0,                                                           /* bAlternateSetting; */
    0x1,                                                         /* bNumEndpoints; */
    0x09,                                                        /* bInterfaceClass; HUB_CLASSCODE */
    0x01,                                                        /* bInterfaceSubClass; */
    0x00,                                                        /* bInterfaceProtocol: */
    0x00,                                                        /* iInterface; */
};

const USB_ENDPOINT_DESCRIPTOR ROOTHUB2_ENDPOINT_DESCRIPTOR =
{
    sizeof(USB_ENDPOINT_DESCRIPTOR),                             /* bLength */
    USB_ENDPOINT_DESCRIPTOR_TYPE,                                /* bDescriptorType */
    0x81,                                                        /* bEndPointAddress */
    USB_ENDPOINT_TYPE_INTERRUPT,                                 /* bmAttributes */
    0x01,                                                        /* wMaxPacketSize */
    0xC                                                          /* bInterval */
};

//----------------------------------------------------------------------------------------
NTSTATUS
STDMETHODCALLTYPE
CHubController::QueryInterface(
    IN  REFIID refiid,
    OUT PVOID* Output)
{
    return STATUS_UNSUCCESSFUL;
}
//----------------------------------------------------------------------------------------
NTSTATUS
CHubController::Initialize(
    IN PDRIVER_OBJECT DriverObject,
    IN PHCDCONTROLLER Controller,
    IN PUSBHARDWAREDEVICE Device,
    IN BOOLEAN IsRootHubDevice,
    IN ULONG DeviceAddress)
{
    NTSTATUS Status;
    PCOMMON_DEVICE_EXTENSION DeviceExtension;
    USHORT VendorID, DeviceID;
    ULONG Dummy1;

    //
    // initialize members
    //
    m_Controller = Controller;
    m_Hardware = Device;
    m_IsRootHubDevice = IsRootHubDevice;
    m_DeviceAddress = DeviceAddress;
    m_DriverObject = DriverObject;
    m_USBType = m_Hardware->GetUSBType();
    KeInitializeSpinLock(&m_Lock);
    InitializeListHead(&m_UsbDeviceList);

    //
    // allocate device address bitmap buffer
    //
    m_DeviceAddressBitmapBuffer = (PULONG)ExAllocatePoolWithTag(NonPagedPool, 16, TAG_USBLIB);
    if (!m_DeviceAddressBitmapBuffer)
    {
        //
        // no memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // initialize device address bitmap
    //
    RtlInitializeBitMap(&m_DeviceAddressBitmap, m_DeviceAddressBitmapBuffer, 128);
    RtlClearAllBits(&m_DeviceAddressBitmap);

    //
    // create PDO
    //
    Status = CreatePDO(m_DriverObject, &m_HubControllerDeviceObject);
    if (!NT_SUCCESS(Status))
    {
        //
        // failed to create hub device object
        //
        return Status;
    }

    //
    // get device extension
    //
    DeviceExtension = (PCOMMON_DEVICE_EXTENSION)m_HubControllerDeviceObject->DeviceExtension;

    //
    // initialize device extension
    //
    DeviceExtension->IsFDO = FALSE;
    DeviceExtension->IsHub = TRUE; //FIXME
    DeviceExtension->Dispatcher = PDISPATCHIRP(this);

    //
    // intialize device descriptor
    //
    C_ASSERT(sizeof(USB_DEVICE_DESCRIPTOR) == sizeof(ROOTHUB2_DEVICE_DESCRIPTOR));
    RtlMoveMemory(&m_DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR));

    if (NT_SUCCESS(m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &Dummy1, &Dummy1)))
    {
        //
        // update device descriptor
        //
        m_DeviceDescriptor.idVendor = VendorID;
        m_DeviceDescriptor.idProduct = DeviceID;
        m_DeviceDescriptor.bcdUSB = 0x200; //FIXME
    }

    //
    // Set the SCE Callback that the Hardware Device will call on port status change
    //
    Device->SetStatusChangeEndpointCallBack((PVOID)StatusChangeEndpointCallBack, this);

    //
    // clear init flag
    //
    m_HubControllerDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

    return STATUS_SUCCESS;
}

//
// Queries the ports to see if there has been a device connected or removed.
//
BOOLEAN
CHubController::QueryStatusChangeEndpoint(
    PIRP Irp)
{
    ULONG PortCount, PortId;
    PIO_STACK_LOCATION IoStack;
    USHORT PortStatus, PortChange;
    PURB Urb;
    PUCHAR TransferBuffer;
    UCHAR Changed = FALSE;

    //
    // get current stack location
    //
    IoStack = IoGetCurrentIrpStackLocation(Irp);
    ASSERT(IoStack);

    //
    // Get the Urb
    //
    Urb = (PURB)IoStack->Parameters.Others.Argument1;
    ASSERT(Urb);

    //
    // Get the number of ports and check each one for device connected
    //
    m_Hardware->GetDeviceDetails(NULL, NULL, &PortCount, NULL);
    DPRINT("[%s] SCE Request %p TransferBufferLength %lu Flags %x MDL %p\n", m_USBType, Urb->UrbBulkOrInterruptTransfer.TransferBuffer, Urb->UrbBulkOrInterruptTransfer.TransferBufferLength, Urb->UrbBulkOrInterruptTransfer.TransferFlags, Urb->UrbBulkOrInterruptTransfer.TransferBufferMDL);

    TransferBuffer = (PUCHAR)Urb->UrbBulkOrInterruptTransfer.TransferBuffer;

    //
    // Loop the ports
    //
    for (PortId = 0; PortId < PortCount; PortId++)
    {
        m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange);

        DPRINT("[%s] Port %d: Status %x, Change %x\n", m_USBType, PortId, PortStatus, PortChange);


        //
        // If there's a flag in PortChange return TRUE so the SCE Irp will be completed
        //
        if (PortChange != 0)
        {
            DPRINT1("[%s] Change state on port %d\n", m_USBType, PortId);
            // Set the value for the port number
             *TransferBuffer = 1 << ((PortId + 1) & 7);
            Changed = TRUE;
        }
    }

    return Changed;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::GetHubControllerDeviceObject(PDEVICE_OBJECT * HubDeviceObject)
{
    //
    // store controller object
    //
    *HubDeviceObject = m_HubControllerDeviceObject;

    return STATUS_SUCCESS;
}
//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::GetHubControllerSymbolicLink(
    ULONG BufferLength,
    PVOID Buffer,
    PULONG RequiredLength)
{
    if (!m_InterfaceEnabled)
    {
        //
        // device interface not yet enabled
        //
        return STATUS_UNSUCCESSFUL;
    }

    if (BufferLength < (ULONG)m_HubDeviceInterfaceString.Length - 8)
    {
        //
        // buffer too small
        // length is without '\??\'
        //
        *RequiredLength = m_HubDeviceInterfaceString.Length- 8;

        //
        // done
        //
        return STATUS_BUFFER_OVERFLOW;
    }

    //
    // copy symbolic link
    //
    RtlCopyMemory(Buffer, &m_HubDeviceInterfaceString.Buffer[4], m_HubDeviceInterfaceString.Length - 8);

    //
    // store length, length is without '\??\'
    //
    *RequiredLength = m_HubDeviceInterfaceString.Length - 8;

    //
    // done
    //
    return STATUS_SUCCESS;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandlePnp(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp)
{
    PIO_STACK_LOCATION IoStack;
    PDEVICE_CAPABILITIES DeviceCapabilities;
    PPNP_BUS_INFORMATION BusInformation;
    PDEVICE_RELATIONS DeviceRelations;
    NTSTATUS Status;
    ULONG Index = 0;
    SIZE_T Length;
    USHORT VendorID, DeviceID;
    ULONG HiSpeed, NumPorts;
    WCHAR Buffer[300];
    LPWSTR DeviceName;

    //
    // get current stack location
    //
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    switch(IoStack->MinorFunction)
    {
        case IRP_MN_START_DEVICE:
        {
            DPRINT("[%s] HandlePnp IRP_MN_START_DEVICE\n", m_USBType);
            //
            // register device interface
            //
            Status = SetDeviceInterface(TRUE);
            break;
        }
        case IRP_MN_QUERY_STOP_DEVICE:
        case IRP_MN_QUERY_REMOVE_DEVICE:
        {
            //
            // sure
            //
            Status = STATUS_SUCCESS;
            break;
        }
        case IRP_MN_QUERY_ID:
        {
            DPRINT("[%s] HandlePnp IRP_MN_QUERY_ID Type %x\n", m_USBType, IoStack->Parameters.QueryId.IdType);

            if (IoStack->Parameters.QueryId.IdType == BusQueryDeviceID)
            {
                if (m_Hardware)
                {
                    //
                    // query device id
                    //
                    Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed);

                    if (HiSpeed == 0x200)
                    {
                        //
                        // USB 2.0 hub
                        //
                        swprintf(Buffer, L"USB\\ROOT_HUB20");
                    }
                    else
                    {
                        //
                        // USB 1.1 hub
                        //
                        swprintf(Buffer, L"USB\\ROOT_HUB");
                    }

                    //
                    // calculate length
                    //
                    Length = (wcslen(Buffer) + 1);

                    //
                    // allocate buffer
                    //
                    DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Length * sizeof(WCHAR), TAG_USBLIB);

                    if (!DeviceName)
                    {
                        //
                        // no memory
                        //
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                        break;
                    }

                    //
                    // copy device name
                    //
                    wcscpy(DeviceName, Buffer);

                    //
                    // store result
                    //
                    Irp->IoStatus.Information = (ULONG_PTR)DeviceName;
                    Status = STATUS_SUCCESS;
                    break;
                 }
                 Status = STATUS_UNSUCCESSFUL;
                 PC_ASSERT(0);
                 break;
            }

            if (IoStack->Parameters.QueryId.IdType == BusQueryHardwareIDs)
            {
                if (m_Hardware)
                {
                    //
                    // query device id
                    //
                    Status = m_Hardware->GetDeviceDetails(&VendorID, &DeviceID, &NumPorts, &HiSpeed);

                    if (!NT_SUCCESS(Status))
                    {
                         DPRINT1("[%s] HandlePnp> failed to get hardware id %x\n", m_USBType, Status);
                         VendorID = 0x8086;
                         DeviceID = 0x3A37;
                    }

                    if (HiSpeed == 0x200)
                    {
                        //
                        // USB 2.0 hub
                        //
                        Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1;
                        Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20&VID%04x&PID%04x", VendorID, DeviceID) + 1;
                        Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB20") + 1;
                    }
                    else
                    {
                        //
                        // USB 1.1 hub
                        //
                        Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x&REV0000", VendorID, DeviceID) + 1;
                        Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB&VID%04x&PID%04x", VendorID, DeviceID) + 1;
                        Index += swprintf(&Buffer[Index], L"USB\\ROOT_HUB") + 1;
                    }

                    Buffer[Index] = UNICODE_NULL;
                    Index++;

                    //
                    // allocate buffer
                    //
                    DeviceName = (LPWSTR)ExAllocatePoolWithTag(PagedPool, Index * sizeof(WCHAR), TAG_USBLIB);

                    if (!DeviceName)
                    {
                        //
                        // no memory
                        //
                        Status = STATUS_INSUFFICIENT_RESOURCES;
                        break;
                    }

                    //
                    // copy device name
                    //
                    RtlMoveMemory(DeviceName, Buffer, Index * sizeof(WCHAR));

                    //
                    // store result
                    //
                    Irp->IoStatus.Information = (ULONG_PTR)DeviceName;
                    Status = STATUS_SUCCESS;
                    break;
                }
            }
            // Here we should leave Status as is.
            Status = Irp->IoStatus.Status;
            break;
        }
        case IRP_MN_QUERY_CAPABILITIES:
        {
            DPRINT("[%s] HandlePnp IRP_MN_QUERY_CAPABILITIES\n", m_USBType);

            DeviceCapabilities = (PDEVICE_CAPABILITIES)IoStack->Parameters.DeviceCapabilities.Capabilities;

            DeviceCapabilities->LockSupported = FALSE;
            DeviceCapabilities->EjectSupported = FALSE;
            DeviceCapabilities->Removable = FALSE;
            DeviceCapabilities->DockDevice = FALSE;
            DeviceCapabilities->UniqueID = FALSE;
            DeviceCapabilities->SilentInstall = FALSE;
            DeviceCapabilities->RawDeviceOK = FALSE;
            DeviceCapabilities->SurpriseRemovalOK = FALSE;
            DeviceCapabilities->Address = 0;
            DeviceCapabilities->UINumber = 0;
            DeviceCapabilities->DeviceD2 = 1;

            /* FIXME */
            DeviceCapabilities->HardwareDisabled = FALSE;
            DeviceCapabilities->NoDisplayInUI = FALSE;
            DeviceCapabilities->DeviceState[0] = PowerDeviceD0;
            for (Index = 1; Index < PowerSystemMaximum; Index++)
                DeviceCapabilities->DeviceState[Index] = PowerDeviceD3;
            DeviceCapabilities->DeviceWake = PowerDeviceUnspecified;
            DeviceCapabilities->D1Latency = 0;
            DeviceCapabilities->D2Latency = 0;
            DeviceCapabilities->D3Latency = 0;

            Status = STATUS_SUCCESS;
            break;
        }
        case IRP_MN_QUERY_INTERFACE:
        {
            DPRINT("[%s] HandlePnp IRP_MN_QUERY_INTERFACE\n", m_USBType);

            //
            // handle device interface requests
            //
            Status = HandleQueryInterface(IoStack);

            //
            // If a bus driver does not export the requested interface, it
            // should leave Status as is.
            //
            if (Status == STATUS_NOT_SUPPORTED)
                Status = Irp->IoStatus.Status;

            break;
        }
        case IRP_MN_REMOVE_DEVICE:
        {
            DPRINT("[%s] HandlePnp IRP_MN_REMOVE_DEVICE\n", m_USBType);

            //
            // deactivate device interface for BUS PDO
            //
            SetDeviceInterface(FALSE);

            //
            // complete the request first
            //
            Irp->IoStatus.Status = STATUS_SUCCESS;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);

            //
            // now delete device
            //
            IoDeleteDevice(m_HubControllerDeviceObject);

            //
            // nullify pointer
            //
            m_HubControllerDeviceObject = 0;

            //
            // done
            //
            return STATUS_SUCCESS;
        }
        case IRP_MN_QUERY_DEVICE_RELATIONS:
        {
            DPRINT("[%s] HandlePnp IRP_MN_QUERY_DEVICE_RELATIONS Type %x\n", m_USBType, IoStack->Parameters.QueryDeviceRelations.Type);

            if (IoStack->Parameters.QueryDeviceRelations.Type == TargetDeviceRelation)
            {
                //
                // allocate device relations
                //
                DeviceRelations = (PDEVICE_RELATIONS)ExAllocatePoolWithTag(PagedPool, sizeof(DEVICE_RELATIONS), TAG_USBLIB);
                if (!DeviceRelations)
                {
                    //
                    // no memory
                    //
                    Status = STATUS_INSUFFICIENT_RESOURCES;
                    break;
                }

                //
                // initialize device relations
                //
                DeviceRelations->Count = 1;
                DeviceRelations->Objects[0] = DeviceObject;
                ObReferenceObject(DeviceObject);

                //
                // done
                //
                Status = STATUS_SUCCESS;
                Irp->IoStatus.Information = (ULONG_PTR)DeviceRelations;
            }
            else
            {
                //
                // not handled
                //
                Status = Irp->IoStatus.Status;
            }
            break;
        }
        case IRP_MN_QUERY_BUS_INFORMATION:
        {
            DPRINT("[%s] HandlePnp IRP_MN_QUERY_BUS_INFORMATION\n", m_USBType);

            //
            // allocate buffer for bus information
            //
            BusInformation = (PPNP_BUS_INFORMATION)ExAllocatePool(PagedPool, sizeof(PNP_BUS_INFORMATION));
            if (BusInformation)
            {
                //
                // copy BUS guid
                //
                RtlMoveMemory(&BusInformation->BusTypeGuid, &GUID_BUS_TYPE_USB, sizeof(GUID));

                //
                // set bus type
                //
                BusInformation->LegacyBusType = PNPBus;
                BusInformation->BusNumber = 0;

                Status = STATUS_SUCCESS;
                Irp->IoStatus.Information = (ULONG_PTR)BusInformation;
            }
            else
            {
                //
                // no memory
                //
                Status = STATUS_INSUFFICIENT_RESOURCES;
            }
            break;
        }
        case IRP_MN_STOP_DEVICE:
        {
            DPRINT("[%s] HandlePnp IRP_MN_STOP_DEVICE\n", m_USBType);
            //
            // stop device
            //
            Status = STATUS_SUCCESS;
            break;
        }
        case IRP_MN_SURPRISE_REMOVAL:
        {
            DPRINT("[%s] HandlePnp IRP_MN_SURPRISE_REMOVAL\n", m_USBType);
            Status = STATUS_SUCCESS;
            break;
        }
        default:
        {
            //
            // ignore request with default status
            //
            Status = Irp->IoStatus.Status;
            break;
        }
    }

    //
    // complete request
    //
    Irp->IoStatus.Status = Status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    //
    // done
    //
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandlePower(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp)
{
    NTSTATUS Status;
    Status = Irp->IoStatus.Status;
    PoStartNextPowerIrp(Irp);
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleSystemControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp)
{
    NTSTATUS Status;
    Status = Irp->IoStatus.Status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleIsochronousTransfer(
    IN OUT PIRP Irp,
    PURB Urb)
{
    PUSBDEVICE UsbDevice;
    PUSB_ENDPOINT_DESCRIPTOR EndPointDesc = NULL;

    //
    // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request
    //
    EndPointDesc = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbIsochronousTransfer.PipeHandle;

    if (!EndPointDesc)
    {
        DPRINT1("[%s] Error No EndpointDesc\n", m_USBType);
        Urb->UrbIsochronousTransfer.Hdr.Status = USBD_STATUS_INVALID_PIPE_HANDLE;
        return STATUS_INVALID_PARAMETER;
    }

    //
    // sanity checks
    //
    ASSERT(EndPointDesc);
    DPRINT("[%s] HandleIsochronousTransfer EndPointDesc %p Address %x bmAttributes %x\n", m_USBType, EndPointDesc, EndPointDesc->bEndpointAddress, EndPointDesc->bmAttributes);
    ASSERT((EndPointDesc->bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_ISOCHRONOUS);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleIsochronousTransfer invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

    return UsbDevice->SubmitIrp(Irp);
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleBulkOrInterruptTransfer(
    IN OUT PIRP Irp,
    PURB Urb)
{
    PUSBDEVICE UsbDevice;
    PUSB_ENDPOINT EndPointDesc = NULL;
    //
    // First check if the request is for the Status Change Endpoint
    //

    //
    // Is the Request for the root hub
    //
    if (Urb->UrbHeader.UsbdDeviceHandle == PVOID(this)  || Urb->UrbHeader.UsbdDeviceHandle == NULL)
    {
        ASSERT(m_PendingSCEIrp == NULL);
        if (QueryStatusChangeEndpoint(Irp))
        {
            //
            // We've seen a change already, so return immediately
            //
            return STATUS_SUCCESS;
        }

        //
        // Else pend the IRP, to be completed when a device connects or disconnects.
        //
        DPRINT("[%s] Pending SCE Irp\n", m_USBType);
        m_PendingSCEIrp = Irp;
        IoMarkIrpPending(Irp);
        return STATUS_PENDING;
    }

    //
    // Check PipeHandle to determine if this is a Bulk or Interrupt Transfer Request
    //
    EndPointDesc = (PUSB_ENDPOINT)Urb->UrbBulkOrInterruptTransfer.PipeHandle;

    //
    // sanity checks
    //
    ASSERT(EndPointDesc);
    ASSERT((EndPointDesc->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_BULK || (EndPointDesc->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK) == USB_ENDPOINT_TYPE_INTERRUPT);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleBulkOrInterruptTransfer invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);
    return UsbDevice->SubmitIrp(Irp);
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleClassOther(
    IN OUT PIRP Irp,
    PURB Urb)
{
    NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
    USHORT PortStatus = 0, PortChange = 0;
    PUSHORT Buffer;
    ULONG NumPort;
    ULONG PortId;

    DPRINT("[%s] HandleClassOther> Request %x Value %x\n", m_USBType, Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value);

    //
    // get number of ports available
    //
    Status = m_Hardware->GetDeviceDetails(NULL, NULL, &NumPort, NULL);
    PC_ASSERT(Status == STATUS_SUCCESS);

    //
    // sanity check
    //
    PC_ASSERT(Urb->UrbControlVendorClassRequest.Index - 1 < (USHORT)NumPort);

    //
    // port range reported start from 1 -n
    // convert back port id so it matches the hardware
    //
    PortId = Urb->UrbControlVendorClassRequest.Index - 1;

    //
    // check request code
    //
    switch(Urb->UrbControlVendorClassRequest.Request)
    {
        case USB_REQUEST_GET_STATUS:
        {
            //
            // sanity check
            //
            PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength == sizeof(USHORT) * 2);
            PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer);

            //
            // get port status
            //
            Status = m_Hardware->GetPortStatus(PortId, &PortStatus, &PortChange);

            if (NT_SUCCESS(Status))
            {
                //
                // request contains buffer of 2 ushort which are used from submitting port status and port change status
                //
                DPRINT("[%s] PortId %x PortStatus %x PortChange %x\n", m_USBType, PortId, PortStatus, PortChange);
                Buffer = (PUSHORT)Urb->UrbControlVendorClassRequest.TransferBuffer;

                //
                // store status, then port change
                //
                *Buffer = PortStatus;
                Buffer++;
                *Buffer = PortChange;
            }

            //
            // done
            //
            break;
        }
        case USB_REQUEST_CLEAR_FEATURE:
        {
            switch (Urb->UrbControlVendorClassRequest.Value)
            {
                case C_PORT_CONNECTION:
                    Status = m_Hardware->ClearPortStatus(PortId, C_PORT_CONNECTION);
                    break;
                case C_PORT_RESET:
                    Status = m_Hardware->ClearPortStatus(PortId, C_PORT_RESET);
                    break;
                default:
                    DPRINT("[%s] Unknown Value for Clear Feature %x \n", m_USBType, Urb->UrbControlVendorClassRequest.Value);
                    break;
           }

            break;
        }
        case USB_REQUEST_SET_FEATURE:
        {
            //
            // request set feature
            //
            switch(Urb->UrbControlVendorClassRequest.Value)
            {
                case PORT_ENABLE:
                {
                    //
                    // port enable is a no-op for EHCI
                    //
                    Status = STATUS_SUCCESS;
                    break;
                }

                case PORT_SUSPEND:
                {
                    //
                    // set suspend port feature
                    //
                    Status = m_Hardware->SetPortFeature(PortId, PORT_SUSPEND);
                    break;
                }
                case PORT_POWER:
                {
                    //
                    // set power feature on port
                    //
                    Status = m_Hardware->SetPortFeature(PortId, PORT_POWER);
                    break;
                }

                case PORT_RESET:
                {
                    //
                    // reset port feature
                    //
                    Status = m_Hardware->SetPortFeature(PortId, PORT_RESET);
                    PC_ASSERT(Status == STATUS_SUCCESS);
                    break;
                }
                default:
                    DPRINT1("[%s] Unsupported request id %x\n", m_USBType, Urb->UrbControlVendorClassRequest.Value);
                    PC_ASSERT(FALSE);
            }
            break;
        }
        default:
            DPRINT1("[%s] HandleClassOther Unknown request code %x\n", m_USBType, Urb->UrbControlVendorClassRequest.Request);
            PC_ASSERT(0);
            Status = STATUS_INVALID_DEVICE_REQUEST;
    }
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleSelectConfiguration(
    IN OUT PIRP Irp,
    PURB Urb)
{
    PUSBDEVICE UsbDevice;
    PUSBD_INTERFACE_INFORMATION InterfaceInfo;
    NTSTATUS Status;

    //
    // is the request for the Root Hub
    //
    if (Urb->UrbHeader.UsbdDeviceHandle == NULL)
    {
        //
        // FIXME: support setting device to unconfigured state
        //
        PC_ASSERT(Urb->UrbSelectConfiguration.ConfigurationDescriptor);

        //
        // set device handle
        //
        Urb->UrbSelectConfiguration.ConfigurationHandle = (PVOID)&ROOTHUB2_CONFIGURATION_DESCRIPTOR;

        //
        // copy interface info
        //
        InterfaceInfo = &Urb->UrbSelectConfiguration.Interface;

        InterfaceInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)&ROOTHUB2_INTERFACE_DESCRIPTOR;
        InterfaceInfo->Class = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceClass;
        InterfaceInfo->SubClass = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceSubClass;
        InterfaceInfo->Protocol = ROOTHUB2_INTERFACE_DESCRIPTOR.bInterfaceProtocol;
        InterfaceInfo->Reserved = 0;

        //
        // sanity check
        //
        PC_ASSERT(InterfaceInfo->NumberOfPipes == 1);

        //
        // copy pipe info
        //
        InterfaceInfo->Pipes[0].MaximumPacketSize = ROOTHUB2_ENDPOINT_DESCRIPTOR.wMaxPacketSize;
        InterfaceInfo->Pipes[0].EndpointAddress = ROOTHUB2_ENDPOINT_DESCRIPTOR.bEndpointAddress;
        InterfaceInfo->Pipes[0].Interval = ROOTHUB2_ENDPOINT_DESCRIPTOR.bInterval;
        InterfaceInfo->Pipes[0].PipeType = (USBD_PIPE_TYPE)(ROOTHUB2_ENDPOINT_DESCRIPTOR.bmAttributes & USB_ENDPOINT_TYPE_MASK);
        InterfaceInfo->Pipes[0].PipeHandle = (PVOID)&ROOTHUB2_ENDPOINT_DESCRIPTOR;

        return STATUS_SUCCESS;
    }
    else
    {
        //
        // check if this is a valid usb device handle
        //
        if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
        {
            DPRINT1("[%s] HandleSelectConfiguration invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

            //
            // invalid device handle
            //
            return STATUS_DEVICE_NOT_CONNECTED;
        }

        //
        // get device
        //
        UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

        //
        // select configuration
        //
        Status = UsbDevice->SelectConfiguration(Urb->UrbSelectConfiguration.ConfigurationDescriptor, &Urb->UrbSelectConfiguration.Interface, &Urb->UrbSelectConfiguration.ConfigurationHandle);
        if (NT_SUCCESS(Status))
        {
            // successfully configured device
            Urb->UrbSelectConfiguration.Hdr.Status = USBD_STATUS_SUCCESS;
        }
        return Status;
    }
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleSelectInterface(
    IN OUT PIRP Irp,
    PURB Urb)
{
    PUSBDEVICE UsbDevice;

    //
    // sanity check
    //
    PC_ASSERT(Urb->UrbSelectInterface.ConfigurationHandle);

    //
    // is the request for the Root Hub
    //
    if (Urb->UrbHeader.UsbdDeviceHandle == NULL)
    {
        //
        // no op for root hub
        //
        return STATUS_SUCCESS;
    }
    else
    {
        //
        // check if this is a valid usb device handle
        //
        if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
        {
            DPRINT1("[%s] HandleSelectInterface invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

            //
            // invalid device handle
            //
            return STATUS_DEVICE_NOT_CONNECTED;
        }

        //
        // get device
        //
        UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

        //
        // select interface
        //
        return UsbDevice->SelectInterface(Urb->UrbSelectInterface.ConfigurationHandle, &Urb->UrbSelectInterface.Interface);
    }
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleGetStatusFromDevice(
    IN OUT PIRP Irp,
    PURB Urb)
{
    PUSHORT DeviceStatus;
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
    NTSTATUS Status;
    PUSBDEVICE UsbDevice;

    //
    // sanity checks
    //
    PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBufferLength >= sizeof(USHORT));
    PC_ASSERT(Urb->UrbControlGetStatusRequest.TransferBuffer);

    //
    // get status buffer
    //
    DeviceStatus = (PUSHORT)Urb->UrbControlGetStatusRequest.TransferBuffer;


    if (Urb->UrbHeader.UsbdDeviceHandle == PVOID(this) || Urb->UrbHeader.UsbdDeviceHandle == NULL)
    {
        //
        // FIXME need more flags ?
        //
        *DeviceStatus = USB_PORT_STATUS_CONNECT;
        return STATUS_SUCCESS;
    }

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleGetStatusFromDevice invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);


    //
    // generate setup packet
    //
    CtrlSetup.bRequest = USB_REQUEST_GET_STATUS;
    CtrlSetup.wValue.LowByte = 0;
    CtrlSetup.wValue.HiByte = 0;
    CtrlSetup.wIndex.W = Urb->UrbControlGetStatusRequest.Index;
    CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength;
    CtrlSetup.bmRequestType.B = 0x80;

    if (Urb->UrbHeader.Function == URB_FUNCTION_GET_STATUS_FROM_INTERFACE)
    {
        //
        // add interface type
        //
        CtrlSetup.bmRequestType.B |= 0x01;
    }
    else if (Urb->UrbHeader.Function == URB_FUNCTION_GET_STATUS_FROM_ENDPOINT)
    {
        //
        // add interface type
        //
        CtrlSetup.bmRequestType.B |= 0x02;
    }

    //
    // submit setup packet
    //
    Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
    ASSERT(Status == STATUS_SUCCESS);
    DPRINT1("[%s] HandleGetStatusFromDevice Status %x Length %lu DeviceStatus %x\n", m_USBType, Status, Urb->UrbControlDescriptorRequest.TransferBufferLength, *DeviceStatus);

    //
    // done
    //
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleClassDevice(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
    PUSB_HUB_DESCRIPTOR UsbHubDescriptor;
    ULONG PortCount, Dummy2;
    USHORT Dummy1;
    PUSBDEVICE UsbDevice;
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;

    DPRINT("[%s] HandleClassDevice Request %x Class %x\n", m_USBType, Urb->UrbControlVendorClassRequest.Request, Urb->UrbControlVendorClassRequest.Value >> 8);

    //
    // check class request type
    //
    switch(Urb->UrbControlVendorClassRequest.Request)
    {
        case USB_REQUEST_GET_STATUS:
        {
            //
            // check if this is a valid usb device handle
            //
            if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
            {
                DPRINT1("[%s] HandleClassDevice invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

                //
                // invalid device handle
                //
                return STATUS_DEVICE_NOT_CONNECTED;
            }

            //
            // get device
            //
            UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);


            //
            // generate setup packet
            //
            CtrlSetup.bRequest = USB_REQUEST_GET_STATUS;
            CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value;
            CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index;
            CtrlSetup.wLength = (USHORT)Urb->UrbControlGetStatusRequest.TransferBufferLength;
            CtrlSetup.bmRequestType.B = 0xA0;

            //
            // submit setup packet
            //
            Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
            ASSERT(Status == STATUS_SUCCESS);
            break;
        }
        case USB_REQUEST_GET_DESCRIPTOR:
        {
            switch (Urb->UrbControlVendorClassRequest.Value >> 8)
            {
                case USB_DEVICE_CLASS_RESERVED: // FALL THROUGH
                case USB_DEVICE_CLASS_HUB:
                {
                    if (Urb->UrbHeader.UsbdDeviceHandle == PVOID(this)  || Urb->UrbHeader.UsbdDeviceHandle == NULL)
                    {
                        //
                        // sanity checks
                        //
                        PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer);
                        PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength >= sizeof(USB_HUB_DESCRIPTOR));

                        //
                        // get hub descriptor
                        //
                        UsbHubDescriptor = (PUSB_HUB_DESCRIPTOR)Urb->UrbControlVendorClassRequest.TransferBuffer;

                        //
                        // one hub is handled
                        //
                        UsbHubDescriptor->bDescriptorLength = sizeof(USB_HUB_DESCRIPTOR);
                        Urb->UrbControlVendorClassRequest.TransferBufferLength = sizeof(USB_HUB_DESCRIPTOR);

                        //
                        // type should 0x29 according to msdn
                        //
                        UsbHubDescriptor->bDescriptorType = 0x29;

                        //
                        // get port count
                        //
                        Status = m_Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &PortCount, &Dummy2);
                        PC_ASSERT(Status == STATUS_SUCCESS);

                        //
                        // FIXME: retrieve values
                        //
                        UsbHubDescriptor->bNumberOfPorts = (UCHAR)PortCount;
                        UsbHubDescriptor->wHubCharacteristics = 0x00;
                        UsbHubDescriptor->bPowerOnToPowerGood = 0x01;
                        UsbHubDescriptor->bHubControlCurrent = 0x00;

                        //
                        // done
                        //
                        Status = STATUS_SUCCESS;
                    }
                    else
                    {
                        if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
                        {
                            DPRINT1("[%s] HandleClassDevice invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);
                            //
                            // invalid device handle
                            //
                            return STATUS_DEVICE_NOT_CONNECTED;
                        }

                        //
                        // FIXME: implement support for real hubs
                        //
                        UNIMPLEMENTED;
                        Status = STATUS_NOT_IMPLEMENTED;
                    }
                    break;
               }
               default:
                   DPRINT1("[%s] HandleClassDevice Class %x not implemented\n", m_USBType, Urb->UrbControlVendorClassRequest.Value >> 8);
                   break;
            }
            break;
        }
        default:
        {
            //
            // check if this is a valid usb device handle
            //
            if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
            {
                DPRINT1("[%s] HandleClassDevice invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

                //
                // invalid device handle
                //
                return STATUS_DEVICE_NOT_CONNECTED;
            }

            //
            // get device
            //
            UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

            //
            // generate setup packet
            //
            CtrlSetup.bmRequestType.B = 0;
            CtrlSetup.bmRequestType.Recipient = BMREQUEST_TO_DEVICE;
            CtrlSetup.bmRequestType.Type = BMREQUEST_CLASS;
            CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request;
            CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value;
            CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index;
            CtrlSetup.wLength = (USHORT)Urb->UrbControlVendorClassRequest.TransferBufferLength;

            if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN)
            {
                //
                // data direction is device to host
                //
                CtrlSetup.bmRequestType.Dir = BMREQUEST_DEVICE_TO_HOST;
            }

            //
            // submit setup packet
            //
            Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
            ASSERT(Status == STATUS_SUCCESS);

            break;
        }
    }

    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleGetDescriptorFromInterface(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    PUSBDEVICE UsbDevice;
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
    NTSTATUS Status;

    //
    // sanity check
    //
    ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength);
    ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleGetDescriptorFromInterface invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

    //
    // generate setup packet
    //
    CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
    CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index;
    CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType;
    CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId;
    CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength;
    CtrlSetup.bmRequestType.B = 0x81;

    //
    // submit setup packet
    //
    Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
    if (!NT_SUCCESS(Status))
    {
        DPRINT1("[%s] HandleGetDescriptorFromInterface failed with %x\n", m_USBType, Status);
    }

    //
    // done
    //
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleGetDescriptor(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
    PUCHAR Buffer;
    PUSBDEVICE UsbDevice;
    ULONG Length, BufferLength;

    DPRINT("[%s] HandleGetDescriptor Type %x\n", m_USBType, Urb->UrbControlDescriptorRequest.DescriptorType);

    //
    // check descriptor type
    //
    switch(Urb->UrbControlDescriptorRequest.DescriptorType)
    {
        case USB_DEVICE_DESCRIPTOR_TYPE:
        {
            //
            // sanity check
            //
            PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR));
            PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer);

            if (Urb->UrbHeader.UsbdDeviceHandle == PVOID(this) || Urb->UrbHeader.UsbdDeviceHandle == NULL)
            {
                //
                // copy root hub device descriptor
                //
                RtlCopyMemory((PUCHAR)Urb->UrbControlDescriptorRequest.TransferBuffer, &m_DeviceDescriptor, sizeof(USB_DEVICE_DESCRIPTOR));
                Irp->IoStatus.Information = sizeof(USB_DEVICE_DESCRIPTOR);
                Urb->UrbControlDescriptorRequest.Hdr.Status = USBD_STATUS_SUCCESS;
                Status = STATUS_SUCCESS;
            }
            else
            {
                //
                // check if this is a valid usb device handle
                //
                if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
                {
                    DPRINT1("[%s] HandleGetDescriptor invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

                    //
                    // invalid device handle
                    //
                    return STATUS_DEVICE_NOT_CONNECTED;
                }

                //
                // get device
                //
                UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

                //
                // retrieve device descriptor from device
                //
                UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)Urb->UrbControlDescriptorRequest.TransferBuffer);
                Irp->IoStatus.Information = sizeof(USB_DEVICE_DESCRIPTOR);
                Urb->UrbControlDescriptorRequest.Hdr.Status = USBD_STATUS_SUCCESS;
                Status = STATUS_SUCCESS;
            }
            break;
        }
       case USB_CONFIGURATION_DESCRIPTOR_TYPE:
        {
            //
            // sanity checks
            //
            PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer);
            //
            // From MSDN
            // The caller must allocate a buffer large enough to hold all of this information or the data is truncated without error.
            //
            BufferLength = Urb->UrbControlDescriptorRequest.TransferBufferLength;
            Buffer = (PUCHAR) Urb->UrbControlDescriptorRequest.TransferBuffer;

            if (Urb->UrbHeader.UsbdDeviceHandle == PVOID(this) || Urb->UrbHeader.UsbdDeviceHandle == NULL)
            {
                //
                // request is for the root bus controller
                //
                Length = BufferLength > sizeof(USB_CONFIGURATION_DESCRIPTOR) ?
                    sizeof(USB_CONFIGURATION_DESCRIPTOR) : BufferLength;
                RtlCopyMemory(Buffer, &ROOTHUB2_CONFIGURATION_DESCRIPTOR, Length);

                //
                // Check if we still have some space left
                //
                if(Length == BufferLength)
                {
                    //
                    // We copied all we could
                    //
                    Status = STATUS_SUCCESS;
                    break;
                }
                //
                // Go further
                //
                Buffer += Length;
                BufferLength -= Length;

                //
                // copy interface descriptor template
                //
                Length = BufferLength > sizeof(USB_INTERFACE_DESCRIPTOR) ?
                    sizeof(USB_INTERFACE_DESCRIPTOR) : BufferLength;
                RtlCopyMemory(Buffer, &ROOTHUB2_INTERFACE_DESCRIPTOR, Length);

                //
                // Check if we still have some space left
                //
                if(Length == BufferLength)
                {
                    //
                    // We copied all we could
                    //
                    Status = STATUS_SUCCESS;
                    break;
                }
                //
                // Go further
                //
                Buffer += Length;
                BufferLength -= Length;


                //
                // copy end point descriptor template
                //
                Length = BufferLength > sizeof(USB_ENDPOINT_DESCRIPTOR) ?
                    sizeof(USB_ENDPOINT_DESCRIPTOR) : BufferLength;
                RtlCopyMemory(Buffer, &ROOTHUB2_ENDPOINT_DESCRIPTOR, Length);

                //
                // done
                //
                Status = STATUS_SUCCESS;

            }
            else
            {
                //
                // check if this is a valid usb device handle
                //
                if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
                {
                    DPRINT1("[%s] USB_CONFIGURATION_DESCRIPTOR_TYPE invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

                    //
                    // invalid device handle
                    //
                    return STATUS_DEVICE_NOT_CONNECTED;
                }

                //
                // get device
                //
                UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

                //
                // Allocate temporary buffer
                //
                BufferLength = UsbDevice->GetConfigurationDescriptorsLength();
                Buffer = (PUCHAR)ExAllocatePoolWithTag(NonPagedPool, BufferLength, TAG_USBLIB);
                if(!Buffer)
                {
                    Status = STATUS_NO_MEMORY;
                    break;
                }

                //
                // perform work in IUSBDevice
                //
                UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)Buffer, BufferLength, &Length);

                //
                // Copy what we can
                //
                Length = Urb->UrbControlDescriptorRequest.TransferBufferLength > Length ?
                    Length : Urb->UrbControlDescriptorRequest.TransferBufferLength;
                RtlCopyMemory(Urb->UrbControlDescriptorRequest.TransferBuffer, Buffer, Length);

                //
                // Free temporary buffer
                //
                ExFreePoolWithTag(Buffer, TAG_USBLIB);

                //
                // store result size
                //
                Irp->IoStatus.Information = Length;
                Urb->UrbControlDescriptorRequest.TransferBufferLength = Length;
                Urb->UrbControlDescriptorRequest.Hdr.Status = USBD_STATUS_SUCCESS;
                Status = STATUS_SUCCESS;
            }
            break;
        }
        case USB_STRING_DESCRIPTOR_TYPE:
        {
            //
            // sanity check
            //
            PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBuffer);
            PC_ASSERT(Urb->UrbControlDescriptorRequest.TransferBufferLength);


            //
            // check if this is a valid usb device handle
            //
            if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
            {
                DPRINT1("[%s] USB_STRING_DESCRIPTOR_TYPE invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

                //
                // invalid device handle
                //
                return STATUS_DEVICE_NOT_CONNECTED;
            }

            //
            // get device
            //
            UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

            //
            // generate setup packet
            //
            CtrlSetup.bRequest = USB_REQUEST_GET_DESCRIPTOR;
            CtrlSetup.wValue.LowByte = Urb->UrbControlDescriptorRequest.Index;
            CtrlSetup.wValue.HiByte = Urb->UrbControlDescriptorRequest.DescriptorType;
            CtrlSetup.wIndex.W = Urb->UrbControlDescriptorRequest.LanguageId;
            CtrlSetup.wLength = (USHORT)Urb->UrbControlDescriptorRequest.TransferBufferLength;
            CtrlSetup.bmRequestType.B = 0x80;

            //
            // submit setup packet
            //
            Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlDescriptorRequest.TransferBufferLength, Urb->UrbControlDescriptorRequest.TransferBuffer);
            break;
        }
        default:
            DPRINT1("[%s] CHubController::HandleGetDescriptor DescriptorType %x unimplemented\n", m_USBType, Urb->UrbControlDescriptorRequest.DescriptorType);
            break;
    }

    //
    // done
    //
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleClassEndpoint(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
    NTSTATUS Status;
    PUSBDEVICE UsbDevice;

    //
    // sanity check
    //
    PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer);
    PC_ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength);
    PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleClassEndpoint invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);


    DPRINT1("URB_FUNCTION_CLASS_ENDPOINT\n");
    DPRINT1("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags);
    DPRINT1("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength);
    DPRINT1("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer);
    DPRINT1("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL);
    DPRINT1("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits);
    DPRINT1("Request %x\n", Urb->UrbControlVendorClassRequest.Request);
    DPRINT1("Value %x\n", Urb->UrbControlVendorClassRequest.Value);
    DPRINT1("Index %x\n", Urb->UrbControlVendorClassRequest.Index);

    //
    // initialize setup packet
    //
    CtrlSetup.bmRequestType.B = 0x22; //FIXME: Const.
    CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request;
    CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value;
    CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index;
    CtrlSetup.wLength = (USHORT)Urb->UrbControlVendorClassRequest.TransferBufferLength;

    if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN)
    {
        //
        // data direction is device to host
        //
        CtrlSetup.bmRequestType.B |= 0x80;
    }


    //
    // issue request
    //
    Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer);

    //
    // assert on failure
    //
    PC_ASSERT(NT_SUCCESS(Status));


    //
    // done
    //
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleVendorDevice(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
    PUSBDEVICE UsbDevice;
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;

    //DPRINT("CHubController::HandleVendorDevice Request %x\n", Urb->UrbControlVendorClassRequest.Request);

    //
    // sanity check
    //
    PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleVendorDevice invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);

    //
    // initialize setup packet
    //
    CtrlSetup.bmRequestType.B = 0;
    CtrlSetup.bmRequestType.Recipient = BMREQUEST_TO_DEVICE;
    CtrlSetup.bmRequestType.Type = BMREQUEST_VENDOR;
    CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request;
    CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value;
    CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index;
    CtrlSetup.wLength = (USHORT)Urb->UrbControlVendorClassRequest.TransferBufferLength;

    if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN)
    {
        //
        // data direction is device to host
        //
        CtrlSetup.bmRequestType.Dir = BMREQUEST_DEVICE_TO_HOST;
    }

    //
    // issue request
    //
    Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer);
    if (NT_SUCCESS(Status))
    {
        // success
        Urb->UrbControlVendorClassRequest.Hdr.Status = USBD_STATUS_SUCCESS;
        Irp->IoStatus.Information = Urb->UrbControlVendorClassRequest.TransferBufferLength;
    }

    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleSyncResetAndClearStall(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    NTSTATUS Status = STATUS_SUCCESS;
    PUSB_ENDPOINT EndpointDescriptor;
    ULONG Type;

    //
    // sanity check
    //
    PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle);
    PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST));
    PC_ASSERT(Urb->UrbPipeRequest.PipeHandle);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleSyncResetAndClearStall invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // abort pipe
    //
    Status = HandleAbortPipe(Irp, Urb);
    if (!NT_SUCCESS(Status))
    {
        //
        // failed
        //
        DPRINT1("[%s] failed to reset pipe %x\n", m_USBType, Status);
    }


    //
    // get endpoint descriptor
    //
    EndpointDescriptor = (PUSB_ENDPOINT)Urb->UrbPipeRequest.PipeHandle;

    //
    // get type
    //
    Type = (EndpointDescriptor->EndPointDescriptor.bmAttributes & USB_ENDPOINT_TYPE_MASK);
    if (Type != USB_ENDPOINT_TYPE_ISOCHRONOUS)
    {
        //
        // clear stall
        //
        Status = HandleClearStall(Irp, Urb);
    }
    DPRINT1("[%s] URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL Status %x\n", m_USBType, Status);

    //
    // reset data toggle
    //
    if (NT_SUCCESS(Status))
        EndpointDescriptor->DataToggle = 0x0;

    //
    // done
    //
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleAbortPipe(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    NTSTATUS Status;
    PUSBDEVICE UsbDevice;
    PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor;

    //
    // sanity check
    //
    PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle);
    PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST));
    PC_ASSERT(Urb->UrbPipeRequest.PipeHandle);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleAbortPipe invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get endpoint descriptor
    //
    EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle;

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);


    //
    // issue request
    //
    Status = UsbDevice->AbortPipe(EndpointDescriptor);
    DPRINT1("[%s] URB_FUNCTION_ABORT_PIPE Status %x\n", m_USBType, Status);

    //
    // done
    //
    return Status;
}


//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleClearStall(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
    NTSTATUS Status;
    PUSBDEVICE UsbDevice;
    PUSB_ENDPOINT_DESCRIPTOR EndpointDescriptor;


    //
    // sanity check
    //
    PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle);
    PC_ASSERT(Urb->UrbHeader.Length == sizeof(struct _URB_PIPE_REQUEST));
    PC_ASSERT(Urb->UrbPipeRequest.PipeHandle);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleClearStall invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get endpoint descriptor
    //
    EndpointDescriptor = (PUSB_ENDPOINT_DESCRIPTOR)Urb->UrbPipeRequest.PipeHandle;

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);
    DPRINT1("[%s] URB_FUNCTION_SYNC_CLEAR_STALL\n", m_USBType);

    //
    // initialize setup packet
    //
    CtrlSetup.bmRequestType.B = 0x02;
    CtrlSetup.bRequest = USB_REQUEST_CLEAR_FEATURE;
    CtrlSetup.wValue.W = USB_FEATURE_ENDPOINT_STALL;
    CtrlSetup.wIndex.W = EndpointDescriptor->bEndpointAddress;
    CtrlSetup.wLength = 0;
    CtrlSetup.wValue.W = 0;

    //
    // issue request
    //
    Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, 0, 0);

    DPRINT1("[%s] URB_FUNCTION_CLEAR_STALL Status %x\n", m_USBType, Status);

    //
    // done
    //
    return Status;
}


//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleClassInterface(
    IN OUT PIRP Irp,
    IN OUT PURB Urb)
{
    USB_DEFAULT_PIPE_SETUP_PACKET CtrlSetup;
    NTSTATUS Status;
    PUSBDEVICE UsbDevice;

    //
    // sanity check
    //
    //ASSERT(Urb->UrbControlVendorClassRequest.TransferBuffer || Urb->UrbControlVendorClassRequest.TransferBufferMDL);
    //ASSERT(Urb->UrbControlVendorClassRequest.TransferBufferLength);
    PC_ASSERT(Urb->UrbHeader.UsbdDeviceHandle);

    //
    // check if this is a valid usb device handle
    //
    if (!ValidateUsbDevice(PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle)))
    {
        DPRINT1("[%s] HandleClassInterface invalid device handle %p\n", m_USBType, Urb->UrbHeader.UsbdDeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device
    //
    UsbDevice = PUSBDEVICE(Urb->UrbHeader.UsbdDeviceHandle);


    DPRINT("URB_FUNCTION_CLASS_INTERFACE\n");
    DPRINT("TransferFlags %x\n", Urb->UrbControlVendorClassRequest.TransferFlags);
    DPRINT("TransferBufferLength %x\n", Urb->UrbControlVendorClassRequest.TransferBufferLength);
    DPRINT("TransferBuffer %x\n", Urb->UrbControlVendorClassRequest.TransferBuffer);
    DPRINT("TransferBufferMDL %x\n", Urb->UrbControlVendorClassRequest.TransferBufferMDL);
    DPRINT("RequestTypeReservedBits %x\n", Urb->UrbControlVendorClassRequest.RequestTypeReservedBits);
    DPRINT("Request %x\n", Urb->UrbControlVendorClassRequest.Request);
    DPRINT("Value %x\n", Urb->UrbControlVendorClassRequest.Value);
    DPRINT("Index %x\n", Urb->UrbControlVendorClassRequest.Index);

    //
    // initialize setup packet
    //
    CtrlSetup.bmRequestType.B = 0x21;
    CtrlSetup.bRequest = Urb->UrbControlVendorClassRequest.Request;
    CtrlSetup.wValue.W = Urb->UrbControlVendorClassRequest.Value;
    CtrlSetup.wIndex.W = Urb->UrbControlVendorClassRequest.Index;
    CtrlSetup.wLength = (USHORT)Urb->UrbControlVendorClassRequest.TransferBufferLength;

    if (Urb->UrbControlVendorClassRequest.TransferFlags & USBD_TRANSFER_DIRECTION_IN)
    {
        //
        // data direction is device to host
        //
        CtrlSetup.bmRequestType.B |= 0x80;
    }

    //
    // issue request
    //
    Status = UsbDevice->SubmitSetupPacket(&CtrlSetup, Urb->UrbControlVendorClassRequest.TransferBufferLength, Urb->UrbControlVendorClassRequest.TransferBuffer);

    //
    // assert on failure
    //
    if (!NT_SUCCESS(Status))
    {
        //
        // display error
        //
        DPRINT1("[%s] URB_FUNCTION_CLASS_INTERFACE failed with Urb Status %x\n", m_USBType, Urb->UrbHeader.Status);
    }

    //
    // done
    //
    return Status;
}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::HandleDeviceControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN OUT PIRP Irp)
{
    PIO_STACK_LOCATION IoStack;
    PURB Urb;
    NTSTATUS Status = STATUS_NOT_IMPLEMENTED;

    //
    // get current stack location
    //
    IoStack = IoGetCurrentIrpStackLocation(Irp);

    //
    // determine which request should be performed
    //
    switch(IoStack->Parameters.DeviceIoControl.IoControlCode)
    {
        case IOCTL_INTERNAL_USB_SUBMIT_URB:
        {
            //
            // get urb
            //
            Urb = (PURB)IoStack->Parameters.Others.Argument1;
            PC_ASSERT(Urb);

            switch (Urb->UrbHeader.Function)
            {
                case URB_FUNCTION_SYNC_RESET_PIPE:
                case URB_FUNCTION_SYNC_RESET_PIPE_AND_CLEAR_STALL:
                    Status = HandleSyncResetAndClearStall(Irp, Urb);
                    break;
                case URB_FUNCTION_ABORT_PIPE:
                    Status = HandleAbortPipe(Irp, Urb);
                    break;
                case URB_FUNCTION_SYNC_CLEAR_STALL:
                    Status = HandleClearStall(Irp, Urb);
                    break;
                case URB_FUNCTION_GET_DESCRIPTOR_FROM_INTERFACE:
                    Status = HandleGetDescriptorFromInterface(Irp, Urb);
                    break;
                case URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE:
                    Status = HandleGetDescriptor(Irp, Urb);
                    break;
                case URB_FUNCTION_CLASS_DEVICE:
                    Status = HandleClassDevice(Irp, Urb);
                    break;
                case URB_FUNCTION_GET_STATUS_FROM_DEVICE:
                case URB_FUNCTION_GET_STATUS_FROM_INTERFACE:
                case URB_FUNCTION_GET_STATUS_FROM_ENDPOINT:
                    Status = HandleGetStatusFromDevice(Irp, Urb);
                    break;
                case URB_FUNCTION_SELECT_CONFIGURATION:
                    Status = HandleSelectConfiguration(Irp, Urb);
                    break;
                case URB_FUNCTION_SELECT_INTERFACE:
                    Status = HandleSelectInterface(Irp, Urb);
                    break;
                case URB_FUNCTION_CLASS_OTHER:
                    Status = HandleClassOther(Irp, Urb);
                    break;
                case URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER:
                    Status = HandleBulkOrInterruptTransfer(Irp, Urb);
                    break;
                case URB_FUNCTION_ISOCH_TRANSFER:
                    Status = HandleIsochronousTransfer(Irp, Urb);
                    break;
                case URB_FUNCTION_CLASS_INTERFACE:
                    Status = HandleClassInterface(Irp, Urb);
                    break;
                case URB_FUNCTION_CLASS_ENDPOINT:
                    Status = HandleClassEndpoint(Irp, Urb);
                    break;
                case URB_FUNCTION_VENDOR_DEVICE:
                    Status = HandleVendorDevice(Irp, Urb);
                    break;
                default:
                    DPRINT1("[%s] IOCTL_INTERNAL_USB_SUBMIT_URB Function %x NOT IMPLEMENTED\n", m_USBType, Urb->UrbHeader.Function);
                    break;
            }
            //
            // request completed
            //
            break;
        }
        case IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE:
        {
            DPRINT("[%s] IOCTL_INTERNAL_USB_GET_DEVICE_HANDLE %p\n", m_USBType, this);

            if (IoStack->Parameters.Others.Argument1)
            {
                //
                // store object as device handle
                //
                *(PVOID *)IoStack->Parameters.Others.Argument1 = (PVOID)this;
                Status = STATUS_SUCCESS;
            }
            else
            {
                //
                // mis-behaving hub driver
                //
                Status = STATUS_INVALID_DEVICE_REQUEST;
            }

            //
            // request completed
            //
            break;
        }
        case IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO:
        {
            DPRINT("[%s] IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO\n", m_USBType);

            //
            // this is the first request send, it delivers the PDO to the caller
            //
            if (IoStack->Parameters.Others.Argument1)
            {
                //
                // store root hub pdo object
                //
                *(PVOID *)IoStack->Parameters.Others.Argument1 = DeviceObject;
            }

            if (IoStack->Parameters.Others.Argument2)
            {
                //
                // documentation claims to deliver the hcd controller object, although it is wrong
                //
                *(PVOID *)IoStack->Parameters.Others.Argument2 = DeviceObject;
            }

            //
            // request completed
            //
            Status = STATUS_SUCCESS;
            break;
        }
        case IOCTL_INTERNAL_USB_GET_HUB_COUNT:
        {
            DPRINT("[%s] IOCTL_INTERNAL_USB_GET_HUB_COUNT\n", m_USBType);

            //
            // after IOCTL_INTERNAL_USB_GET_ROOTHUB_PDO is delivered, the usbhub driver
            // requests this ioctl to deliver the number of presents.

            if (IoStack->Parameters.Others.Argument1)
            {
                //
                // FIXME / verify: there is only one hub
                //
                *(PULONG)IoStack->Parameters.Others.Argument1 = 1;
            }

            //
            // request completed
            //
            Status = STATUS_SUCCESS;
            Irp->IoStatus.Information = sizeof(ULONG);
            break;
        }
        case IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION:
        {
            DPRINT1("[%s] IOCTL_INTERNAL_USB_SUBMIT_IDLE_NOTIFICATION UNIMPLEMENTED\n", m_USBType);
            Status = STATUS_SUCCESS;
            break;
        }
        default:
        {
            DPRINT1("[%s] HandleDeviceControl>Type: IoCtl %x InputBufferLength %lu OutputBufferLength %lu NOT IMPLEMENTED\n", m_USBType,
                    IoStack->Parameters.DeviceIoControl.IoControlCode,
                    IoStack->Parameters.DeviceIoControl.InputBufferLength,
                    IoStack->Parameters.DeviceIoControl.OutputBufferLength);
            break;
        }
    }
    if (Status != STATUS_PENDING)
    {
        Irp->IoStatus.Status = Status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
    }

    return Status;
}

//-----------------------------------------------------------------------------------------
PUSBHARDWAREDEVICE
CHubController::GetUsbHardware()
{
    return m_Hardware;
}

//-----------------------------------------------------------------------------------------
ULONG
CHubController::AcquireDeviceAddress()
{
    KIRQL OldLevel;
    ULONG DeviceAddress;

    //
    // acquire device lock
    //
    KeAcquireSpinLock(&m_Lock, &OldLevel);

    //
    // find address
    //
    DeviceAddress = RtlFindClearBits(&m_DeviceAddressBitmap, 1, 0);
    if (DeviceAddress != MAXULONG)
    {
        //
        // reserve address
        //
        RtlSetBits(&m_DeviceAddressBitmap, DeviceAddress, 1);

        //
        // device addresses start from 0x1 - 0xFF
        //
        DeviceAddress++;
    }

    //
    // release spin lock
    //
    KeReleaseSpinLock(&m_Lock, OldLevel);

    //
    // return device address
    //
    return DeviceAddress;
}
//-----------------------------------------------------------------------------------------
VOID
CHubController::ReleaseDeviceAddress(
    ULONG DeviceAddress)
{
    KIRQL OldLevel;

    //
    // acquire device lock
    //
    KeAcquireSpinLock(&m_Lock, &OldLevel);

    //
    // sanity check
    //
    PC_ASSERT(DeviceAddress != 0);

    //
    // convert back to bit number
    //
    DeviceAddress--;

    //
    // clear bit
    //
    RtlClearBits(&m_DeviceAddressBitmap, DeviceAddress, 1);

    //
    // release lock
    //
    KeReleaseSpinLock(&m_Lock, OldLevel);
}
//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::RemoveUsbDevice(
    PUSBDEVICE UsbDevice)
{
    PUSBDEVICE_ENTRY DeviceEntry;
    PLIST_ENTRY Entry;
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    KIRQL OldLevel;

    //
    // acquire lock
    //
    KeAcquireSpinLock(&m_Lock, &OldLevel);

    //
    // point to first entry
    //
    Entry = m_UsbDeviceList.Flink;

    //
    // find matching entry
    //
    while(Entry != &m_UsbDeviceList)
    {
        //
        // get entry
        //
        DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry);

        //
        // is it current entry
        //
        if (DeviceEntry->Device == UsbDevice)
        {
            //
            // remove entry
            //
            RemoveEntryList(Entry);

            //
            // free entry
            //
            ExFreePoolWithTag(DeviceEntry, TAG_USBLIB);

            //
            // done
            //
            Status = STATUS_SUCCESS;
            break;
        }

        //
        // goto next device
        //
        Entry = Entry->Flink;
    }

    //
    // release lock
    //
    KeReleaseSpinLock(&m_Lock, OldLevel);

    //
    // return result
    //
    return Status;
}
//-----------------------------------------------------------------------------------------
BOOLEAN
CHubController::ValidateUsbDevice(PUSBDEVICE UsbDevice)
{
    PUSBDEVICE_ENTRY DeviceEntry;
    PLIST_ENTRY Entry;
    KIRQL OldLevel;
    BOOLEAN Result = FALSE;

    //
    // acquire lock
    //
    KeAcquireSpinLock(&m_Lock, &OldLevel);

    //
    // point to first entry
    //
    Entry = m_UsbDeviceList.Flink;

    //
    // find matching entry
    //
    while(Entry != &m_UsbDeviceList)
    {
        //
        // get entry
        //
        DeviceEntry = (PUSBDEVICE_ENTRY)CONTAINING_RECORD(Entry, USBDEVICE_ENTRY, Entry);

        //
        // is it current entry
        //
        if (DeviceEntry->Device == UsbDevice)
        {
            //
            // device is valid
            //
            Result = TRUE;
            break;
        }

        //
        // goto next device
        //
        Entry = Entry->Flink;
    }

    //
    // release lock
    //
    KeReleaseSpinLock(&m_Lock, OldLevel);

    //
    // return result
    //
    return Result;

}

//-----------------------------------------------------------------------------------------
NTSTATUS
CHubController::AddUsbDevice(
    PUSBDEVICE UsbDevice)
{
    PUSBDEVICE_ENTRY DeviceEntry;
    KIRQL OldLevel;

    //
    // allocate device entry
    //
    DeviceEntry = (PUSBDEVICE_ENTRY)ExAllocatePoolWithTag(NonPagedPool, sizeof(USBDEVICE_ENTRY), TAG_USBLIB);
    if (!DeviceEntry)
    {
        //
        // no memory
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // initialize entry
    //
    DeviceEntry->Device = UsbDevice;

    //
    // acquire lock
    //
    KeAcquireSpinLock(&m_Lock, &OldLevel);

    //
    // insert entry
    //
    InsertTailList(&m_UsbDeviceList, &DeviceEntry->Entry);

    //
    // release spin lock
    //
    KeReleaseSpinLock(&m_Lock, OldLevel);

    //
    // done
    //
    return STATUS_SUCCESS;
}

//-----------------------------------------------------------------------------------------
VOID
CHubController::SetNotification(
    PVOID CallbackContext,
    PRH_INIT_CALLBACK CallbackRoutine)
{
    KIRQL OldLevel;

    //
    // acquire hub controller lock
    //
    KeAcquireSpinLock(&m_Lock, &OldLevel);

    //
    // now set the callback routine and context of the hub
    //
    m_HubCallbackContext = CallbackContext;
    m_HubCallbackRoutine = CallbackRoutine;

   //
   // release hub controller lock
   //
   KeReleaseSpinLock(&m_Lock, OldLevel);
}

//=================================================================================================
//
// Generic Interface functions
//
VOID
USB_BUSIFFN
USBI_InterfaceReference(
    PVOID BusContext)
{
    CHubController * Controller = (CHubController*)BusContext;

    DPRINT("USBI_InterfaceReference\n");

    //
    // add reference
    //
    Controller->AddRef();
}

VOID
USB_BUSIFFN
USBI_InterfaceDereference(
    PVOID BusContext)
{
    CHubController * Controller = (CHubController*)BusContext;

    DPRINT("USBI_InterfaceDereference\n");

    //
    // release
    //
    Controller->Release();
}
//=================================================================================================
//
// USB Hub Interface functions
//
NTSTATUS
USB_BUSIFFN
USBHI_CreateUsbDevice(
    PVOID BusContext,
    PUSB_DEVICE_HANDLE *NewDevice,
    PUSB_DEVICE_HANDLE HubDeviceHandle,
    USHORT PortStatus,
    USHORT PortNumber)
{
    PUSBDEVICE NewUsbDevice;
    CHubController * Controller;
    NTSTATUS Status;

    DPRINT1("USBHI_CreateUsbDevice\n");

    //
    // first get hub controller
    //
    Controller = (CHubController *)BusContext;

    //
    // sanity check
    //
    PC_ASSERT(Controller);
    PC_ASSERT(BusContext == HubDeviceHandle);

    //
    // now allocate usb device
    //
    Status = CreateUSBDevice(&NewUsbDevice);

    //
    // check for success
    //
    if (!NT_SUCCESS(Status))
    {
        //
        // release controller
        //
        Controller->Release();
        DPRINT1("USBHI_CreateUsbDevice: failed to create usb device %x\n", Status);
        return Status;
    }

    //
    // now initialize device
    //
    Status = NewUsbDevice->Initialize(PHUBCONTROLLER(Controller), Controller->GetUsbHardware(), HubDeviceHandle, PortNumber, PortStatus);

    //
    // check for success
    //
    if (!NT_SUCCESS(Status))
    {
        //
        // release usb device
        //
        NewUsbDevice->Release();
        DPRINT1("USBHI_CreateUsbDevice: failed to initialize usb device %x\n", Status);
        return Status;
    }

    //
    // insert into list
    //
    Status = Controller->AddUsbDevice(NewUsbDevice);
    //
    // check for success
    //
    if (!NT_SUCCESS(Status))
    {
        //
        // release usb device
        //
        NewUsbDevice->Release();

        DPRINT1("USBHI_CreateUsbDevice: failed to add usb device %x\n", Status);
        return Status;
    }

    //
    // store the handle
    //
    *NewDevice = NewUsbDevice;

    //
    // done
    //
    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_InitializeUsbDevice(
    PVOID BusContext,
    PUSB_DEVICE_HANDLE DeviceHandle)
{
    PUSBDEVICE UsbDevice;
    CHubController * Controller;
    ULONG DeviceAddress;
    NTSTATUS Status;
    ULONG Index = 0;

    DPRINT("USBHI_InitializeUsbDevice\n");

    //
    // first get controller
    //
    Controller = (CHubController *)BusContext;
    PC_ASSERT(Controller);

    //
    // get device object
    //
    UsbDevice = (PUSBDEVICE)DeviceHandle;
    PC_ASSERT(UsbDevice);

    //
    // validate device handle
    //
    if (!Controller->ValidateUsbDevice(UsbDevice))
    {
        DPRINT1("USBHI_InitializeUsbDevice invalid device handle %p\n", DeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // now reserve an address
    //
    DeviceAddress = Controller->AcquireDeviceAddress();

    //
    // is the device address valid
    //
    if (DeviceAddress == MAXULONG)
    {
        //
        // failed to get an device address from the device address pool
        //
        DPRINT1("USBHI_InitializeUsbDevice failed to get device address\n");
        return STATUS_DEVICE_DATA_ERROR;
    }

    do
    {
        //
        // now set the device address
        //
        Status = UsbDevice->SetDeviceAddress((UCHAR)DeviceAddress);

        if (NT_SUCCESS(Status))
            break;

    }while(Index++ < 3    );

    //
    // check for failure
    //
    if (!NT_SUCCESS(Status))
    {
        //
        // failed to set device address
        //
        DPRINT1("USBHI_InitializeUsbDevice failed to set address with %x\n", Status);

        //
        // release address
        //
        Controller->ReleaseDeviceAddress(DeviceAddress);

        //
        // return error
        //
        return STATUS_DEVICE_DATA_ERROR;
    }

    //
    // done
    //
    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_GetUsbDescriptors(
    PVOID BusContext,
    PUSB_DEVICE_HANDLE DeviceHandle,
    PUCHAR DeviceDescriptorBuffer,
    PULONG DeviceDescriptorBufferLength,
    PUCHAR ConfigDescriptorBuffer,
    PULONG ConfigDescriptorBufferLength)
{
    PUSBDEVICE UsbDevice;
    CHubController * Controller;

    DPRINT("USBHI_GetUsbDescriptors\n");

    //
    // sanity check
    //
    PC_ASSERT(DeviceDescriptorBuffer);
    PC_ASSERT(DeviceDescriptorBufferLength);
    PC_ASSERT(*DeviceDescriptorBufferLength >= sizeof(USB_DEVICE_DESCRIPTOR));
    PC_ASSERT(ConfigDescriptorBufferLength);
    PC_ASSERT(*ConfigDescriptorBufferLength >= sizeof(USB_CONFIGURATION_DESCRIPTOR));

    //
    // first get controller
    //
    Controller = (CHubController *)BusContext;
    PC_ASSERT(Controller);


    //
    // get device object
    //
    UsbDevice = (PUSBDEVICE)DeviceHandle;
    PC_ASSERT(UsbDevice);

    //
    // validate device handle
    //
    if (!Controller->ValidateUsbDevice(UsbDevice))
    {
        DPRINT1("USBHI_GetUsbDescriptors invalid device handle %p\n", DeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // get device descriptor
    //
    UsbDevice->GetDeviceDescriptor((PUSB_DEVICE_DESCRIPTOR)DeviceDescriptorBuffer);

    //
    // store result length
    //
    *DeviceDescriptorBufferLength = sizeof(USB_DEVICE_DESCRIPTOR);

    //
    // get configuration descriptor
    //
    UsbDevice->GetConfigurationDescriptors((PUSB_CONFIGURATION_DESCRIPTOR)ConfigDescriptorBuffer, *ConfigDescriptorBufferLength, ConfigDescriptorBufferLength);

    //
    // complete the request
    //
    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_RemoveUsbDevice(
    PVOID BusContext,
    PUSB_DEVICE_HANDLE DeviceHandle,
    ULONG Flags)
{
    PUSBDEVICE UsbDevice;
    CHubController * Controller;
    NTSTATUS Status;

    DPRINT("USBHI_RemoveUsbDevice\n");

    //
    // first get controller
    //
    Controller = (CHubController *)BusContext;
    PC_ASSERT(Controller);

    //
    // get device object
    //
    UsbDevice = (PUSBDEVICE)DeviceHandle;
    PC_ASSERT(UsbDevice);

    //
    // validate device handle
    //
    if (!Controller->ValidateUsbDevice(UsbDevice))
    {
        DPRINT1("USBHI_RemoveUsbDevice invalid device handle %p\n", DeviceHandle);

        //
        // invalid device handle
        //
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // check if there were flags passed
    //
    if (Flags & USBD_KEEP_DEVICE_DATA || Flags  & USBD_MARK_DEVICE_BUSY)
    {
        //
        // ignore flags for now
        //
        return STATUS_SUCCESS;
    }

    //
    // remove device
    //
    Status = Controller->RemoveUsbDevice(UsbDevice);
    if (!NT_SUCCESS(Status))
    {
        //
        // invalid device handle
        //
        DPRINT1("USBHI_RemoveUsbDevice Invalid device handle %p\n", UsbDevice);
        return STATUS_DEVICE_NOT_CONNECTED;
    }

    //
    // release usb device
    //
    UsbDevice->Release();

    //
    // done
    //
    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_RestoreUsbDevice(
    PVOID BusContext,
    PUSB_DEVICE_HANDLE OldDeviceHandle,
    PUSB_DEVICE_HANDLE NewDeviceHandle)
{
    PUSBDEVICE OldUsbDevice, NewUsbDevice;
    CHubController * Controller;

    DPRINT("USBHI_RestoreUsbDevice\n");

    //
    // first get controller
    //
    Controller = (CHubController *)BusContext;
    PC_ASSERT(Controller);

    //
    // get device object
    //
    OldUsbDevice = (PUSBDEVICE)OldDeviceHandle;
    NewUsbDevice = (PUSBDEVICE)NewDeviceHandle;
    PC_ASSERT(OldUsbDevice);
    PC_ASSERT(NewDeviceHandle);

    //
    // validate device handle
    //
    PC_ASSERT(Controller->ValidateUsbDevice(NewUsbDevice));
    PC_ASSERT(Controller->ValidateUsbDevice(OldUsbDevice));

    DPRINT1("NewUsbDevice: DeviceAddress %x\n", NewUsbDevice->GetDeviceAddress());
    DPRINT1("OldUsbDevice: DeviceAddress %x\n", OldUsbDevice->GetDeviceAddress());

    //
    // remove old device handle
    //
    USBHI_RemoveUsbDevice(BusContext, OldDeviceHandle, 0);

    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_QueryDeviceInformation(
    PVOID BusContext,
    PUSB_DEVICE_HANDLE DeviceHandle,
    PVOID DeviceInformationBuffer,
    ULONG DeviceInformationBufferLength,
    PULONG LengthReturned)
{
    PUSB_DEVICE_INFORMATION_0 DeviceInfo;
    PUSBDEVICE UsbDevice;
    CHubController * Controller;

    DPRINT("USBHI_QueryDeviceInformation %p\n", BusContext);

    //
    // sanity check
    //
    PC_ASSERT(DeviceInformationBufferLength >= sizeof(USB_DEVICE_INFORMATION_0));
    PC_ASSERT(DeviceInformationBuffer);
    PC_ASSERT(LengthReturned);

    //
    // get controller object
    //
    Controller = (CHubController*)BusContext;
    PC_ASSERT(Controller);

    //
    // get device object
    //
    UsbDevice = (PUSBDEVICE)DeviceHandle;
    PC_ASSERT(UsbDevice);

    if (BusContext != DeviceHandle)
    {
        //
        // validate device handle
        //
        if (!Controller->ValidateUsbDevice(UsbDevice))
        {
            DPRINT1("USBHI_QueryDeviceInformation invalid device handle %p\n", DeviceHandle);

            //
            // invalid device handle
            //
            return STATUS_DEVICE_NOT_CONNECTED;
        }

        //
        // access information buffer
        //
        DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer;

        //
        // initialize with default values
        //
        DeviceInfo->InformationLevel = 0;
        DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0);
        DeviceInfo->PortNumber = UsbDevice->GetPort();
        DeviceInfo->CurrentConfigurationValue = UsbDevice->GetConfigurationValue();
        DeviceInfo->DeviceAddress = UsbDevice->GetDeviceAddress();
        DeviceInfo->HubAddress = 0; //FIXME
        DeviceInfo->DeviceSpeed = UsbDevice->GetSpeed();
        DeviceInfo->DeviceType = UsbDevice->GetType();
        DeviceInfo->NumberOfOpenPipes = 0; //FIXME

        //
        // get device descriptor
        //
        UsbDevice->GetDeviceDescriptor(&DeviceInfo->DeviceDescriptor);

        //
        // FIXME return pipe information
        //

        //
        // store result length
        //
        *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0);

        return STATUS_SUCCESS;
    }

    //
    // access information buffer
    //
    DeviceInfo = (PUSB_DEVICE_INFORMATION_0)DeviceInformationBuffer;

    //
    // initialize with default values
    //
    DeviceInfo->InformationLevel = 0;
    DeviceInfo->ActualLength = sizeof(USB_DEVICE_INFORMATION_0);
    DeviceInfo->PortNumber = 0;
    DeviceInfo->CurrentConfigurationValue = 0; //FIXME;
    DeviceInfo->DeviceAddress = 0;
    DeviceInfo->HubAddress = 0; //FIXME
    DeviceInfo->DeviceSpeed = UsbHighSpeed; //FIXME
    DeviceInfo->DeviceType = Usb20Device; //FIXME
    DeviceInfo->NumberOfOpenPipes = 0; //FIXME

    //
    // get device descriptor
    //
    RtlMoveMemory(&DeviceInfo->DeviceDescriptor, ROOTHUB2_DEVICE_DESCRIPTOR, sizeof(USB_DEVICE_DESCRIPTOR));

    //
    // FIXME return pipe information
    //

    //
    // store result length
    //
#ifdef _MSC_VER
    *LengthReturned = FIELD_OFFSET(USB_DEVICE_INFORMATION_0, PipeList[DeviceInfo->NumberOfOpenPipes]);
#else
    *LengthReturned = sizeof(USB_DEVICE_INFORMATION_0) + (DeviceInfo->NumberOfOpenPipes > 1 ? (DeviceInfo->NumberOfOpenPipes - 1) * sizeof(USB_PIPE_INFORMATION_0) : 0);
#endif
    //
    // done
    //
    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_GetControllerInformation(
    PVOID BusContext,
    PVOID ControllerInformationBuffer,
    ULONG ControllerInformationBufferLength,
    PULONG LengthReturned)
{
    PUSB_CONTROLLER_INFORMATION_0 ControllerInfo;

    DPRINT("USBHI_GetControllerInformation\n");

    //
    // sanity checks
    //
    PC_ASSERT(ControllerInformationBuffer);
    PC_ASSERT(ControllerInformationBufferLength >= sizeof(USB_CONTROLLER_INFORMATION_0));

    //
    // get controller info buffer
    //
    ControllerInfo = (PUSB_CONTROLLER_INFORMATION_0)ControllerInformationBuffer;

    //
    // FIXME only version 0 is supported for now
    //
    PC_ASSERT(ControllerInfo->InformationLevel == 0);

    //
    // fill in information
    //
    ControllerInfo->ActualLength = sizeof(USB_CONTROLLER_INFORMATION_0);
    ControllerInfo->SelectiveSuspendEnabled = FALSE; //FIXME
    ControllerInfo->IsHighSpeedController = TRUE;

    //
    // set length returned
    //
    *LengthReturned = ControllerInfo->ActualLength;

    //
    // done
    //
    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_ControllerSelectiveSuspend(
    PVOID BusContext,
    BOOLEAN Enable)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
USB_BUSIFFN
USBHI_GetExtendedHubInformation(
    PVOID BusContext,
    PDEVICE_OBJECT HubPhysicalDeviceObject,
    PVOID HubInformationBuffer,
    ULONG HubInformationBufferLength,
    PULONG LengthReturned)
{
    PUSB_EXTHUB_INFORMATION_0 HubInfo;
    CHubController * Controller;
    PUSBHARDWAREDEVICE Hardware;
    ULONG Index;
    ULONG NumPort, Dummy2;
    USHORT Dummy1;
    NTSTATUS Status;

    DPRINT("USBHI_GetExtendedHubInformation\n");

    //
    // sanity checks
    //
    PC_ASSERT(HubInformationBuffer);
    PC_ASSERT(HubInformationBufferLength == sizeof(USB_EXTHUB_INFORMATION_0));
    PC_ASSERT(LengthReturned);

    //
    // get hub controller
    //
    Controller = (CHubController *)BusContext;
    PC_ASSERT(Controller);

    //
    // get usb hardware device
    //
    Hardware = Controller->GetUsbHardware();

    //
    // retrieve number of ports
    //
    Status = Hardware->GetDeviceDetails(&Dummy1, &Dummy1, &NumPort, &Dummy2);
    if (!NT_SUCCESS(Status))
    {
        //
        // failed to get hardware details, ouch ;)
        //
        DPRINT1("USBHI_GetExtendedHubInformation failed to get hardware details with %x\n", Status);
        return Status;
    }

    //
    // get hub information buffer
    //
    HubInfo = (PUSB_EXTHUB_INFORMATION_0)HubInformationBuffer;

    //
    // initialize hub information
    //
    HubInfo->InformationLevel = 0;

    //
    // store port count
    //
    HubInfo->NumberOfPorts = NumPort;

    //
    // initialize port information
    //
    for(Index = 0; Index < NumPort; Index++)
    {
        HubInfo->Port[Index].PhysicalPortNumber = Index + 1;
        HubInfo->Port[Index].PortLabelNumber = Index + 1;
        HubInfo->Port[Index].VidOverride = 0;
        HubInfo->Port[Index].PidOverride = 0;
        HubInfo->Port[Index].PortAttributes = USB_PORTATTR_SHARED_USB2; //FIXME
    }

    //
    // store result length
    //
#ifdef _MSC_VER
    *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port[HubInfo->NumberOfPorts]);
#else
    *LengthReturned = FIELD_OFFSET(USB_EXTHUB_INFORMATION_0, Port) + sizeof(USB_EXTPORT_INFORMATION_0) * HubInfo->NumberOfPorts;
#endif

    //
    // done
    //
    return STATUS_SUCCESS;
}

NTSTATUS
USB_BUSIFFN
USBHI_GetRootHubSymbolicName(
    PVOID BusContext,
    PVOID HubSymNameBuffer,
    ULONG HubSymNameBufferLength,
    PULONG HubSymNameActualLength)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

PVOID
USB_BUSIFFN
USBHI_GetDeviceBusContext(
    PVOID HubBusContext,
    PVOID DeviceHandle)
{
    UNIMPLEMENTED;
    return NULL;
}

NTSTATUS
USB_BUSIFFN
USBHI_Initialize20Hub(
    PVOID BusContext,
    PUSB_DEVICE_HANDLE HubDeviceHandle,
    ULONG TtCount)
{
    DPRINT("USBHI_Initialize20Hub HubDeviceHandle %p UNIMPLEMENTED TtCount %lu\n", HubDeviceHandle, TtCount);
    return STATUS_SUCCESS;
}


WORKER_THREAD_ROUTINE InitRootHub;

VOID
NTAPI
InitRootHub(IN PVOID Context)
{
    PINIT_ROOT_HUB_CONTEXT WorkItem;

    //
    // get context
    //
    WorkItem = (PINIT_ROOT_HUB_CONTEXT)Context;

    //
    // perform callback
    //
    WorkItem->CallbackRoutine(WorkItem->CallbackContext);

    //
    // free contextg
    //
    ExFreePoolWithTag(Context, TAG_USBLIB);
}

NTSTATUS
USB_BUSIFFN
USBHI_RootHubInitNotification(
    PVOID BusContext,
    PVOID CallbackContext,
    PRH_INIT_CALLBACK CallbackRoutine)
{
    CHubController * Controller;
    PINIT_ROOT_HUB_CONTEXT WorkItem;

    DPRINT("USBHI_RootHubInitNotification %p \n", CallbackContext);

    //
    // get controller object
    //
    Controller = (CHubController*)BusContext;
    PC_ASSERT(Controller);

    //
    // set notification routine
    //
    Controller->SetNotification(CallbackContext, CallbackRoutine);

    //
    // Create and initialize work item data
    //
    WorkItem = (PINIT_ROOT_HUB_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(INIT_ROOT_HUB_CONTEXT), TAG_USBLIB);
    if (!WorkItem)
    {
        DPRINT1("Failed to allocate memory!n");
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // init context
    //
    WorkItem->CallbackRoutine = CallbackRoutine;
    WorkItem->CallbackContext = CallbackContext;

    //
    // Queue the work item to handle initializing the device
    //
    ExInitializeWorkItem(&WorkItem->WorkItem, InitRootHub, (PVOID)WorkItem);
    ExQueueWorkItem(&WorkItem->WorkItem, DelayedWorkQueue);

    //
    // done
    //
    return STATUS_SUCCESS;
}

VOID
USB_BUSIFFN
USBHI_FlushTransfers(
    PVOID BusContext,
    PVOID DeviceHandle)
{
    UNIMPLEMENTED;
}

VOID
USB_BUSIFFN
USBHI_SetDeviceHandleData(
    PVOID BusContext,
    PVOID DeviceHandle,
    PDEVICE_OBJECT UsbDevicePdo)
{
    PUSBDEVICE UsbDevice;
    CHubController * Controller;

    //
    // get controller
    //
    Controller = (CHubController *)BusContext;
    PC_ASSERT(Controller);

    //
    // get device handle
    //
    UsbDevice = (PUSBDEVICE)DeviceHandle;

    //
    // validate device handle
    //
    if (!Controller->ValidateUsbDevice(UsbDevice))
    {
        DPRINT1("USBHI_SetDeviceHandleData DeviceHandle %p is invalid\n", DeviceHandle);

        //
        // invalid handle
        //
        return;
    }
    else
    {
        //
        // usbhub sends this request as a part of the Pnp startup sequence
        // looks like we need apply a dragon voodoo to fixup the device stack
        // otherwise usbhub will cause a bugcheck
        //
        DPRINT1("USBHI_SetDeviceHandleData %p\n", UsbDevicePdo);

        //
        // sanity check
        //
        PC_ASSERT(UsbDevicePdo->AttachedDevice);

        //
        // should be usbstor
        // fixup device stack voodoo part #2
        //
        UsbDevicePdo->AttachedDevice->StackSize++;

        //
        // set device handle data
        //
        UsbDevice->SetDeviceHandleData(UsbDevicePdo);
    }
}

//=================================================================================================
//
// USB Device Interface functions
//

VOID
USB_BUSIFFN
USBDI_GetUSBDIVersion(
    PVOID BusContext,
    PUSBD_VERSION_INFORMATION VersionInformation,
    PULONG HcdCapabilites)
{
    CHubController * Controller;
    PUSBHARDWAREDEVICE Device;
    ULONG Speed, Dummy2;
    USHORT Dummy1;

    DPRINT("USBDI_GetUSBDIVersion\n");

    //
    // get controller
    //
    Controller = (CHubController*)BusContext;

    //
    // get usb hardware
    //
    Device = Controller->GetUsbHardware();
    PC_ASSERT(Device);

    if (VersionInformation)
    {
        //
        // windows xp supported
        //
        VersionInformation->USBDI_Version = 0x00000500;

        //
        // get device speed
        //
        Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed);

        //
        // store speed details
        //
        VersionInformation->Supported_USB_Version = Speed;
    }

    //
    // no flags supported
    //
    *HcdCapabilites = 0;
}

NTSTATUS
USB_BUSIFFN
USBDI_QueryBusTime(
    PVOID BusContext,
    PULONG CurrentFrame)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
USB_BUSIFFN
USBDI_SubmitIsoOutUrb(
    PVOID BusContext,
    PURB Urb)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
USB_BUSIFFN
USBDI_QueryBusInformation(
    PVOID BusContext,
    ULONG Level,
    PVOID BusInformationBuffer,
    PULONG BusInformationBufferLength,
    PULONG BusInformationActualLength)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

BOOLEAN
USB_BUSIFFN
USBDI_IsDeviceHighSpeed(
    PVOID BusContext)
{
    CHubController * Controller;
    PUSBHARDWAREDEVICE Device;
    ULONG Speed, Dummy2;
    USHORT Dummy1;

    DPRINT("USBDI_IsDeviceHighSpeed\n");

    //
    // get controller
    //
    Controller = (CHubController*)BusContext;

    //
    // get usb hardware
    //
    Device = Controller->GetUsbHardware();
    PC_ASSERT(Device);

    //
    // get device speed
    //
    Device->GetDeviceDetails(&Dummy1, &Dummy1, &Dummy2, &Speed);

    //
    // USB 2.0 equals 0x200
    //
    return (Speed == 0x200);
}

NTSTATUS
USB_BUSIFFN
USBDI_EnumLogEntry(
    PVOID BusContext,
    ULONG DriverTag,
    ULONG EnumTag,
    ULONG P1,
    ULONG P2)
{
    UNIMPLEMENTED;
    return STATUS_NOT_IMPLEMENTED;
}

NTSTATUS
CHubController::HandleQueryInterface(
    PIO_STACK_LOCATION IoStack)
{
    PUSB_BUS_INTERFACE_HUB_V5 InterfaceHub;
    PUSB_BUS_INTERFACE_USBDI_V2 InterfaceDI;
    UNICODE_STRING GuidBuffer;
    NTSTATUS Status;

    if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_HUB_GUID))
    {
        //
        // get request parameters
        //
        InterfaceHub = (PUSB_BUS_INTERFACE_HUB_V5)IoStack->Parameters.QueryInterface.Interface;
        InterfaceHub->Version = IoStack->Parameters.QueryInterface.Version;

        //
        // check version
        //
        if (IoStack->Parameters.QueryInterface.Version >= 6)
        {
            DPRINT1("USB_BUS_INTERFACE_HUB_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version);

            //
            // version not supported
            //
            return STATUS_NOT_SUPPORTED;
        }

        //
        // Interface version 0
        //
        InterfaceHub->Size = IoStack->Parameters.QueryInterface.Size;
        InterfaceHub->BusContext = PVOID(this);
        InterfaceHub->InterfaceReference = USBI_InterfaceReference;
        InterfaceHub->InterfaceDereference = USBI_InterfaceDereference;

        //
        // Interface version 1
        //
        if (IoStack->Parameters.QueryInterface.Version >= 1)
        {
            InterfaceHub->CreateUsbDevice = USBHI_CreateUsbDevice;
            InterfaceHub->InitializeUsbDevice = USBHI_InitializeUsbDevice;
            InterfaceHub->GetUsbDescriptors = USBHI_GetUsbDescriptors;
            InterfaceHub->RemoveUsbDevice = USBHI_RemoveUsbDevice;
            InterfaceHub->RestoreUsbDevice = USBHI_RestoreUsbDevice;
            InterfaceHub->QueryDeviceInformation = USBHI_QueryDeviceInformation;
        }

        //
        // Interface version 2
        //
        if (IoStack->Parameters.QueryInterface.Version >= 2)
        {
            InterfaceHub->GetControllerInformation = USBHI_GetControllerInformation;
            InterfaceHub->ControllerSelectiveSuspend = USBHI_ControllerSelectiveSuspend;
            InterfaceHub->GetExtendedHubInformation = USBHI_GetExtendedHubInformation;
            InterfaceHub->GetRootHubSymbolicName = USBHI_GetRootHubSymbolicName;
            InterfaceHub->GetDeviceBusContext = USBHI_GetDeviceBusContext;
            InterfaceHub->Initialize20Hub = USBHI_Initialize20Hub;

        }

        //
        // Interface version 3
        //
        if (IoStack->Parameters.QueryInterface.Version >= 3)
        {
            InterfaceHub->RootHubInitNotification = USBHI_RootHubInitNotification;
        }

        //
        // Interface version 4
        //
        if (IoStack->Parameters.QueryInterface.Version >= 4)
        {
            InterfaceHub->FlushTransfers = USBHI_FlushTransfers;
        }

        //
        // Interface version 5
        //
        if (IoStack->Parameters.QueryInterface.Version >= 5)
        {
            InterfaceHub->SetDeviceHandleData = USBHI_SetDeviceHandleData;
        }

        InterfaceHub->InterfaceReference(InterfaceHub->BusContext);
        //
        // request completed
        //
        return STATUS_SUCCESS;
    }
    else if (IsEqualGUIDAligned(*IoStack->Parameters.QueryInterface.InterfaceType, USB_BUS_INTERFACE_USBDI_GUID))
    {
        //
        // get request parameters
        //
        InterfaceDI = (PUSB_BUS_INTERFACE_USBDI_V2) IoStack->Parameters.QueryInterface.Interface;
        InterfaceDI->Version = IoStack->Parameters.QueryInterface.Version;

        //
        // check version
        //
        if (IoStack->Parameters.QueryInterface.Version >= 3)
        {
            DPRINT1("USB_BUS_INTERFACE_USBDI_GUID version %x not supported!\n", IoStack->Parameters.QueryInterface.Version);

            //
            // version not supported
            //
            return STATUS_NOT_SUPPORTED;
        }

        //
        // interface version 0
        //
        InterfaceDI->Size = IoStack->Parameters.QueryInterface.Size;
        InterfaceDI->BusContext = PVOID(this);
        InterfaceDI->InterfaceReference = USBI_InterfaceReference;
        InterfaceDI->InterfaceDereference = USBI_InterfaceDereference;
        InterfaceDI->GetUSBDIVersion = USBDI_GetUSBDIVersion;
        InterfaceDI->QueryBusTime = USBDI_QueryBusTime;
        InterfaceDI->SubmitIsoOutUrb = USBDI_SubmitIsoOutUrb;
        InterfaceDI->QueryBusInformation = USBDI_QueryBusInformation;

        //
        // interface version 1
        //
        if (IoStack->Parameters.QueryInterface.Version >= 1)
        {
            InterfaceDI->IsDeviceHighSpeed = USBDI_IsDeviceHighSpeed;
        }

        //
        // interface version 2
        //
        if (IoStack->Parameters.QueryInterface.Version >= 2)
        {
            InterfaceDI->EnumLogEntry = USBDI_EnumLogEntry;
        }

        InterfaceDI->InterfaceReference(InterfaceDI->BusContext);
        //
        // request completed
        //
        return STATUS_SUCCESS;
    }
    else
    {
        //
        // convert guid to string
        //
        Status = RtlStringFromGUID(*IoStack->Parameters.QueryInterface.InterfaceType, &GuidBuffer);
        if (NT_SUCCESS(Status))
        {
            //
            // print interface
            //
            DPRINT1("HandleQueryInterface UNKNOWN INTERFACE GUID: %wZ Version %x\n", &GuidBuffer, IoStack->Parameters.QueryInterface.Version);

            //
            // free guid buffer
            //
            RtlFreeUnicodeString(&GuidBuffer);
        }
    }
    return STATUS_NOT_SUPPORTED;
}

NTSTATUS
CHubController::SetDeviceInterface(
    BOOLEAN Enable)
{
    NTSTATUS Status = STATUS_SUCCESS;

    if (Enable)
    {
        //
        // register device interface
        //
        Status = IoRegisterDeviceInterface(m_HubControllerDeviceObject, &GUID_DEVINTERFACE_USB_HUB, NULL, &m_HubDeviceInterfaceString);

        if (NT_SUCCESS(Status))
        {
            //
            // now enable the device interface
            //
            Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, TRUE);

            //
            // enable interface
            //
            m_InterfaceEnabled = TRUE;
        }
    }
    else if (m_InterfaceEnabled)
    {
        //
        // disable device interface
        //
        Status = IoSetDeviceInterfaceState(&m_HubDeviceInterfaceString, FALSE);

        if (NT_SUCCESS(Status))
        {
            //
            // now delete interface string
            //
            RtlFreeUnicodeString(&m_HubDeviceInterfaceString);
        }

        //
        // disable interface
        //
        m_InterfaceEnabled = FALSE;
    }

    //
    // done
    //
    return STATUS_SUCCESS;
}

NTSTATUS
CHubController::CreatePDO(
    PDRIVER_OBJECT DriverObject,
    PDEVICE_OBJECT * OutDeviceObject)
{
    WCHAR CharDeviceName[64];
    NTSTATUS Status;
    ULONG UsbDeviceNumber = 0;
    UNICODE_STRING DeviceName;

    while (TRUE)
    {
        //
        // construct device name
        //
        swprintf(CharDeviceName, L"\\Device\\USBPDO-%d", UsbDeviceNumber);

        //
        // initialize device name
        //
        RtlInitUnicodeString(&DeviceName, CharDeviceName);

        //
        // create device
        //
        Status = IoCreateDevice(DriverObject,
                                sizeof(COMMON_DEVICE_EXTENSION),
                                &DeviceName,
                                FILE_DEVICE_CONTROLLER,
                                0,
                                FALSE,
                                OutDeviceObject);

        /* check for success */
        if (NT_SUCCESS(Status))
            break;

        //
        // is there a device object with that same name
        //
        if ((Status == STATUS_OBJECT_NAME_EXISTS) || (Status == STATUS_OBJECT_NAME_COLLISION))
        {
            //
            // Try the next name
            //
            UsbDeviceNumber++;
            continue;
        }

        //
        // bail out on other errors
        //
        if (!NT_SUCCESS(Status))
        {
            DPRINT1("CreatePDO: Failed to create %wZ, Status %x\n", &DeviceName, Status);
            return Status;
        }
    }

    DPRINT("CHubController::CreatePDO: DeviceName %wZ\n", &DeviceName);

    //
    // fixup device stack voodoo part #1
    //
    (*OutDeviceObject)->StackSize++;

    /* done */
    return Status;
}



NTSTATUS
NTAPI
CreateHubController(
    PHUBCONTROLLER *OutHcdController)
{
    PHUBCONTROLLER This;

    //
    // allocate controller
    //
    This = new(NonPagedPool, TAG_USBLIB) CHubController(0);
    if (!This)
    {
        //
        // failed to allocate
        //
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // add reference count
    //
    This->AddRef();

    //
    // return result
    //
    *OutHcdController = (PHUBCONTROLLER)This;

    //
    // done
    //
    return STATUS_SUCCESS;
}

VOID NTAPI StatusChangeEndpointCallBack(PVOID Context)
{
    CHubController* This;
    PIRP Irp;
    This = (CHubController*)Context;

    ASSERT(This);

    Irp = This->m_PendingSCEIrp;
    if (!Irp)
    {
        DPRINT1("There was no pending IRP for SCE. Did the usb hub 2.0 driver (usbhub2) load?\n");
        return;
    }

    This->m_PendingSCEIrp = NULL;
    This->QueryStatusChangeEndpoint(Irp);

    Irp->IoStatus.Status = STATUS_SUCCESS;
    Irp->IoStatus.Information = 0;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);
}