/*
 * PROJECT:     ReactOS USB OHCI Miniport Driver
 * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
 * PURPOSE:     USBOHCI main driver functions
 * COPYRIGHT:   Copyright 2017-2018 Vadim Galyant <vgal@rambler.ru>
 */

#include "usbohci.h"

#define NDEBUG
#include <debug.h>

#define NDEBUG_OHCI_TRACE
#include "dbg_ohci.h"

USBPORT_REGISTRATION_PACKET RegPacket;

static const UCHAR Index[8] =
{
    ENDPOINT_INTERRUPT_1ms - 1,
    ENDPOINT_INTERRUPT_2ms - 1,
    ENDPOINT_INTERRUPT_4ms - 1,
    ENDPOINT_INTERRUPT_8ms - 1,
    ENDPOINT_INTERRUPT_16ms - 1,
    ENDPOINT_INTERRUPT_32ms - 1,
    ENDPOINT_INTERRUPT_32ms - 1,
    ENDPOINT_INTERRUPT_32ms - 1
};

static const UCHAR Balance[OHCI_NUMBER_OF_INTERRUPTS] =
{
    0, 16, 8, 24, 4, 20, 12, 28, 2, 18, 10, 26, 6, 22, 14, 30,
    1, 17, 9, 25, 5, 21, 13, 29, 3, 19, 11, 27, 7, 23, 15, 31
};

VOID
NTAPI
OHCI_DumpHcdED(POHCI_HCD_ED ED)
{
    DPRINT("ED                - %p\n", ED);
    DPRINT("EndpointControl   - %X\n", ED->HwED.EndpointControl.AsULONG);
    DPRINT("TailPointer       - %08X\n", ED->HwED.TailPointer);
    DPRINT("HeadPointer       - %08X\n", ED->HwED.HeadPointer);
    DPRINT("NextED            - %08X\n", ED->HwED.NextED);
}

VOID
NTAPI
OHCI_DumpHcdTD(POHCI_HCD_TD TD)
{
    DPRINT("TD                - %p\n", TD);
    DPRINT("gTD.Control       - %08X\n", TD->HwTD.gTD.Control.AsULONG);
    if (TD->HwTD.gTD.CurrentBuffer)
        DPRINT("gTD.CurrentBuffer - %08X\n", TD->HwTD.gTD.CurrentBuffer);
    if (TD->HwTD.gTD.NextTD)
        DPRINT("gTD.NextTD        - %08X\n", TD->HwTD.gTD.NextTD);
    if (TD->HwTD.gTD.BufferEnd)
        DPRINT("gTD.BufferEnd     - %08X\n", TD->HwTD.gTD.BufferEnd);

    if (TD->HwTD.SetupPacket.bmRequestType.B)
        DPRINT("bmRequestType     - %02X\n", TD->HwTD.SetupPacket.bmRequestType.B);
    if (TD->HwTD.SetupPacket.bRequest)
        DPRINT("bRequest          - %02X\n", TD->HwTD.SetupPacket.bRequest);
    if (TD->HwTD.SetupPacket.wValue.W)
        DPRINT("wValue            - %04X\n", TD->HwTD.SetupPacket.wValue.W);
    if (TD->HwTD.SetupPacket.wIndex.W)
        DPRINT("wIndex            - %04X\n", TD->HwTD.SetupPacket.wIndex.W);
    if (TD->HwTD.SetupPacket.wLength)
        DPRINT("wLength           - %04X\n", TD->HwTD.SetupPacket.wLength);

    DPRINT("PhysicalAddress   - %p\n", TD->PhysicalAddress);
    DPRINT("Flags             - %X\n", TD->Flags);
    DPRINT("OhciTransfer      - %08X\n", TD->OhciTransfer);
    DPRINT("NextTDVa          - %08X\n", TD->NextTDVa);
    if (TD->TransferLen)
        DPRINT("TransferLen       - %X\n", TD->TransferLen);
}

VOID
NTAPI
OHCI_EnableList(IN POHCI_EXTENSION OhciExtension,
                IN POHCI_ENDPOINT OhciEndpoint)
{
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG CommandStatusReg;
    ULONG TransferType;
    OHCI_REG_COMMAND_STATUS CommandStatus;

    DPRINT_OHCI("OHCI_EnableList: ... \n");

    OperationalRegs = OhciExtension->OperationalRegs;
    CommandStatusReg = (PULONG)&OperationalRegs->HcCommandStatus;

    CommandStatus.AsULONG = 0;

    if (READ_REGISTER_ULONG((PULONG)&OperationalRegs->HcControlHeadED))
        CommandStatus.ControlListFilled = 1;

    if (READ_REGISTER_ULONG((PULONG)&OperationalRegs->HcBulkHeadED))
        CommandStatus.BulkListFilled = 1;

    TransferType = OhciEndpoint->EndpointProperties.TransferType;

    if (TransferType == USBPORT_TRANSFER_TYPE_BULK)
        CommandStatus.BulkListFilled = 1;
    else if (TransferType == USBPORT_TRANSFER_TYPE_CONTROL)
        CommandStatus.ControlListFilled = 1;

    WRITE_REGISTER_ULONG(CommandStatusReg, CommandStatus.AsULONG);
}

VOID
NTAPI
OHCI_InsertEndpointInSchedule(IN POHCI_ENDPOINT OhciEndpoint)
{
    POHCI_STATIC_ED HeadED;
    POHCI_HCD_ED ED;
    POHCI_HCD_ED PrevED;
    PLIST_ENTRY HeadLink;

    DPRINT_OHCI("OHCI_InsertEndpointInSchedule: OhciEndpoint - %p\n",
                OhciEndpoint);

    ED = OhciEndpoint->HcdED;

    HeadED = OhciEndpoint->HeadED;
    HeadLink = &HeadED->Link;

    if (IsListEmpty(HeadLink))
    {
        InsertHeadList(HeadLink, &ED->HcdEDLink);

        if (HeadED->Type == OHCI_STATIC_ED_TYPE_CONTROL ||
            HeadED->Type == OHCI_STATIC_ED_TYPE_BULK)
        {
            ED->HwED.NextED = READ_REGISTER_ULONG(HeadED->pNextED);
            WRITE_REGISTER_ULONG(HeadED->pNextED, ED->PhysicalAddress);
        }
        else if (HeadED->Type == OHCI_STATIC_ED_TYPE_INTERRUPT)
        {
            ED->HwED.NextED = *HeadED->pNextED;
            *HeadED->pNextED = ED->PhysicalAddress;
        }
        else
        {
            DPRINT1("OHCI_InsertEndpointInSchedule: Unknown HeadED->Type - %x\n",
                    HeadED->Type);
            DbgBreakPoint();
        }
    }
    else
    {
        PrevED = CONTAINING_RECORD(HeadLink->Blink,
                                   OHCI_HCD_ED,
                                   HcdEDLink);

        InsertTailList(HeadLink, &ED->HcdEDLink);

        ED->HwED.NextED = 0;
        PrevED->HwED.NextED = ED->PhysicalAddress;
    }
}

POHCI_HCD_ED
NTAPI
OHCI_InitializeED(IN POHCI_ENDPOINT OhciEndpoint,
                  IN POHCI_HCD_ED ED,
                  IN POHCI_HCD_TD FirstTD,
                  IN ULONG_PTR EdPA)
{
    OHCI_ENDPOINT_CONTROL EndpointControl;
    PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties;

    DPRINT_OHCI("OHCI_InitializeED: OhciEndpoint - %p, ED - %p, FirstTD - %p, EdPA - %p\n",
                OhciEndpoint,
                ED,
                FirstTD,
                EdPA);

    RtlZeroMemory(ED, sizeof(OHCI_HCD_ED));

    ED->PhysicalAddress = EdPA;

    EndpointProperties = &OhciEndpoint->EndpointProperties;

    EndpointControl = ED->HwED.EndpointControl;

    EndpointControl.FunctionAddress = EndpointProperties->DeviceAddress;
    EndpointControl.EndpointNumber = EndpointProperties->EndpointAddress;
    EndpointControl.MaximumPacketSize = EndpointProperties->TotalMaxPacketSize;

    if (EndpointProperties->TransferType == USBPORT_TRANSFER_TYPE_CONTROL)
    {
        EndpointControl.Direction = OHCI_ED_DATA_FLOW_DIRECTION_FROM_TD;
    }
    else if (EndpointProperties->Direction)
    {
        EndpointControl.Direction = OHCI_ED_DATA_FLOW_DIRECTION_OUT;
    }
    else
    {
        EndpointControl.Direction = OHCI_ED_DATA_FLOW_DIRECTION_IN;
    }

    if (EndpointProperties->DeviceSpeed == UsbLowSpeed)
        EndpointControl.Speed = OHCI_ENDPOINT_LOW_SPEED;

    if (EndpointProperties->TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
        EndpointControl.Format = OHCI_ENDPOINT_ISOCHRONOUS_FORMAT;
    else
        EndpointControl.sKip = 1;

    ED->HwED.EndpointControl = EndpointControl;

    ED->HwED.TailPointer = FirstTD->PhysicalAddress;
    ED->HwED.HeadPointer = FirstTD->PhysicalAddress;

    FirstTD->Flags |= OHCI_HCD_TD_FLAG_ALLOCATED;

    OhciEndpoint->HcdTailP = FirstTD;
    OhciEndpoint->HcdHeadP = FirstTD;

    return ED;
}

VOID
NTAPI
OHCI_InitializeTDs(IN POHCI_ENDPOINT OhciEndpoint,
                   IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties)
{
    POHCI_HCD_TD TdVA;
    ULONG TdPA;
    ULONG TdCount;
    ULONG ix;

    ASSERT(EndpointProperties->BufferLength > sizeof(OHCI_HCD_ED));

    TdCount = (EndpointProperties->BufferLength - sizeof(OHCI_HCD_ED)) /
              sizeof(OHCI_HCD_TD);

    OhciEndpoint->MaxTransferDescriptors = TdCount;

    DPRINT_OHCI("OHCI_InitializeTDs: TdCount - %x\n", TdCount);

    ASSERT(TdCount > 0);

    TdVA = OhciEndpoint->FirstTD;

    TdPA = EndpointProperties->BufferPA + sizeof(OHCI_HCD_ED);

    for (ix = 0; ix < TdCount; ix++)
    {
        DPRINT_OHCI("OHCI_InitializeTDs: TdVA - %p, TdPA - %08X\n", TdVA, TdPA);

        RtlZeroMemory(TdVA, sizeof(OHCI_HCD_TD));

        TdVA->PhysicalAddress = TdPA;
        TdVA->Flags = 0;
        TdVA->OhciTransfer = 0;

        TdVA++;
        TdPA += sizeof(OHCI_HCD_TD);
    }
}

MPSTATUS
NTAPI
OHCI_OpenControlEndpoint(IN POHCI_EXTENSION OhciExtension,
                         IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties,
                         IN POHCI_ENDPOINT OhciEndpoint)
{
    POHCI_HCD_ED ED;

    DPRINT_OHCI("OHCI_OpenControlEndpoint: ... \n");

    ED = (POHCI_HCD_ED)EndpointProperties->BufferVA;

    OhciEndpoint->FirstTD = (POHCI_HCD_TD)((ULONG_PTR)ED + sizeof(OHCI_HCD_ED));
    OhciEndpoint->HeadED = &OhciExtension->ControlStaticED;

    OHCI_InitializeTDs(OhciEndpoint, EndpointProperties);

    OhciEndpoint->HcdED = OHCI_InitializeED(OhciEndpoint,
                                            ED,
                                            OhciEndpoint->FirstTD,
                                            EndpointProperties->BufferPA);

    OhciEndpoint->HcdED->Flags = OHCI_HCD_ED_FLAG_CONTROL |
                                 OHCI_HCD_ED_FLAG_RESET_ON_HALT;

    OHCI_InsertEndpointInSchedule(OhciEndpoint);

    return MP_STATUS_SUCCESS;
}

MPSTATUS
NTAPI
OHCI_OpenBulkEndpoint(IN POHCI_EXTENSION OhciExtension,
                      IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties,
                      IN POHCI_ENDPOINT OhciEndpoint)
{
    POHCI_HCD_ED ED;

    DPRINT_OHCI("OHCI_OpenBulkEndpoint: ... \n");

    ED = (POHCI_HCD_ED)EndpointProperties->BufferVA;

    OhciEndpoint->FirstTD = (POHCI_HCD_TD)((ULONG_PTR)ED + sizeof(OHCI_HCD_ED));
    OhciEndpoint->HeadED = &OhciExtension->BulkStaticED;

    OHCI_InitializeTDs(OhciEndpoint, EndpointProperties);

    OhciEndpoint->HcdED = OHCI_InitializeED(OhciEndpoint,
                                            ED,
                                            OhciEndpoint->FirstTD,
                                            EndpointProperties->BufferPA);

    OHCI_InsertEndpointInSchedule(OhciEndpoint);

    return MP_STATUS_SUCCESS;
}

MPSTATUS
NTAPI
OHCI_OpenInterruptEndpoint(IN POHCI_EXTENSION OhciExtension,
                           IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties,
                           IN POHCI_ENDPOINT OhciEndpoint)
{
    UCHAR Period;
    ULONG PeriodIdx = 0;
    POHCI_HCD_ED ED;
    ULONG ScheduleOffset;

    DPRINT_OHCI("OHCI_OpenInterruptEndpoint: ... \n");

    ED = (POHCI_HCD_ED)EndpointProperties->BufferVA;

    OhciEndpoint->FirstTD = (POHCI_HCD_TD)((ULONG_PTR)ED + sizeof(OHCI_HCD_ED));

    Period = EndpointProperties->Period;

    ASSERT(Period != 0);

    while (!(Period & 1))
    {
        PeriodIdx++;
        Period >>= 1;
    }

    ASSERT(PeriodIdx < ARRAYSIZE(Index));

    ScheduleOffset = EndpointProperties->ScheduleOffset;
    DPRINT_OHCI("OHCI_OpenInterruptEndpoint: InitTD. Index[PeriodIdx] - %x, ScheduleOffset - %x\n",
                Index[PeriodIdx],
                ScheduleOffset);

    OhciEndpoint->HeadED = &OhciExtension->IntStaticED[Index[PeriodIdx] +
                                                       ScheduleOffset];

    //OhciEndpoint->HeadED->UsbBandwidth += EndpointProperties->UsbBandwidth;

    OHCI_InitializeTDs(OhciEndpoint, EndpointProperties);

    OhciEndpoint->HcdED = OHCI_InitializeED(OhciEndpoint,
                                            ED,
                                            OhciEndpoint->FirstTD,
                                            EndpointProperties->BufferPA);

    OHCI_InsertEndpointInSchedule(OhciEndpoint);

    return MP_STATUS_SUCCESS;
}

MPSTATUS
NTAPI
OHCI_OpenIsoEndpoint(IN POHCI_EXTENSION OhciExtension,
                     IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties,
                     IN POHCI_ENDPOINT OhciEndpoint)
{
    DPRINT1("OHCI_OpenIsoEndpoint: UNIMPLEMENTED. FIXME\n");
    return MP_STATUS_NOT_SUPPORTED;
}

MPSTATUS
NTAPI
OHCI_OpenEndpoint(IN PVOID ohciExtension,
                  IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties,
                  IN PVOID ohciEndpoint)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    ULONG TransferType;
    MPSTATUS MPStatus;

    DPRINT_OHCI("OHCI_OpenEndpoint: ... \n");

    RtlCopyMemory(&OhciEndpoint->EndpointProperties,
                  EndpointProperties,
                  sizeof(OhciEndpoint->EndpointProperties));

    InitializeListHead(&OhciEndpoint->TDList);

    TransferType = EndpointProperties->TransferType;

    switch (TransferType)
    {
        case USBPORT_TRANSFER_TYPE_ISOCHRONOUS:
            MPStatus = OHCI_OpenIsoEndpoint(OhciExtension,
                                            EndpointProperties,
                                            OhciEndpoint);
            break;

        case USBPORT_TRANSFER_TYPE_CONTROL:
            MPStatus = OHCI_OpenControlEndpoint(OhciExtension,
                                                EndpointProperties,
                                                OhciEndpoint);
            break;

        case USBPORT_TRANSFER_TYPE_BULK:
            MPStatus = OHCI_OpenBulkEndpoint(OhciExtension,
                                             EndpointProperties,
                                             OhciEndpoint);
            break;

        case USBPORT_TRANSFER_TYPE_INTERRUPT:
            MPStatus = OHCI_OpenInterruptEndpoint(OhciExtension,
                                                  EndpointProperties,
                                                  OhciEndpoint);
            break;

        default:
            MPStatus = MP_STATUS_NOT_SUPPORTED;
            break;
    }

    return MPStatus;
}

MPSTATUS
NTAPI
OHCI_ReopenEndpoint(IN PVOID ohciExtension,
                    IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties,
                    IN PVOID ohciEndpoint)
{
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_HCD_ED ED;

    DPRINT_OHCI("OHCI_ReopenEndpoint: ... \n");

    ED = OhciEndpoint->HcdED;

    RtlCopyMemory(&OhciEndpoint->EndpointProperties,
                  EndpointProperties,
                  sizeof(OhciEndpoint->EndpointProperties));

    ED->HwED.EndpointControl.FunctionAddress =
        OhciEndpoint->EndpointProperties.DeviceAddress;

    ED->HwED.EndpointControl.MaximumPacketSize =
        OhciEndpoint->EndpointProperties.TotalMaxPacketSize;

    return MP_STATUS_SUCCESS;
}

VOID
NTAPI
OHCI_QueryEndpointRequirements(IN PVOID ohciExtension,
                               IN PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties,
                               IN PUSBPORT_ENDPOINT_REQUIREMENTS EndpointRequirements)
{
    ULONG TransferType;

    DPRINT_OHCI("OHCI_QueryEndpointRequirements: ... \n");

    TransferType = EndpointProperties->TransferType;

    switch (TransferType)
    {
        case USBPORT_TRANSFER_TYPE_ISOCHRONOUS:
            DPRINT_OHCI("OHCI_QueryEndpointRequirements: IsoTransfer\n");
            EndpointRequirements->MaxTransferSize = OHCI_MAX_ISO_TRANSFER_SIZE;
            EndpointRequirements->HeaderBufferSize =
            sizeof(OHCI_HCD_ED) + OHCI_MAX_ISO_TD_COUNT * sizeof(OHCI_HCD_TD);
            break;

        case USBPORT_TRANSFER_TYPE_CONTROL:
            DPRINT_OHCI("OHCI_QueryEndpointRequirements: ControlTransfer\n");
            EndpointRequirements->MaxTransferSize = OHCI_MAX_CONTROL_TRANSFER_SIZE;
            EndpointRequirements->HeaderBufferSize =
            sizeof(OHCI_HCD_ED) + OHCI_MAX_CONTROL_TD_COUNT * sizeof(OHCI_HCD_TD);
            break;

        case USBPORT_TRANSFER_TYPE_BULK:
            DPRINT_OHCI("OHCI_QueryEndpointRequirements: BulkTransfer\n");
            EndpointRequirements->MaxTransferSize = OHCI_MAX_BULK_TRANSFER_SIZE;
            EndpointRequirements->HeaderBufferSize =
            sizeof(OHCI_HCD_ED) + OHCI_MAX_BULK_TD_COUNT * sizeof(OHCI_HCD_TD);
            break;

        case USBPORT_TRANSFER_TYPE_INTERRUPT:
            DPRINT_OHCI("OHCI_QueryEndpointRequirements: InterruptTransfer\n");
            EndpointRequirements->MaxTransferSize = OHCI_MAX_INTERRUPT_TRANSFER_SIZE;
            EndpointRequirements->HeaderBufferSize =
            sizeof(OHCI_HCD_ED) + OHCI_MAX_INTERRUPT_TD_COUNT * sizeof(OHCI_HCD_TD);
            break;

        default:
            DPRINT1("OHCI_QueryEndpointRequirements: Unknown TransferType - %x\n",
                    TransferType);
            DbgBreakPoint();
            break;
    }
}

VOID
NTAPI
OHCI_CloseEndpoint(IN PVOID ohciExtension,
                   IN PVOID ohciEndpoint,
                   IN BOOLEAN IsDoDisablePeriodic)
{
#if DBG
    DPRINT1("OHCI_CloseEndpoint: Not supported\n");
#endif
    return;
}

MPSTATUS
NTAPI
OHCI_TakeControlHC(IN POHCI_EXTENSION OhciExtension,
                   IN PUSBPORT_RESOURCES Resources)
{
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG ControlReg;
    PULONG InterruptEnableReg;
    PULONG InterruptDisableReg;
    PULONG CommandStatusReg;
    PULONG InterruptStatusReg;
    OHCI_REG_CONTROL Control;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntEnable;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntDisable;
    OHCI_REG_COMMAND_STATUS CommandStatus;
    OHCI_REG_INTERRUPT_STATUS IntStatus;
    LARGE_INTEGER StartTicks, CurrentTicks;
    UINT32 TicksDiff;

    DPRINT("OHCI_TakeControlHC: ...\n");

    OperationalRegs = OhciExtension->OperationalRegs;

    ControlReg = (PULONG)&OperationalRegs->HcControl;
    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;
    InterruptDisableReg = (PULONG)&OperationalRegs->HcInterruptDisable;
    CommandStatusReg = (PULONG)&OperationalRegs->HcCommandStatus;
    InterruptStatusReg = (PULONG)&OperationalRegs->HcInterruptStatus;

    /* 5.1.1.3 Take Control of Host Controller */
    Control.AsULONG = READ_REGISTER_ULONG(ControlReg);

    if (Control.InterruptRouting == 0)
        return MP_STATUS_SUCCESS;

    DPRINT1("OHCI_TakeControlHC: detected Legacy BIOS\n");

    IntEnable.AsULONG = READ_REGISTER_ULONG(InterruptEnableReg);

    DPRINT("OHCI_TakeControlHC: Control - %lX, IntEnable - %lX\n",
           Control.AsULONG,
           IntEnable.AsULONG);

    if (Control.HostControllerFunctionalState == OHCI_HC_STATE_RESET &&
        IntEnable.AsULONG == 0)
    {
        Control.AsULONG = 0;
        WRITE_REGISTER_ULONG(ControlReg, Control.AsULONG);
        return MP_STATUS_SUCCESS;
    }

    /* Enable interrupt generations */
    IntEnable.AsULONG = 0;
    IntEnable.MasterInterruptEnable = 1;

    WRITE_REGISTER_ULONG(InterruptEnableReg, IntEnable.AsULONG);

    /* Request a change of control of the HC */
    CommandStatus.AsULONG = 0;
    CommandStatus.OwnershipChangeRequest = 1;

    WRITE_REGISTER_ULONG(CommandStatusReg, CommandStatus.AsULONG);

    /* Disable interrupt generation due to Root Hub Status Change */
    IntDisable.AsULONG = 0;
    IntDisable.RootHubStatusChange = 1;

    WRITE_REGISTER_ULONG(InterruptDisableReg, IntDisable.AsULONG);

    /* Monitoring the InterruptRouting bit
       to determine when the ownership change has taken effect. */

    TicksDiff = (500 * 10000) / KeQueryTimeIncrement(); // 500 ms
    KeQueryTickCount(&StartTicks);

    do
    {
        Control.AsULONG = READ_REGISTER_ULONG(ControlReg);

        if (Control.InterruptRouting == 0)
        {
            /* Clear all bits in register */
            IntStatus.AsULONG = 0xFFFFFFFF;
            WRITE_REGISTER_ULONG(InterruptStatusReg, IntStatus.AsULONG);

            /* Disable interrupt generations */
            IntDisable.AsULONG = 0;
            IntDisable.MasterInterruptEnable = 1;

            WRITE_REGISTER_ULONG(InterruptDisableReg, IntDisable.AsULONG);

            return MP_STATUS_SUCCESS;
        }

        KeQueryTickCount(&CurrentTicks);
    }
    while (CurrentTicks.QuadPart - StartTicks.QuadPart < TicksDiff);

    return MP_STATUS_HW_ERROR;
}

MPSTATUS
NTAPI
OHCI_StartController(IN PVOID ohciExtension,
                     IN PUSBPORT_RESOURCES Resources)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG CommandStatusReg;
    PULONG FmIntervalReg;
    PULONG ControlReg;
    PULONG InterruptEnableReg;
    PULONG RhStatusReg;
    OHCI_REG_COMMAND_STATUS CommandStatus;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE Interrupts;
    OHCI_REG_RH_STATUS RhStatus;
    OHCI_REG_FRAME_INTERVAL FrameInterval;
    ULONG MaxFrameIntervalAdjusting;
    OHCI_REG_CONTROL Control;
    UCHAR HeadIndex;
    POHCI_ENDPOINT_DESCRIPTOR IntED;
    ULONG_PTR IntEdPA;
    POHCI_HCCA OhciHCCA;
    LARGE_INTEGER StartTicks, CurrentTicks;
    UINT32 TicksDiff;
    ULONG ix;
    ULONG jx;
    MPSTATUS MPStatus = MP_STATUS_SUCCESS;

    DPRINT_OHCI("OHCI_StartController: ohciExtension - %p, Resources - %p\n",
                ohciExtension,
                Resources);

    /* HC on-chip operational registers */
    OperationalRegs = (POHCI_OPERATIONAL_REGISTERS)Resources->ResourceBase;
    OhciExtension->OperationalRegs = OperationalRegs;

    CommandStatusReg = (PULONG)&OperationalRegs->HcCommandStatus;
    FmIntervalReg = (PULONG)&OperationalRegs->HcFmInterval;
    ControlReg = (PULONG)&OperationalRegs->HcControl;
    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;
    RhStatusReg = (PULONG)&OperationalRegs->HcRhStatus;

    /* 5.1.1 Initialization */

    MPStatus = OHCI_TakeControlHC(OhciExtension, Resources);

    if (MPStatus != MP_STATUS_SUCCESS)
    {
        DPRINT1("OHCI_StartController: OHCI_TakeControlHC return MPStatus - %x\n",
                MPStatus);

        return MPStatus;
    }

    OhciExtension->HcResourcesVA = (POHCI_HC_RESOURCES)Resources->StartVA;
    OhciExtension->HcResourcesPA = Resources->StartPA;

    DPRINT_OHCI("OHCI_StartController: HcResourcesVA - %p, HcResourcesPA - %lx\n",
                OhciExtension->HcResourcesVA,
                OhciExtension->HcResourcesPA);

    /* 5.2.7.2 Interrupt */

    /* Build structure of interrupt static EDs */
    for (ix = 0; ix < INTERRUPT_ENDPOINTs; ix++)
    {
        IntED = &OhciExtension->HcResourcesVA->InterrruptHeadED[ix];
        IntEdPA = OhciExtension->HcResourcesPA + FIELD_OFFSET(OHCI_HC_RESOURCES, InterrruptHeadED[ix]);

        if (ix == (ENDPOINT_INTERRUPT_1ms - 1))
        {
            HeadIndex = ED_EOF;
            IntED->NextED = 0;
        }
        else
        {
            HeadIndex = ((ix - 1) / 2);

            ASSERT(HeadIndex >= (ENDPOINT_INTERRUPT_1ms - 1) &&
                   HeadIndex < (INTERRUPT_ENDPOINTs - ENDPOINT_INTERRUPT_32ms));

            IntED->NextED = OhciExtension->IntStaticED[HeadIndex].PhysicalAddress;
        }

        IntED->EndpointControl.sKip = 1;

        IntED->TailPointer = 0;
        IntED->HeadPointer = 0;

        OhciExtension->IntStaticED[ix].HwED = IntED;
        OhciExtension->IntStaticED[ix].PhysicalAddress = IntEdPA;
        OhciExtension->IntStaticED[ix].HeadIndex = HeadIndex;
        OhciExtension->IntStaticED[ix].pNextED = &IntED->NextED;
        OhciExtension->IntStaticED[ix].Type = OHCI_STATIC_ED_TYPE_INTERRUPT;

        InitializeListHead(&OhciExtension->IntStaticED[ix].Link);
    }

    OhciHCCA = &OhciExtension->HcResourcesVA->HcHCCA;
    DPRINT_OHCI("OHCI_InitializeSchedule: OhciHCCA - %p\n", OhciHCCA);

    /* Set head pointers which start from HCCA */
    for (ix = 0, jx = (INTERRUPT_ENDPOINTs - ENDPOINT_INTERRUPT_32ms);
         ix < OHCI_NUMBER_OF_INTERRUPTS;
         ix++, jx++)
    {
        OhciHCCA->InterrruptTable[Balance[ix]] =
            OhciExtension->IntStaticED[jx].PhysicalAddress;

        OhciExtension->IntStaticED[jx].pNextED =
            (PULONG)&OhciHCCA->InterrruptTable[Balance[ix]];

        OhciExtension->IntStaticED[jx].HccaIndex = Balance[ix];
    }

    DPRINT_OHCI("OHCI_InitializeSchedule: ix - %x\n", ix);

    /* Init static Control and Bulk EDs head pointers which start from HCCA */
    InitializeListHead(&OhciExtension->ControlStaticED.Link);

    OhciExtension->ControlStaticED.HeadIndex = ED_EOF;
    OhciExtension->ControlStaticED.Type = OHCI_STATIC_ED_TYPE_CONTROL;
    OhciExtension->ControlStaticED.pNextED = &OperationalRegs->HcControlHeadED;

    InitializeListHead(&OhciExtension->BulkStaticED.Link);

    OhciExtension->BulkStaticED.HeadIndex = ED_EOF;
    OhciExtension->BulkStaticED.Type = OHCI_STATIC_ED_TYPE_BULK;
    OhciExtension->BulkStaticED.pNextED = &OperationalRegs->HcBulkHeadED;

    /* 6.3.1 Frame Timing */
    FrameInterval.AsULONG = READ_REGISTER_ULONG(FmIntervalReg);

    MaxFrameIntervalAdjusting = OHCI_DEFAULT_FRAME_INTERVAL / 10; // 10%

    if ((FrameInterval.FrameInterval) < (OHCI_DEFAULT_FRAME_INTERVAL - MaxFrameIntervalAdjusting) ||
        (FrameInterval.FrameInterval) > (OHCI_DEFAULT_FRAME_INTERVAL + MaxFrameIntervalAdjusting))
    {
        FrameInterval.FrameInterval = OHCI_DEFAULT_FRAME_INTERVAL;
    }

    /* 5.4 FrameInterval Counter */
    FrameInterval.FrameIntervalToggle = 1;

    /* OHCI_MAXIMUM_OVERHEAD is the maximum overhead per frame */
    FrameInterval.FSLargestDataPacket =
        ((FrameInterval.FrameInterval - OHCI_MAXIMUM_OVERHEAD) * 6) / 7;

    OhciExtension->FrameInterval = FrameInterval;

    DPRINT_OHCI("OHCI_StartController: FrameInterval - %lX\n",
                FrameInterval.AsULONG);

    /* Reset HostController */
    CommandStatus.AsULONG = 0;
    CommandStatus.HostControllerReset = 1;

    WRITE_REGISTER_ULONG(CommandStatusReg, CommandStatus.AsULONG);

    KeStallExecutionProcessor(25);

    Control.AsULONG = READ_REGISTER_ULONG(ControlReg);
    Control.HostControllerFunctionalState = OHCI_HC_STATE_RESET;

    WRITE_REGISTER_ULONG(ControlReg, Control.AsULONG);

    TicksDiff = (500 * 10000) / KeQueryTimeIncrement(); // 500 ms
    KeQueryTickCount(&StartTicks);

    while (TRUE)
    {
        WRITE_REGISTER_ULONG(FmIntervalReg, OhciExtension->FrameInterval.AsULONG);
        FrameInterval.AsULONG = READ_REGISTER_ULONG(FmIntervalReg);

        KeQueryTickCount(&CurrentTicks);

        if (CurrentTicks.QuadPart - StartTicks.QuadPart >= TicksDiff)
        {
            MPStatus = MP_STATUS_HW_ERROR;
            break;
        }

        if (FrameInterval.AsULONG == OhciExtension->FrameInterval.AsULONG)
        {
            MPStatus = MP_STATUS_SUCCESS;
            break;
        }
    }

    if (MPStatus != MP_STATUS_SUCCESS)
    {
        DPRINT_OHCI("OHCI_StartController: frame interval not set\n");
        return MPStatus;
    }

    /* Setup HcPeriodicStart register */
    WRITE_REGISTER_ULONG(&OperationalRegs->HcPeriodicStart,
                        (OhciExtension->FrameInterval.FrameInterval * 9) / 10); //90%

    /* Setup HcHCCA register */
    WRITE_REGISTER_ULONG(&OperationalRegs->HcHCCA,
                         OhciExtension->HcResourcesPA + FIELD_OFFSET(OHCI_HC_RESOURCES, HcHCCA));

    /* Setup HcInterruptEnable register */
    Interrupts.AsULONG = 0;

    Interrupts.SchedulingOverrun = 1;
    Interrupts.WritebackDoneHead = 1;
    Interrupts.UnrecoverableError = 1;
    Interrupts.FrameNumberOverflow = 1;
    Interrupts.OwnershipChange = 1;

    WRITE_REGISTER_ULONG(InterruptEnableReg, Interrupts.AsULONG);

    /* Setup HcControl register */
    Control.AsULONG = READ_REGISTER_ULONG(ControlReg);

    Control.ControlBulkServiceRatio = 0; // FIXME (1 : 1)
    Control.PeriodicListEnable = 1;
    Control.IsochronousEnable = 1;
    Control.ControlListEnable = 1;
    Control.BulkListEnable = 1;
    Control.HostControllerFunctionalState = OHCI_HC_STATE_OPERATIONAL;

    WRITE_REGISTER_ULONG(ControlReg, Control.AsULONG);

    /* Setup HcRhStatus register */
    RhStatus.AsULONG = 0;
    RhStatus.SetGlobalPower = 1;

    WRITE_REGISTER_ULONG(RhStatusReg, RhStatus.AsULONG);

    return MP_STATUS_SUCCESS;
}

VOID
NTAPI
OHCI_StopController(IN PVOID ohciExtension,
                    IN BOOLEAN IsDoDisableInterrupts)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG ControlReg;
    PULONG InterruptDisableReg;
    PULONG InterruptStatusReg;
    OHCI_REG_CONTROL Control;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntDisable;
    OHCI_REG_INTERRUPT_STATUS IntStatus;

    DPRINT("OHCI_StopController: ... \n");

    OperationalRegs = OhciExtension->OperationalRegs;

    ControlReg = (PULONG)&OperationalRegs->HcControl;
    InterruptDisableReg = (PULONG)&OperationalRegs->HcInterruptDisable;
    InterruptStatusReg = (PULONG)&OperationalRegs->HcInterruptStatus;

    /* Setup HcControl register */
    Control.AsULONG = READ_REGISTER_ULONG(ControlReg);

    Control.PeriodicListEnable = 0;
    Control.IsochronousEnable = 0;
    Control.ControlListEnable = 0;
    Control.BulkListEnable = 0;
    Control.HostControllerFunctionalState = OHCI_HC_STATE_SUSPEND;
    Control.RemoteWakeupEnable = 0;

    WRITE_REGISTER_ULONG(ControlReg, Control.AsULONG);

    /* Disable interrupt generations */
    IntDisable.AsULONG = 0xFFFFFFFF;
    WRITE_REGISTER_ULONG(InterruptDisableReg, IntDisable.AsULONG);

    /* Clear all bits in HcInterruptStatus register */
    IntStatus.AsULONG = 0xFFFFFFFF;
    WRITE_REGISTER_ULONG(InterruptStatusReg, IntStatus.AsULONG);
}

VOID
NTAPI
OHCI_SuspendController(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG ControlReg;
    PULONG InterruptEnableReg;
    OHCI_REG_CONTROL Control;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE InterruptReg;

    DPRINT("OHCI_SuspendController: ... \n");

    OperationalRegs = OhciExtension->OperationalRegs;
    ControlReg = (PULONG)&OperationalRegs->HcControl;
    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;

    /* Disable all interrupt generations */
    WRITE_REGISTER_ULONG(&OperationalRegs->HcInterruptDisable.AsULONG,
                         0xFFFFFFFF);

    /* Clear all bits in HcInterruptStatus register */
    WRITE_REGISTER_ULONG(&OperationalRegs->HcInterruptStatus.AsULONG,
                         0xFFFFFFFF);

    /* Setup HcControl register */
    Control.AsULONG = READ_REGISTER_ULONG(ControlReg);
    Control.HostControllerFunctionalState = OHCI_HC_STATE_SUSPEND;
    Control.RemoteWakeupEnable =  1;

    WRITE_REGISTER_ULONG(ControlReg, Control.AsULONG);

    /* Setup HcInterruptEnable register */
    InterruptReg.AsULONG = 0;
    InterruptReg.ResumeDetected = 1;
    InterruptReg.UnrecoverableError = 1;
    InterruptReg.RootHubStatusChange = 1;
    InterruptReg.MasterInterruptEnable = 1;

    WRITE_REGISTER_ULONG(InterruptEnableReg, InterruptReg.AsULONG);
}

MPSTATUS
NTAPI
OHCI_ResumeController(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG ControlReg;
    PULONG InterruptEnableReg;
    POHCI_HCCA HcHCCA;
    OHCI_REG_CONTROL control;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE InterruptReg;

    DPRINT("OHCI_ResumeController \n");

    OperationalRegs = OhciExtension->OperationalRegs;
    ControlReg = (PULONG)&OperationalRegs->HcControl;
    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;

    control.AsULONG = READ_REGISTER_ULONG(ControlReg);

    if (control.HostControllerFunctionalState != OHCI_HC_STATE_SUSPEND)
        return MP_STATUS_HW_ERROR;

    HcHCCA = &OhciExtension->HcResourcesVA->HcHCCA;
    HcHCCA->Pad1 = 0;

    /* Setup HcControl register */
    control.HostControllerFunctionalState = OHCI_HC_STATE_OPERATIONAL;
    WRITE_REGISTER_ULONG(ControlReg, control.AsULONG);

    /* Setup HcInterruptEnable register */
    InterruptReg.AsULONG = 0;
    InterruptReg.SchedulingOverrun = 1;
    InterruptReg.WritebackDoneHead = 1;
    InterruptReg.UnrecoverableError = 1;
    InterruptReg.FrameNumberOverflow = 1;
    InterruptReg.OwnershipChange = 1;

    WRITE_REGISTER_ULONG(InterruptEnableReg, InterruptReg.AsULONG);
    WRITE_REGISTER_ULONG(ControlReg, control.AsULONG);

    return MP_STATUS_SUCCESS;
}

BOOLEAN
NTAPI
OHCI_HardwarePresent(IN POHCI_EXTENSION OhciExtension,
                     IN BOOLEAN IsInvalidateController)
{
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG CommandStatusReg;

    OperationalRegs = OhciExtension->OperationalRegs;
    CommandStatusReg = (PULONG)&OperationalRegs->HcCommandStatus;

    if (READ_REGISTER_ULONG(CommandStatusReg) != 0xFFFFFFFF)
        return TRUE;

    DPRINT1("OHCI_HardwarePresent: IsInvalidateController - %x\n",
            IsInvalidateController);

    if (IsInvalidateController)
    {
        RegPacket.UsbPortInvalidateController(OhciExtension,
                                              USBPORT_INVALIDATE_CONTROLLER_SURPRISE_REMOVE);
    }

    return FALSE;
}

BOOLEAN
NTAPI
OHCI_InterruptService(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    OHCI_REG_INTERRUPT_STATUS IntStatus;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntEnable;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntDisable;
    BOOLEAN HardwarePresent = FALSE;

    DPRINT_OHCI("OHCI_Interrupt: Ext %p\n", OhciExtension);

    OperationalRegs = OhciExtension->OperationalRegs;

    HardwarePresent = OHCI_HardwarePresent(OhciExtension, FALSE);

    if (!HardwarePresent)
        return FALSE;

    IntEnable.AsULONG = READ_REGISTER_ULONG((PULONG)&OperationalRegs->HcInterruptEnable);
    IntStatus.AsULONG = READ_REGISTER_ULONG((PULONG)&OperationalRegs->HcInterruptStatus) & IntEnable.AsULONG;

    if ((IntStatus.AsULONG == 0) || (IntEnable.MasterInterruptEnable == 0))
        return FALSE;

    if (IntStatus.UnrecoverableError)
        DPRINT1("OHCI_InterruptService: IntStatus.UnrecoverableError\n");

    if (IntStatus.FrameNumberOverflow)
    {
        POHCI_HCCA HcHCCA;
        ULONG fm;
        ULONG hp;

        HcHCCA = &OhciExtension->HcResourcesVA->HcHCCA;

        DPRINT("FrameNumberOverflow %lX\n", HcHCCA->FrameNumber);

        hp = OhciExtension->FrameHighPart;
        fm = HcHCCA->FrameNumber;

        /* Increment value of FrameHighPart */
        OhciExtension->FrameHighPart += 1 * (1 << 16) - ((hp ^ fm) & 0x8000);
    }

    /* Disable interrupt generation */
    IntDisable.AsULONG = 0;
    IntDisable.MasterInterruptEnable = 1;
    WRITE_REGISTER_ULONG((PULONG)&OperationalRegs->HcInterruptDisable,
                         IntDisable.AsULONG);

    return TRUE;
}

VOID
NTAPI
OHCI_InterruptDpc(IN PVOID ohciExtension,
                  IN BOOLEAN IsDoEnableInterrupts)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG InterruptDisableReg;
    PULONG InterruptEnableReg;
    PULONG InterruptStatusReg;
    OHCI_REG_INTERRUPT_STATUS IntStatus;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE InterruptBits;
    POHCI_HCCA HcHCCA;

    OperationalRegs = OhciExtension->OperationalRegs;

    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;
    InterruptDisableReg = (PULONG)&OperationalRegs->HcInterruptDisable;
    InterruptStatusReg = (PULONG)&OperationalRegs->HcInterruptStatus;

    DPRINT_OHCI("OHCI_InterruptDpc: OhciExtension - %p, IsDoEnableInterrupts - %x\n",
                OhciExtension,
                IsDoEnableInterrupts);

    IntStatus.AsULONG = READ_REGISTER_ULONG(InterruptStatusReg);

    if (IntStatus.RootHubStatusChange)
    {
        DPRINT_OHCI("OHCI_InterruptDpc: RootHubStatusChange\n");
        RegPacket.UsbPortInvalidateRootHub(OhciExtension);
    }

    if (IntStatus.WritebackDoneHead)
    {
        DPRINT_OHCI("OHCI_InterruptDpc: WritebackDoneHead\n");

        HcHCCA = &OhciExtension->HcResourcesVA->HcHCCA;
        HcHCCA->DoneHead = 0;

        RegPacket.UsbPortInvalidateEndpoint(OhciExtension, NULL);
    }

    if (IntStatus.StartofFrame)
    {
        /* Disable interrupt generation due to Start of Frame */
        InterruptBits.AsULONG = 0;
        InterruptBits.StartofFrame = 1;

        WRITE_REGISTER_ULONG(InterruptDisableReg, InterruptBits.AsULONG);
    }

    if (IntStatus.ResumeDetected)
        DPRINT1("OHCI_IntDpc: ResumeDetected\n");

    if (IntStatus.UnrecoverableError)
    {
        DPRINT1("OHCI_IntDpc: UnrecoverableError\n");
    }

    WRITE_REGISTER_ULONG(InterruptStatusReg, IntStatus.AsULONG);

    if (IsDoEnableInterrupts)
    {
        /*  Enable interrupt generation */
        InterruptBits.AsULONG = 0;
        InterruptBits.MasterInterruptEnable = 1;

        WRITE_REGISTER_ULONG(InterruptEnableReg, InterruptBits.AsULONG);
    }
}

/**
 * @brief      Forms the next General Transfer Descriptor for the current transfer
 *
 * @param[in]  OhciExtension  The ohci extension
 * @param[in]  TransferedLen  The consolidated length of all previous descriptors' buffers
 * @param[in]  OhciTransfer   The ohci transfer
 * @param[out] TD             The transfer descriptor we are forming
 * @param[in]  SGList         The scatter/gather list
 *
 * @return     The length of all previous buffers summed with the length of the current buffer
 */
static
ULONG
OHCI_MapTransferToTD(IN POHCI_EXTENSION OhciExtension,
                     IN ULONG TransferedLen,
                     IN POHCI_TRANSFER OhciTransfer,
                     OUT POHCI_HCD_TD TD,
                     IN PUSBPORT_SCATTER_GATHER_LIST SGList)
{
    PUSBPORT_SCATTER_GATHER_ELEMENT SgElement;
    ULONG SgIdx, CurrentTransferLen, BufferEnd, CurrentBuffer;
    ULONG TransferDataLeft = OhciTransfer->TransferParameters->TransferBufferLength - TransferedLen;

    DPRINT_OHCI("OHCI_MapTransferToTD: TransferedLen - %x\n", TransferedLen);

    for (SgIdx = 0; SgIdx < SGList->SgElementCount; SgIdx++)
    {
        SgElement = &SGList->SgElement[SgIdx];

        if (TransferedLen >= SgElement->SgOffset &&
            TransferedLen < SgElement->SgOffset + SgElement->SgTransferLength)
        {
            break;
        }
    }

    DPRINT_OHCI("OHCI_MapTransferToTD: SgIdx - %x, SgCount - %x\n",
                SgIdx,
                SGList->SgElementCount);

    ASSERT(SgIdx < SGList->SgElementCount);
    ASSERT(TransferedLen == SgElement->SgOffset);

    /* The buffer for a TD can be 0 to 8192 bytes long,
     * and can span within mo more than two 4k pages (see OpenHCI spec 3.3.2)
     * CurrentBuffer - the (physical) address of the first byte in the buffer
     * BufferEnd - the address of the last byte in the buffer. It can be on a different physical 4k page
     * when a controller will reach the end of a page from CurrentBuffer, it will take the first 20 bits
     * of the BufferEnd as a next address (OpenHCI spec, 4.3.1.3.1)
     */

    CurrentBuffer = SgElement->SgPhysicalAddress.LowPart;

    if (TransferDataLeft <= SgElement->SgTransferLength)
    {
        CurrentTransferLen = TransferDataLeft;
        BufferEnd = SgElement->SgPhysicalAddress.LowPart + CurrentTransferLen - 1;
    }
    else
    {
        PUSBPORT_SCATTER_GATHER_ELEMENT SgNextElement;
        ASSERT(SGList->SgElementCount - SgIdx > 1);

        SgNextElement = &SGList->SgElement[SgIdx + 1];

        TransferDataLeft -= SgElement->SgTransferLength;
        CurrentTransferLen = SgElement->SgTransferLength;

        if (TransferDataLeft <= SgNextElement->SgTransferLength)
        {
            CurrentTransferLen += TransferDataLeft;
            BufferEnd = SgNextElement->SgPhysicalAddress.LowPart + TransferDataLeft - 1;
        }
        else
        {
            CurrentTransferLen += SgNextElement->SgTransferLength;
            BufferEnd = SgNextElement->SgPhysicalAddress.LowPart + SgNextElement->SgTransferLength - 1;
        }
    }

    TD->HwTD.gTD.CurrentBuffer = CurrentBuffer;
    TD->HwTD.gTD.BufferEnd = BufferEnd;
    TD->TransferLen = CurrentTransferLen;

    return TransferedLen + CurrentTransferLen;
}

POHCI_HCD_TD
NTAPI
OHCI_AllocateTD(IN POHCI_EXTENSION OhciExtension,
                IN POHCI_ENDPOINT OhciEndpoint)
{
    POHCI_HCD_TD TD;

    DPRINT_OHCI("OHCI_AllocateTD: ... \n");

    TD = OhciEndpoint->FirstTD;

    while (TD->Flags & OHCI_HCD_TD_FLAG_ALLOCATED)
    {
        TD += 1;
    }

    TD->Flags |= OHCI_HCD_TD_FLAG_ALLOCATED;

    RtlSecureZeroMemory(&TD->HwTD, sizeof(TD->HwTD));

    return TD;
}

ULONG
NTAPI
OHCI_RemainTDs(IN POHCI_EXTENSION OhciExtension,
               IN POHCI_ENDPOINT OhciEndpoint)
{
    POHCI_HCD_TD TD;
    ULONG MaxTDs;
    ULONG RemainTDs;
    ULONG ix;

    DPRINT_OHCI("OHCI_RemainTDs: ... \n");

    MaxTDs = OhciEndpoint->MaxTransferDescriptors;
    TD = OhciEndpoint->FirstTD;

    RemainTDs = 0;

    for (ix = 0; ix < MaxTDs; ix++)
    {
        if (!(TD->Flags & OHCI_HCD_TD_FLAG_ALLOCATED))
            RemainTDs++;

        TD += 1;
    }

    return RemainTDs;
}

static
MPSTATUS
OHCI_ControlTransfer(IN POHCI_EXTENSION OhciExtension,
                     IN POHCI_ENDPOINT OhciEndpoint,
                     IN PUSBPORT_TRANSFER_PARAMETERS TransferParameters,
                     IN POHCI_TRANSFER OhciTransfer,
                     IN PUSBPORT_SCATTER_GATHER_LIST SGList)
{
    POHCI_HCD_TD SetupTD;
    POHCI_HCD_TD TD;
    POHCI_HCD_TD PrevTD;
    ULONG MaxTDs;
    ULONG TransferedLen;
    UCHAR DataToggle;

    DPRINT_OHCI("OHCI_ControlTransfer: Ext %p, Endpoint %p\n",
                OhciExtension,
                OhciEndpoint);

    MaxTDs = OHCI_RemainTDs(OhciExtension, OhciEndpoint);

    if ((SGList->SgElementCount + OHCI_NON_DATA_CONTROL_TDS) > MaxTDs)
        return MP_STATUS_FAILURE;

    /* Form a setup packet first */
    SetupTD = OhciEndpoint->HcdTailP;
    RtlSecureZeroMemory(&SetupTD->HwTD, sizeof(SetupTD->HwTD));

    SetupTD->Flags |= OHCI_HCD_TD_FLAG_PROCESSED;
    SetupTD->OhciTransfer = OhciTransfer;

    OhciTransfer->PendingTDs++;

    RtlCopyMemory(&SetupTD->HwTD.SetupPacket,
                  &TransferParameters->SetupPacket,
                  sizeof(SetupTD->HwTD.SetupPacket));

    SetupTD->HwTD.gTD.CurrentBuffer = SetupTD->PhysicalAddress + FIELD_OFFSET(OHCI_HCD_TD, HwTD.SetupPacket);
    SetupTD->HwTD.gTD.BufferEnd = SetupTD->PhysicalAddress + FIELD_OFFSET(OHCI_HCD_TD, HwTD.SetupPacket) +
                                  sizeof(USB_DEFAULT_PIPE_SETUP_PACKET) - 1;
    SetupTD->HwTD.gTD.Control.DelayInterrupt = OHCI_TD_INTERRUPT_NONE;
    SetupTD->HwTD.gTD.Control.ConditionCode = OHCI_TD_CONDITION_NOT_ACCESSED;
    SetupTD->HwTD.gTD.Control.DataToggle = OHCI_TD_DATA_TOGGLE_DATA0;

    PrevTD = SetupTD;

    /* Data packets follow a setup packet (if any) */
    TD = OHCI_AllocateTD(OhciExtension, OhciEndpoint);
    TD->Flags |= OHCI_HCD_TD_FLAG_PROCESSED;
    TD->OhciTransfer = OhciTransfer;

    PrevTD->HwTD.gTD.NextTD = TD->PhysicalAddress;
    PrevTD->NextTDVa = TD;

    /* The first data packet should use DATA1, subsequent ones use DATA0 (OpenHCI spec, 4.3.1.3.4) */
    DataToggle = OHCI_TD_DATA_TOGGLE_DATA1;
    TransferedLen = 0;

    while (TransferedLen < TransferParameters->TransferBufferLength)
    {
        OhciTransfer->PendingTDs++;

        if (TransferParameters->TransferFlags & USBD_TRANSFER_DIRECTION_IN)
            TD->HwTD.gTD.Control.DirectionPID = OHCI_TD_DIRECTION_PID_IN;
        else
            TD->HwTD.gTD.Control.DirectionPID = OHCI_TD_DIRECTION_PID_OUT;

        TD->HwTD.gTD.Control.DelayInterrupt = OHCI_TD_INTERRUPT_NONE;
        TD->HwTD.gTD.Control.ConditionCode = OHCI_TD_CONDITION_NOT_ACCESSED;
        TD->HwTD.gTD.Control.DataToggle = DataToggle;

        TransferedLen = OHCI_MapTransferToTD(OhciExtension,
                                             TransferedLen,
                                             OhciTransfer,
                                             TD,
                                             SGList);

        PrevTD = TD;

        TD = OHCI_AllocateTD(OhciExtension, OhciEndpoint);
        TD->HwTD.gTD.Control.DelayInterrupt = OHCI_TD_INTERRUPT_NONE;
        TD->Flags |= OHCI_HCD_TD_FLAG_PROCESSED;
        TD->OhciTransfer = OhciTransfer;

        PrevTD->HwTD.gTD.NextTD = TD->PhysicalAddress;
        PrevTD->NextTDVa = TD;

        DataToggle = OHCI_TD_DATA_TOGGLE_DATA0;
    }

    if (TransferParameters->TransferFlags & USBD_SHORT_TRANSFER_OK)
    {
        PrevTD->HwTD.gTD.Control.BufferRounding = TRUE;
        OhciTransfer->Flags |= OHCI_TRANSFER_FLAGS_SHORT_TRANSFER_OK;
    }

    /* After data packets, goes a status packet */

    TD->Flags |= OHCI_HCD_TD_FLAG_CONTROL_STATUS;
    TD->TransferLen = 0;

    if ((TransferParameters->TransferFlags & USBD_TRANSFER_DIRECTION_IN) != 0)
    {
        TD->HwTD.gTD.Control.BufferRounding = FALSE;
        TD->HwTD.gTD.Control.DirectionPID = OHCI_TD_DIRECTION_PID_OUT;
    }
    else
    {
        TD->HwTD.gTD.Control.BufferRounding = TRUE;
        TD->HwTD.gTD.Control.DirectionPID = OHCI_TD_DIRECTION_PID_IN;
    }

    /* OpenHCI spec, 4.3.1.3.4 */
    TD->HwTD.gTD.Control.DataToggle = OHCI_TD_DATA_TOGGLE_DATA1;
    TD->HwTD.gTD.Control.ConditionCode = OHCI_TD_CONDITION_NOT_ACCESSED;
    TD->HwTD.gTD.Control.DelayInterrupt = OHCI_TD_INTERRUPT_IMMEDIATE;

    OhciTransfer->PendingTDs++;
    OhciTransfer->ControlStatusTD = TD;

    PrevTD = TD;

    /* And the last descriptor, which is not used in the current transfer (OpenHCI spec, 4.6) */
    TD = OHCI_AllocateTD(OhciExtension, OhciEndpoint);

    PrevTD->HwTD.gTD.NextTD = TD->PhysicalAddress;
    PrevTD->NextTDVa = TD;

    TD->NextTDVa = NULL;
    /* TD->HwTD.gTD.NextTD = 0; */

    OhciTransfer->NextTD = TD;
    OhciEndpoint->HcdTailP = TD;

    OhciEndpoint->HcdED->HwED.TailPointer = TD->PhysicalAddress;

    OHCI_EnableList(OhciExtension, OhciEndpoint);

    return MP_STATUS_SUCCESS;
}

static
MPSTATUS
OHCI_BulkOrInterruptTransfer(IN POHCI_EXTENSION OhciExtension,
                             IN POHCI_ENDPOINT OhciEndpoint,
                             IN PUSBPORT_TRANSFER_PARAMETERS TransferParameters,
                             IN POHCI_TRANSFER OhciTransfer,
                             IN PUSBPORT_SCATTER_GATHER_LIST SGList)
{
    POHCI_HCD_TD TD;
    POHCI_HCD_TD PrevTD;
    ULONG TransferedLen;
    ULONG MaxTDs;

    DPRINT_OHCI("OHCI_BulkOrInterruptTransfer: ... \n");

    MaxTDs = OHCI_RemainTDs(OhciExtension, OhciEndpoint);

    if (SGList->SgElementCount > MaxTDs)
        return MP_STATUS_FAILURE;

    TD = OhciEndpoint->HcdTailP;

    TransferedLen = 0;

    do
    {
        TD->HwTD.gTD.Control.DelayInterrupt = OHCI_TD_INTERRUPT_NONE;
        TD->HwTD.gTD.Control.ConditionCode = OHCI_TD_CONDITION_NOT_ACCESSED;

        if (TransferParameters->TransferFlags & USBD_TRANSFER_DIRECTION_IN)
        {
            TD->HwTD.gTD.Control.BufferRounding = FALSE;
            TD->HwTD.gTD.Control.DirectionPID = OHCI_TD_DIRECTION_PID_IN;
        }
        else
        {
            TD->HwTD.gTD.Control.BufferRounding = TRUE;
            TD->HwTD.gTD.Control.DirectionPID = OHCI_TD_DIRECTION_PID_OUT;
        }

        TD->Flags |= OHCI_HCD_TD_FLAG_PROCESSED;
        TD->OhciTransfer = OhciTransfer;

        if (TransferParameters->TransferBufferLength)
        {
            TransferedLen = OHCI_MapTransferToTD(OhciExtension,
                                                 TransferedLen,
                                                 OhciTransfer,
                                                 TD,
                                                 SGList);
        }
        else
        {
            ASSERT(SGList->SgElementCount == 0);
            TD->TransferLen = 0;
        }

        PrevTD = TD;

        TD = OHCI_AllocateTD(OhciExtension, OhciEndpoint);
        OhciTransfer->PendingTDs++;

        PrevTD->HwTD.gTD.NextTD = TD->PhysicalAddress;
        PrevTD->NextTDVa = TD;
    }
    while (TransferedLen < TransferParameters->TransferBufferLength);

    if (TransferParameters->TransferFlags & USBD_SHORT_TRANSFER_OK)
    {
        PrevTD->HwTD.gTD.Control.BufferRounding = TRUE;
        OhciTransfer->Flags |= OHCI_TRANSFER_FLAGS_SHORT_TRANSFER_OK;
    }

    PrevTD->HwTD.gTD.Control.DelayInterrupt = OHCI_TD_INTERRUPT_IMMEDIATE;

    /* The last TD in a chain is not used in a transfer. The controller does not access it
     * so it will be used for chaining a next transfer to it (OpenHCI spec, 4.6)
     */
    /* TD->HwTD.gTD.NextTD = 0; */
    TD->NextTDVa = NULL;

    OhciTransfer->NextTD = TD;
    OhciEndpoint->HcdTailP = TD;

    OhciEndpoint->HcdED->HwED.TailPointer = TD->PhysicalAddress;

    OHCI_EnableList(OhciExtension, OhciEndpoint);

    return MP_STATUS_SUCCESS;
}

/**
 * @brief      Creates the transfer descriptor chain for the given transfer's buffer
 *             and attaches it to a given endpoint (for control, bulk or interrupt transfers)
 *
 * @param[in]  OhciExtension       The ohci extension
 * @param[in]  OhciEndpoint        The ohci endpoint
 * @param[in]  TransferParameters  The transfer parameters
 * @param[in]  OhciTransfer        The ohci transfer
 * @param[in]  SGList              The scatter/gather list
 *
 * @return     MP_STATUS_SUCCESS or MP_STATUS_FAILURE if there are not enough TDs left
 *             or wrong transfer type given
 */
MPSTATUS
NTAPI
OHCI_SubmitTransfer(IN PVOID ohciExtension,
                    IN PVOID ohciEndpoint,
                    IN PUSBPORT_TRANSFER_PARAMETERS TransferParameters,
                    IN PVOID ohciTransfer,
                    IN PUSBPORT_SCATTER_GATHER_LIST SGList)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_TRANSFER OhciTransfer = ohciTransfer;
    ULONG TransferType;

    DPRINT_OHCI("OHCI_SubmitTransfer: ... \n");

    RtlZeroMemory(OhciTransfer, sizeof(OHCI_TRANSFER));

    OhciTransfer->TransferParameters = TransferParameters;
    OhciTransfer->OhciEndpoint = OhciEndpoint;

    TransferType = OhciEndpoint->EndpointProperties.TransferType;

    if (TransferType == USBPORT_TRANSFER_TYPE_CONTROL)
    {
        return OHCI_ControlTransfer(OhciExtension,
                                    OhciEndpoint,
                                    TransferParameters,
                                    OhciTransfer,
                                    SGList);
    }

    if (TransferType == USBPORT_TRANSFER_TYPE_BULK ||
        TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
    {
        return OHCI_BulkOrInterruptTransfer(OhciExtension,
                                            OhciEndpoint,
                                            TransferParameters,
                                            OhciTransfer,
                                            SGList);
    }

    return MP_STATUS_FAILURE;
}

MPSTATUS
NTAPI
OHCI_SubmitIsoTransfer(IN PVOID ohciExtension,
                       IN PVOID ohciEndpoint,
                       IN PUSBPORT_TRANSFER_PARAMETERS TransferParameters,
                       IN PVOID ohciTransfer,
                       IN PVOID isoParameters)
{
    DPRINT1("OHCI_SubmitIsoTransfer: UNIMPLEMENTED. FIXME\n");
    return MP_STATUS_SUCCESS;
}

VOID
NTAPI
OHCI_ProcessDoneTD(IN POHCI_EXTENSION OhciExtension,
                   IN POHCI_HCD_TD TD,
                   IN BOOLEAN IsPortComplete)
{
    POHCI_TRANSFER OhciTransfer;
    POHCI_ENDPOINT OhciEndpoint;
    ULONG Buffer;
    ULONG BufferEnd;
    ULONG Length;

    DPRINT_OHCI("OHCI_ProcessDoneTD: ... \n");

    OhciTransfer = TD->OhciTransfer;
    OhciEndpoint = OhciTransfer->OhciEndpoint;

    OhciTransfer->PendingTDs--;

    Buffer = TD->HwTD.gTD.CurrentBuffer;
    BufferEnd = TD->HwTD.gTD.BufferEnd;

    if (TD->Flags & OHCI_HCD_TD_FLAG_NOT_ACCESSED)
    {
        TD->HwTD.gTD.Control.ConditionCode = OHCI_TD_CONDITION_NO_ERROR;
    }
    else
    {
        if (TD->HwTD.gTD.CurrentBuffer)
        {
            if (TD->TransferLen)
            {
                Length = (BufferEnd & (PAGE_SIZE - 1)) -
                         (Buffer & (PAGE_SIZE - 1));

                Length++;

                if (Buffer >> PAGE_SHIFT != BufferEnd >> PAGE_SHIFT)
                    Length += PAGE_SIZE;

                TD->TransferLen -= Length;
            }
        }

        if (TD->HwTD.gTD.Control.DirectionPID != OHCI_TD_DIRECTION_PID_SETUP)
            OhciTransfer->TransferLen += TD->TransferLen;

        if (TD->HwTD.gTD.Control.ConditionCode)
        {
            OhciTransfer->USBDStatus = USBD_STATUS_HALTED |
                                       TD->HwTD.gTD.Control.ConditionCode;
        }
    }

    TD->Flags = 0;
    TD->HwTD.gTD.NextTD = 0;
    TD->OhciTransfer = 0;

    TD->DoneLink.Flink = NULL;
    TD->DoneLink.Blink = NULL;

    if (IsPortComplete && (OhciTransfer->PendingTDs == 0))
    {
        RegPacket.UsbPortCompleteTransfer(OhciExtension,
                                          OhciEndpoint,
                                          OhciTransfer->TransferParameters,
                                          OhciTransfer->USBDStatus,
                                          OhciTransfer->TransferLen);
    }
}

VOID
NTAPI
OHCI_ProcessDoneIsoTD(IN POHCI_EXTENSION OhciExtension,
                      IN POHCI_HCD_TD TD,
                      IN BOOLEAN IsPortComplete)
{
    DPRINT1("OHCI_ProcessDoneIsoTD: UNIMPLEMENTED. FIXME\n");
}

/**
 * @brief      Aborts the transfer descriptor chain in a given endpoint
 *
 * @param[in]  ohciExtension   The ohci extension
 * @param[in]  ohciEndpoint    The ohci endpoint
 * @param[in]  ohciTransfer    The ohci transfer
 * @param[out] CompletedLength
 */
VOID
NTAPI
OHCI_AbortTransfer(IN PVOID ohciExtension,
                   IN PVOID ohciEndpoint,
                   IN PVOID ohciTransfer,
                   IN OUT PULONG CompletedLength)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_TRANSFER OhciTransfer = ohciTransfer;
    POHCI_HCD_ED ED = OhciEndpoint->HcdED;
    POHCI_HCD_TD TD, NextTD, LastTD;
    ULONG ix;
    BOOLEAN IsIsoEndpoint;
    BOOLEAN IsProcessed = FALSE;

    DPRINT("OHCI_AbortTransfer: ohciEndpoint - %p, ohciTransfer - %p\n",
           OhciEndpoint,
           OhciTransfer);

    IsIsoEndpoint = (OhciEndpoint->EndpointProperties.TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS);
    NextTD = RegPacket.UsbPortGetMappedVirtualAddress(ED->HwED.HeadPointer & OHCI_ED_HEAD_POINTER_MASK,
                                                      OhciExtension,
                                                      OhciEndpoint);

    if (NextTD->OhciTransfer == OhciTransfer)
    {
        LastTD = OhciTransfer->NextTD;

        /* Keeping the carry bit from previous pointer value */
        ED->HwED.HeadPointer = LastTD->PhysicalAddress |
                               (ED->HwED.HeadPointer & OHCI_ED_HEAD_POINTER_CARRY);

        OhciEndpoint->HcdHeadP = LastTD;

        for (ix = 0; ix < OhciEndpoint->MaxTransferDescriptors; ix++)
        {
            TD = &OhciEndpoint->FirstTD[ix];

            if (TD->OhciTransfer != OhciTransfer)
                continue;

            if (IsIsoEndpoint)
                OHCI_ProcessDoneIsoTD(OhciExtension, TD, FALSE);
            else
                OHCI_ProcessDoneTD(OhciExtension, TD, FALSE);
        }

        *CompletedLength = OhciTransfer->TransferLen;
        return;
    }

    if (NextTD == OhciEndpoint->HcdHeadP)
        IsProcessed = TRUE;

    for (TD = OhciEndpoint->HcdHeadP; TD != NextTD; TD = TD->NextTDVa)
    {
        if (TD->OhciTransfer != OhciTransfer)
            continue;

        if (OhciEndpoint->HcdHeadP == TD)
            OhciEndpoint->HcdHeadP = TD->NextTDVa;

        if (IsIsoEndpoint)
            OHCI_ProcessDoneIsoTD(OhciExtension, TD, FALSE);
        else
            OHCI_ProcessDoneTD(OhciExtension, TD, FALSE);

        IsProcessed = TRUE;
    }

    if (!IsProcessed)
    {
        for (TD = OhciEndpoint->HcdHeadP; TD->OhciTransfer != OhciTransfer; TD = TD->NextTDVa)
        {
            if (TD == OhciEndpoint->HcdTailP)
            {
                TD = NULL;
                break;
            }
            LastTD = TD;
        }

        for (; TD->OhciTransfer == OhciTransfer; TD = TD->NextTDVa)
        {
            if (TD == OhciEndpoint->HcdTailP)
                break;

            if (IsIsoEndpoint)
                OHCI_ProcessDoneIsoTD(OhciExtension, TD, FALSE);
            else
                OHCI_ProcessDoneTD(OhciExtension, TD, FALSE);
        }

        LastTD->OhciTransfer->NextTD = TD;

        LastTD->NextTDVa = TD;
        LastTD->HwTD.gTD.NextTD = TD->PhysicalAddress;
    }

    *CompletedLength = OhciTransfer->TransferLen;

    if (OhciTransfer->TransferLen)
    {
        DPRINT("OHCI_AbortTransfer: *CompletedLength - %x\n", *CompletedLength);
    }
}

ULONG
NTAPI
OHCI_GetEndpointState(IN PVOID ohciExtension,
                      IN PVOID ohciEndpoint)
{
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_HCD_ED ED;

    DPRINT_OHCI("OHCI_GetEndpointState: ... \n");

    ED = OhciEndpoint->HcdED;

    if (ED->Flags & OHCI_HCD_TD_FLAG_NOT_ACCESSED)
        return USBPORT_ENDPOINT_REMOVE;

    if (ED->HwED.EndpointControl.sKip)
        return USBPORT_ENDPOINT_PAUSED;

    return USBPORT_ENDPOINT_ACTIVE;
}

VOID
NTAPI
OHCI_RemoveEndpointFromSchedule(IN POHCI_ENDPOINT OhciEndpoint)
{
    POHCI_HCD_ED ED;
    POHCI_HCD_ED PreviousED;
    POHCI_STATIC_ED HeadED;

    DPRINT_OHCI("OHCI_RemoveEndpointFromSchedule \n");

    ED = OhciEndpoint->HcdED;
    HeadED = OhciEndpoint->HeadED;

    if (&HeadED->Link == ED->HcdEDLink.Blink)
    {
        if (HeadED->Type == OHCI_STATIC_ED_TYPE_CONTROL ||
            HeadED->Type == OHCI_STATIC_ED_TYPE_BULK)
        {
            WRITE_REGISTER_ULONG(HeadED->pNextED, ED->HwED.NextED);
        }
        else if (HeadED->Type == OHCI_STATIC_ED_TYPE_INTERRUPT)
        {
            *HeadED->pNextED = ED->HwED.NextED;
        }
        else
        {
            DPRINT1("OHCI_RemoveEndpointFromSchedule: Unknown HeadED->Type - %x\n",
                    HeadED->Type);
            DbgBreakPoint();
        }
    }
    else
    {
        PreviousED = CONTAINING_RECORD(ED->HcdEDLink.Blink,
                                       OHCI_HCD_ED,
                                       HcdEDLink);

        PreviousED->HwED.NextED = ED->HwED.NextED;
    }

    RemoveEntryList(&ED->HcdEDLink);

    OhciEndpoint->HeadED = NULL;
}

VOID
NTAPI
OHCI_SetEndpointState(IN PVOID ohciExtension,
                      IN PVOID ohciEndpoint,
                      IN ULONG EndpointState)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_HCD_ED ED;

    DPRINT_OHCI("OHCI_SetEndpointState: EndpointState - %x\n",
                EndpointState);

    ED = OhciEndpoint->HcdED;

    switch (EndpointState)
    {
        case USBPORT_ENDPOINT_PAUSED:
            ED->HwED.EndpointControl.sKip = 1;
            break;

        case USBPORT_ENDPOINT_ACTIVE:
            ED->HwED.EndpointControl.sKip = 0;
            OHCI_EnableList(OhciExtension, OhciEndpoint);
            break;

        case USBPORT_ENDPOINT_REMOVE:
            ED->HwED.EndpointControl.sKip = 1;
            ED->Flags |= OHCI_HCD_ED_FLAG_NOT_ACCESSED;
            OHCI_RemoveEndpointFromSchedule(OhciEndpoint);
            break;

        default:
            ASSERT(FALSE);
            break;
    }
}

VOID
NTAPI
OHCI_PollAsyncEndpoint(IN POHCI_EXTENSION OhciExtension,
                       IN POHCI_ENDPOINT OhciEndpoint)
{
    PUSBPORT_TRANSFER_PARAMETERS TransferParameters;
    POHCI_HCD_ED ED;
    ULONG_PTR NextTdPA;
    POHCI_HCD_TD NextTD;
    POHCI_HCD_TD TD;
    PLIST_ENTRY DoneList;
    POHCI_TRANSFER OhciTransfer;
    POHCI_HCD_TD ControlStatusTD;
    ULONG_PTR PhysicalAddress;
    ULONG TransferNumber;
    POHCI_TRANSFER transfer;
    UCHAR ConditionCode;
    BOOLEAN IsResetOnHalt = FALSE;

    //DPRINT_OHCI("OHCI_PollAsyncEndpoint: Endpoint - %p\n", OhciEndpoint);

    ED = OhciEndpoint->HcdED;
    NextTdPA = ED->HwED.HeadPointer & OHCI_ED_HEAD_POINTER_MASK;

    if (!NextTdPA)
    {
        OHCI_DumpHcdED(ED);
        DbgBreakPoint();
    }

    NextTD = RegPacket.UsbPortGetMappedVirtualAddress(NextTdPA,
                                                      OhciExtension,
                                                      OhciEndpoint);
    DPRINT_OHCI("NextTD - %p\n", NextTD);

    if ((ED->HwED.HeadPointer & OHCI_ED_HEAD_POINTER_HALT) == 0)
        goto ProcessListTDs;

    OHCI_DumpHcdED(ED);

    IsResetOnHalt = (ED->Flags & OHCI_HCD_ED_FLAG_RESET_ON_HALT) != 0;
    DPRINT1("PollAsyncEndpoint: IsResetOnHalt %x\n", IsResetOnHalt);

    for (TD = OhciEndpoint->HcdHeadP; ; TD = TD->NextTDVa)
    {
        if (!TD)
        {
            OHCI_DumpHcdED(ED);
            DbgBreakPoint();
        }

        if (TD == NextTD)
        {
            DPRINT("TD == NextTD - %p\n", TD);
            goto HandleDoneList;
        }

        OhciTransfer = TD->OhciTransfer;
        ConditionCode = TD->HwTD.gTD.Control.ConditionCode;

        DPRINT("TD - %p, ConditionCode - %X\n", TD, ConditionCode);
        OHCI_DumpHcdTD(TD);

        switch (ConditionCode)
        {
            case OHCI_TD_CONDITION_NO_ERROR:
                TD->Flags |= OHCI_HCD_TD_FLAG_DONE;
                InsertTailList(&OhciEndpoint->TDList, &TD->DoneLink);
                continue;

            case OHCI_TD_CONDITION_NOT_ACCESSED:
                TD->Flags |= (OHCI_HCD_TD_FLAG_DONE | OHCI_HCD_TD_FLAG_NOT_ACCESSED);
                InsertTailList(&OhciEndpoint->TDList, &TD->DoneLink);
                continue;

            case OHCI_TD_CONDITION_DATA_UNDERRUN:
                DPRINT1("DATA_UNDERRUN. Transfer->Flags - %X\n", OhciTransfer->Flags);

                if (OhciTransfer->Flags & OHCI_TRANSFER_FLAGS_SHORT_TRANSFER_OK)
                {
                    IsResetOnHalt = TRUE;
                    TD->HwTD.gTD.Control.ConditionCode = OHCI_TD_CONDITION_NO_ERROR;

                    ControlStatusTD = OhciTransfer->ControlStatusTD;

                    if ((TD->Flags & OHCI_HCD_TD_FLAG_CONTROL_STATUS) == 0 &&
                        ControlStatusTD)
                    {
                        PhysicalAddress = ControlStatusTD->PhysicalAddress;
                        PhysicalAddress |= (ED->HwED.HeadPointer &
                                            OHCI_ED_HEAD_POINTER_FLAGS_MASK);

                        ED->HwED.HeadPointer = PhysicalAddress;

                        NextTD = OhciTransfer->ControlStatusTD;
                        DPRINT("PhysicalAddress - %p, NextTD - %p\n", PhysicalAddress, NextTD);
                    }
                    else
                    {
                        TransferParameters = OhciTransfer->TransferParameters;

                        if (TransferParameters->IsTransferSplited)
                        {
                            TransferNumber = TransferParameters->TransferCounter;
                            transfer = OhciTransfer;

                            do
                            {
                                transfer = transfer->NextTD->OhciTransfer;
                                NextTD = transfer->NextTD;
                            }
                            while (transfer && TransferNumber ==
                                   transfer->TransferParameters->TransferCounter);

                            PhysicalAddress = NextTD->PhysicalAddress;
                            PhysicalAddress |= (ED->HwED.HeadPointer &
                                                OHCI_ED_HEAD_POINTER_FLAGS_MASK);

                            ED->HwED.HeadPointer = PhysicalAddress;
                            DPRINT("PhysicalAddress - %p, NextTD - %p\n", PhysicalAddress, NextTD);
                        }
                        else
                        {
                            PhysicalAddress = OhciTransfer->NextTD->PhysicalAddress;
                            PhysicalAddress |= (ED->HwED.HeadPointer &
                                                OHCI_ED_HEAD_POINTER_FLAGS_MASK);

                            ED->HwED.HeadPointer = PhysicalAddress;

                            NextTD = OhciTransfer->NextTD;
                            DPRINT("PhysicalAddress - %p, NextTD - %p\n", PhysicalAddress, NextTD);
                        }
                    }

                    TD->Flags |= OHCI_HCD_TD_FLAG_DONE;
                    InsertTailList(&OhciEndpoint->TDList, &TD->DoneLink);
                    continue;
                }

                /* fall through */

            default:
                TD->Flags |= OHCI_HCD_TD_FLAG_DONE;
                InsertTailList(&OhciEndpoint->TDList, &TD->DoneLink);

                ED->HwED.HeadPointer = OhciTransfer->NextTD->PhysicalAddress |
                                       (ED->HwED.HeadPointer &
                                        OHCI_ED_HEAD_POINTER_FLAGS_MASK);

                NextTD = OhciTransfer->NextTD;
                break;
        }
    }

ProcessListTDs:

    TD = OhciEndpoint->HcdHeadP;

    while (TD != NextTD)
    {
        OHCI_DumpHcdTD(TD);
        TD->Flags |= OHCI_HCD_TD_FLAG_DONE;
        InsertTailList(&OhciEndpoint->TDList, &TD->DoneLink);
        TD = TD->NextTDVa;
    }

HandleDoneList:

    TD = NextTD;
    OhciEndpoint->HcdHeadP = NextTD;

    DoneList = &OhciEndpoint->TDList;

    while (!IsListEmpty(DoneList))
    {
        TD = CONTAINING_RECORD(DoneList->Flink,
                               OHCI_HCD_TD,
                               DoneLink);

        RemoveHeadList(DoneList);

        if (TD->Flags & OHCI_HCD_TD_FLAG_DONE &&
            TD->Flags & OHCI_HCD_TD_FLAG_PROCESSED)
        {
            OHCI_ProcessDoneTD(OhciExtension, TD, TRUE);
        }
    }

    if (IsResetOnHalt)
    {
        ED->HwED.HeadPointer &= ~OHCI_ED_HEAD_POINTER_HALT;
        DPRINT("ED->HwED.HeadPointer - %p\n", ED->HwED.HeadPointer);
    }
}

VOID
NTAPI
OHCI_PollIsoEndpoint(IN POHCI_EXTENSION OhciExtension,
                     IN POHCI_ENDPOINT OhciEndpoint)
{
    DPRINT1("OHCI_PollAsyncEndpoint: UNIMPLEMENTED. FIXME \n");
    ASSERT(FALSE);
}

VOID
NTAPI
OHCI_PollEndpoint(IN PVOID ohciExtension,
                  IN PVOID ohciEndpoint)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    ULONG TransferType;

    DPRINT_OHCI("OHCI_PollEndpoint: OhciExtension - %p, Endpoint - %p\n",
                OhciExtension,
                OhciEndpoint);

    TransferType = OhciEndpoint->EndpointProperties.TransferType;

    if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
    {
        OHCI_PollIsoEndpoint(OhciExtension, OhciEndpoint);
        return;
    }

    if (TransferType == USBPORT_TRANSFER_TYPE_CONTROL ||
        TransferType == USBPORT_TRANSFER_TYPE_BULK ||
        TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
    {
        OHCI_PollAsyncEndpoint(OhciExtension, OhciEndpoint);
    }
}

VOID
NTAPI
OHCI_CheckController(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG HcControlReg;
    OHCI_REG_CONTROL HcControl;
    ULONG FmNumber;
    USHORT FmDiff;
    POHCI_HCCA HcHCCA;

    //DPRINT_OHCI("OHCI_CheckController: ...\n");

    OperationalRegs = OhciExtension->OperationalRegs;

    if (!OHCI_HardwarePresent(OhciExtension, TRUE))
        return;

    HcControlReg = (PULONG)&OperationalRegs->HcControl;
    HcControl.AsULONG = READ_REGISTER_ULONG(HcControlReg);

    if (HcControl.HostControllerFunctionalState != OHCI_HC_STATE_OPERATIONAL)
        return;

    FmNumber = READ_REGISTER_ULONG(&OperationalRegs->HcFmNumber);
    FmDiff = (USHORT)(FmNumber - OhciExtension->HcdFmNumber);

    if (FmNumber == 0 || FmDiff < 5)
        return;

    HcHCCA = &OhciExtension->HcResourcesVA->HcHCCA;
    OhciExtension->HcdFmNumber = FmNumber;

    if (HcHCCA->Pad1 == 0)
    {
        HcHCCA->Pad1 = 0xBAD1;
        return;
    }

    DPRINT1("OHCI_CheckController: HcHCCA->Pad1 - %x\n", HcHCCA->Pad1);

    if (HcHCCA->Pad1 == 0xBAD1)
    {
        HcHCCA->Pad1 = 0xBAD2;
    }
    else if (HcHCCA->Pad1 == 0xBAD2)
    {
        HcHCCA->Pad1 = 0xBAD3;

        RegPacket.UsbPortInvalidateController(OhciExtension,
                                              USBPORT_INVALIDATE_CONTROLLER_RESET);
    }
}

ULONG
NTAPI
OHCI_Get32BitFrameNumber(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_HCCA HcHCCA;
    ULONG fm;
    ULONG hp;

    HcHCCA = &OhciExtension->HcResourcesVA->HcHCCA;

    /* 5.4 FrameInterval Counter: Get32BitFrameNumber() */

    hp = OhciExtension->FrameHighPart;
    fm = HcHCCA->FrameNumber;

    DPRINT_OHCI("OHCI_Get32BitFrameNumber: hp - %lX, fm - %lX\n", hp, fm);

    return ((fm & 0x7FFF) | hp) + ((fm ^ hp) & 0x8000);
}

VOID
NTAPI
OHCI_InterruptNextSOF(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG InterruptEnableReg;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntEnable;

    DPRINT_OHCI("OHCI_InterruptNextSOF: OhciExtension - %p\n",
                OhciExtension);

    OperationalRegs = OhciExtension->OperationalRegs;
    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;

    /* Enable interrupt generation due to Start of Frame */
    IntEnable.AsULONG = 0;
    IntEnable.StartofFrame = 1;

    WRITE_REGISTER_ULONG(InterruptEnableReg, IntEnable.AsULONG);
}

VOID
NTAPI
OHCI_EnableInterrupts(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG InterruptEnableReg;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntEnable;

    DPRINT_OHCI("OHCI_EnableInterrupts: OhciExtension - %p\n",
                OhciExtension);

    OperationalRegs = OhciExtension->OperationalRegs;
    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;

    /*  Enable interrupt generation */
    IntEnable.AsULONG = 0;
    IntEnable.MasterInterruptEnable = 1;

    WRITE_REGISTER_ULONG(InterruptEnableReg, IntEnable.AsULONG);
}

VOID
NTAPI
OHCI_DisableInterrupts(IN PVOID ohciExtension)
{
    POHCI_EXTENSION  OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    PULONG InterruptDisableReg;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntDisable;

    DPRINT_OHCI("OHCI_DisableInterrupts\n");

    OperationalRegs = OhciExtension->OperationalRegs;
    InterruptDisableReg = (PULONG)&OperationalRegs->HcInterruptDisable;

    /*  Disable interrupt generation */
    IntDisable.AsULONG = 0;
    IntDisable.MasterInterruptEnable = 1;

    WRITE_REGISTER_ULONG(InterruptDisableReg, IntDisable.AsULONG);
}

VOID
NTAPI
OHCI_PollController(IN PVOID ohciExtension)
{
    DPRINT1("OHCI_PollController: UNIMPLEMENTED. FIXME\n");
}

VOID
NTAPI
OHCI_SetEndpointDataToggle(IN PVOID ohciExtension,
                           IN PVOID ohciEndpoint,
                           IN ULONG DataToggle)
{
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_HCD_ED ED;

    DPRINT_OHCI("OHCI_SetEndpointDataToggle: Endpoint - %p, DataToggle - %x\n",
                OhciEndpoint,
                DataToggle);

    ED = OhciEndpoint->HcdED;

    if (DataToggle)
        ED->HwED.HeadPointer |= OHCI_ED_HEAD_POINTER_CARRY;
    else
        ED->HwED.HeadPointer &= ~OHCI_ED_HEAD_POINTER_CARRY;
}

ULONG
NTAPI
OHCI_GetEndpointStatus(IN PVOID ohciExtension,
                       IN PVOID ohciEndpoint)
{
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_HCD_ED ED;
    ULONG EndpointStatus = USBPORT_ENDPOINT_RUN;

    DPRINT_OHCI("OHCI_GetEndpointStatus: ... \n");

    ED = OhciEndpoint->HcdED;

    if ((ED->HwED.HeadPointer & OHCI_ED_HEAD_POINTER_HALT) &&
        !(ED->Flags & OHCI_HCD_ED_FLAG_RESET_ON_HALT))
    {
        EndpointStatus = USBPORT_ENDPOINT_HALT;
    }

    return EndpointStatus;
}

VOID
NTAPI
OHCI_SetEndpointStatus(IN PVOID ohciExtension,
                       IN PVOID ohciEndpoint,
                       IN ULONG EndpointStatus)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_ENDPOINT OhciEndpoint = ohciEndpoint;
    POHCI_HCD_ED ED;

    DPRINT_OHCI("OHCI_SetEndpointStatus: Endpoint - %p, EndpointStatus - %lX\n",
                OhciEndpoint,
                EndpointStatus);

    if (EndpointStatus == USBPORT_ENDPOINT_RUN)
    {
        ED = OhciEndpoint->HcdED;
        ED->HwED.HeadPointer &= ~OHCI_ED_HEAD_POINTER_HALT;

        OHCI_EnableList(OhciExtension, OhciEndpoint);
    }
    else if (EndpointStatus == USBPORT_ENDPOINT_HALT)
    {
        ASSERT(FALSE);
    }
}

VOID
NTAPI
OHCI_ResetController(IN PVOID ohciExtension)
{
    POHCI_EXTENSION OhciExtension = ohciExtension;
    POHCI_OPERATIONAL_REGISTERS OperationalRegs;
    ULONG FrameNumber;
    PULONG ControlReg;
    PULONG CommandStatusReg;
    PULONG InterruptEnableReg;
    PULONG FmIntervalReg;
    PULONG RhStatusReg;
    PULONG PortStatusReg;
    OHCI_REG_CONTROL ControlBak;
    OHCI_REG_CONTROL Control;
    OHCI_REG_COMMAND_STATUS CommandStatus;
    OHCI_REG_INTERRUPT_ENABLE_DISABLE IntEnable;
    ULONG_PTR HCCA;
    ULONG_PTR ControlHeadED;
    ULONG_PTR BulkHeadED;
    OHCI_REG_FRAME_INTERVAL FrameInterval;
    ULONG_PTR PeriodicStart;
    ULONG_PTR LSThreshold;
    OHCI_REG_RH_STATUS RhStatus;
    OHCI_REG_RH_DESCRIPTORA RhDescriptorA;
    OHCI_REG_RH_PORT_STATUS PortStatus;
    ULONG NumPorts;
    ULONG ix;

    DPRINT("OHCI_ResetController: ... \n");

    OperationalRegs = OhciExtension->OperationalRegs;

    ControlReg = (PULONG)&OperationalRegs->HcControl;
    CommandStatusReg = (PULONG)&OperationalRegs->HcCommandStatus;
    InterruptEnableReg = (PULONG)&OperationalRegs->HcInterruptEnable;
    FmIntervalReg = (PULONG)&OperationalRegs->HcFmInterval;
    RhStatusReg = (PULONG)&OperationalRegs->HcRhStatus;

    /* Backup FrameNumber from HcHCCA */
    FrameNumber = OhciExtension->HcResourcesVA->HcHCCA.FrameNumber;

    /* Backup registers */
    ControlBak.AsULONG = READ_REGISTER_ULONG(ControlReg);
    HCCA = READ_REGISTER_ULONG(&OperationalRegs->HcHCCA);
    ControlHeadED = READ_REGISTER_ULONG(&OperationalRegs->HcControlHeadED);
    BulkHeadED = READ_REGISTER_ULONG(&OperationalRegs->HcBulkHeadED);
    FrameInterval.AsULONG = READ_REGISTER_ULONG(FmIntervalReg);
    PeriodicStart = READ_REGISTER_ULONG(&OperationalRegs->HcPeriodicStart);
    LSThreshold = READ_REGISTER_ULONG(&OperationalRegs->HcLSThreshold);

    /* Reset HostController */
    CommandStatus.AsULONG = 0;
    CommandStatus.HostControllerReset = 1;
    WRITE_REGISTER_ULONG(CommandStatusReg, CommandStatus.AsULONG);

    KeStallExecutionProcessor(10);

    /* Restore registers */
    WRITE_REGISTER_ULONG(&OperationalRegs->HcHCCA, HCCA);
    WRITE_REGISTER_ULONG(&OperationalRegs->HcControlHeadED, ControlHeadED);
    WRITE_REGISTER_ULONG(&OperationalRegs->HcBulkHeadED, BulkHeadED);

    /* Set OPERATIONAL state for HC */
    Control.AsULONG = 0;
    Control.HostControllerFunctionalState = OHCI_HC_STATE_OPERATIONAL;
    WRITE_REGISTER_ULONG(ControlReg, Control.AsULONG);

    /* Set Toggle bit for FmInterval register */
    FrameInterval.FrameIntervalToggle = 1;
    WRITE_REGISTER_ULONG(FmIntervalReg, FrameInterval.AsULONG);

    /* Restore registers */
    WRITE_REGISTER_ULONG(&OperationalRegs->HcFmNumber, FrameNumber);
    WRITE_REGISTER_ULONG(&OperationalRegs->HcPeriodicStart, PeriodicStart);
    WRITE_REGISTER_ULONG(&OperationalRegs->HcLSThreshold, LSThreshold);

    /* Setup RhStatus register */
    RhStatus.AsULONG = 0;
    RhStatus.SetRemoteWakeupEnable = 1;
    RhStatus.SetGlobalPower = 1;
    WRITE_REGISTER_ULONG(RhStatusReg, RhStatus.AsULONG);

    /* Setup RH PortStatus registers */
    RhDescriptorA = OHCI_ReadRhDescriptorA(OhciExtension);
    NumPorts = RhDescriptorA.NumberDownstreamPorts;

    PortStatus.AsULONG = 0;
    PortStatus.SetPortPower = 1;

    for (ix = 0; ix < NumPorts; ix++)
    {
        PortStatusReg = (PULONG)&OperationalRegs->HcRhPortStatus[ix];
        WRITE_REGISTER_ULONG(PortStatusReg, PortStatus.AsULONG);
    }

    /* Restore HcControl register */
    ControlBak.HostControllerFunctionalState = OHCI_HC_STATE_OPERATIONAL;
    WRITE_REGISTER_ULONG(ControlReg, ControlBak.AsULONG);

    /* Setup HcInterruptEnable register */
    IntEnable.AsULONG = 0xFFFFFFFF;
    IntEnable.Reserved1 = 0;
    WRITE_REGISTER_ULONG(InterruptEnableReg, IntEnable.AsULONG);
}

MPSTATUS
NTAPI
OHCI_StartSendOnePacket(IN PVOID ohciExtension,
                        IN PVOID PacketParameters,
                        IN PVOID Data,
                        IN PULONG pDataLength,
                        IN PVOID BufferVA,
                        IN PVOID BufferPA,
                        IN ULONG BufferLength,
                        IN USBD_STATUS * pUSBDStatus)
{
    DPRINT1("OHCI_StartSendOnePacket: UNIMPLEMENTED. FIXME\n");
    return MP_STATUS_SUCCESS;
}

MPSTATUS
NTAPI
OHCI_EndSendOnePacket(IN PVOID ohciExtension,
                      IN PVOID PacketParameters,
                      IN PVOID Data,
                      IN PULONG pDataLength,
                      IN PVOID BufferVA,
                      IN PVOID BufferPA,
                      IN ULONG BufferLength,
                      IN USBD_STATUS * pUSBDStatus)
{
    DPRINT1("OHCI_EndSendOnePacket: UNIMPLEMENTED. FIXME\n");
    return MP_STATUS_SUCCESS;
}

MPSTATUS
NTAPI
OHCI_PassThru(IN PVOID ohciExtension,
              IN PVOID passThruParameters,
              IN ULONG ParameterLength,
              IN PVOID pParameters)
{
    DPRINT1("OHCI_PassThru: UNIMPLEMENTED. FIXME\n");
    return MP_STATUS_SUCCESS;
}

VOID
NTAPI
OHCI_Unload(IN PDRIVER_OBJECT DriverObject)
{
#if DBG
    DPRINT1("OHCI_Unload: Not supported\n");
#endif
    return;
}

VOID
NTAPI
OHCI_FlushInterrupts(IN PVOID uhciExtension)
{
#if DBG
    DPRINT1("OHCI_FlushInterrupts: Not supported\n");
#endif
    return;
}

NTSTATUS
NTAPI
DriverEntry(IN PDRIVER_OBJECT DriverObject,
            IN PUNICODE_STRING RegistryPath)
{
    NTSTATUS Status;

    DPRINT_OHCI("DriverEntry: DriverObject - %p, RegistryPath - %wZ\n",
                DriverObject,
                RegistryPath);

    RtlZeroMemory(&RegPacket, sizeof(USBPORT_REGISTRATION_PACKET));

    RegPacket.MiniPortVersion = USB_MINIPORT_VERSION_OHCI;

    RegPacket.MiniPortFlags = USB_MINIPORT_FLAGS_INTERRUPT |
                              USB_MINIPORT_FLAGS_MEMORY_IO;

    RegPacket.MiniPortBusBandwidth = TOTAL_USB11_BUS_BANDWIDTH;

    RegPacket.MiniPortExtensionSize = sizeof(OHCI_EXTENSION);
    RegPacket.MiniPortEndpointSize = sizeof(OHCI_ENDPOINT);
    RegPacket.MiniPortTransferSize = sizeof(OHCI_TRANSFER);
    RegPacket.MiniPortResourcesSize = sizeof(OHCI_HC_RESOURCES);

    RegPacket.OpenEndpoint = OHCI_OpenEndpoint;
    RegPacket.ReopenEndpoint = OHCI_ReopenEndpoint;
    RegPacket.QueryEndpointRequirements = OHCI_QueryEndpointRequirements;
    RegPacket.CloseEndpoint = OHCI_CloseEndpoint;
    RegPacket.StartController = OHCI_StartController;
    RegPacket.StopController = OHCI_StopController;
    RegPacket.SuspendController = OHCI_SuspendController;
    RegPacket.ResumeController = OHCI_ResumeController;
    RegPacket.InterruptService = OHCI_InterruptService;
    RegPacket.InterruptDpc = OHCI_InterruptDpc;
    RegPacket.SubmitTransfer = OHCI_SubmitTransfer;
    RegPacket.SubmitIsoTransfer = OHCI_SubmitIsoTransfer;
    RegPacket.AbortTransfer = OHCI_AbortTransfer;
    RegPacket.GetEndpointState = OHCI_GetEndpointState;
    RegPacket.SetEndpointState = OHCI_SetEndpointState;
    RegPacket.PollEndpoint = OHCI_PollEndpoint;
    RegPacket.CheckController = OHCI_CheckController;
    RegPacket.Get32BitFrameNumber = OHCI_Get32BitFrameNumber;
    RegPacket.InterruptNextSOF = OHCI_InterruptNextSOF;
    RegPacket.EnableInterrupts = OHCI_EnableInterrupts;
    RegPacket.DisableInterrupts = OHCI_DisableInterrupts;
    RegPacket.PollController = OHCI_PollController;
    RegPacket.SetEndpointDataToggle = OHCI_SetEndpointDataToggle;
    RegPacket.GetEndpointStatus = OHCI_GetEndpointStatus;
    RegPacket.SetEndpointStatus = OHCI_SetEndpointStatus;
    RegPacket.ResetController = OHCI_ResetController;
    RegPacket.RH_GetRootHubData = OHCI_RH_GetRootHubData;
    RegPacket.RH_GetStatus = OHCI_RH_GetStatus;
    RegPacket.RH_GetPortStatus = OHCI_RH_GetPortStatus;
    RegPacket.RH_GetHubStatus = OHCI_RH_GetHubStatus;
    RegPacket.RH_SetFeaturePortReset = OHCI_RH_SetFeaturePortReset;
    RegPacket.RH_SetFeaturePortPower = OHCI_RH_SetFeaturePortPower;
    RegPacket.RH_SetFeaturePortEnable = OHCI_RH_SetFeaturePortEnable;
    RegPacket.RH_SetFeaturePortSuspend = OHCI_RH_SetFeaturePortSuspend;
    RegPacket.RH_ClearFeaturePortEnable = OHCI_RH_ClearFeaturePortEnable;
    RegPacket.RH_ClearFeaturePortPower = OHCI_RH_ClearFeaturePortPower;
    RegPacket.RH_ClearFeaturePortSuspend = OHCI_RH_ClearFeaturePortSuspend;
    RegPacket.RH_ClearFeaturePortEnableChange = OHCI_RH_ClearFeaturePortEnableChange;
    RegPacket.RH_ClearFeaturePortConnectChange = OHCI_RH_ClearFeaturePortConnectChange;
    RegPacket.RH_ClearFeaturePortResetChange = OHCI_RH_ClearFeaturePortResetChange;
    RegPacket.RH_ClearFeaturePortSuspendChange = OHCI_RH_ClearFeaturePortSuspendChange;
    RegPacket.RH_ClearFeaturePortOvercurrentChange = OHCI_RH_ClearFeaturePortOvercurrentChange;
    RegPacket.RH_DisableIrq = OHCI_RH_DisableIrq;
    RegPacket.RH_EnableIrq = OHCI_RH_EnableIrq;
    RegPacket.StartSendOnePacket = OHCI_StartSendOnePacket;
    RegPacket.EndSendOnePacket = OHCI_EndSendOnePacket;
    RegPacket.PassThru = OHCI_PassThru;
    RegPacket.FlushInterrupts = OHCI_FlushInterrupts;

    DriverObject->DriverUnload = OHCI_Unload;

    Status = USBPORT_RegisterUSBPortDriver(DriverObject,
                                           USB10_MINIPORT_INTERFACE_VERSION,
                                           &RegPacket);

    DPRINT_OHCI("DriverEntry: USBPORT_RegisterUSBPortDriver return Status - %x\n",
                Status);

    return Status;
}
