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

#include "usbport.h"

#define NDEBUG
#include <debug.h>

static const UCHAR CMASKS[USB2_MICROFRAMES] = {
  0x1C, 0x38, 0x70, 0xE0, 0xC1, 0x83, 0x07, 0x0E
};

BOOLEAN
NTAPI
USB2_AllocateCheck(IN OUT PULONG OutTimeUsed,
                   IN ULONG CalcBusTime,
                   IN ULONG LimitAllocation)
{
    ULONG BusTime;
    BOOLEAN Result = TRUE;

    BusTime = *OutTimeUsed + CalcBusTime;
    *OutTimeUsed += CalcBusTime;

    if (BusTime > LimitAllocation)
    {
        DPRINT("USB2_AllocateCheck: BusTime > LimitAllocation\n");
        Result = FALSE;
    }

    return Result;
}

USHORT
NTAPI
USB2_AddDataBitStuff(IN USHORT DataTime)
{
    return (DataTime + (DataTime / 16));
}

VOID
NTAPI
USB2_IncMicroFrame(OUT PUCHAR frame,
                   OUT PUCHAR uframe)
{
    ++*uframe;

    if (*uframe > (USB2_MICROFRAMES - 1))
    {
        *uframe = 0;
        *frame = (*frame + 1) & (USB2_FRAMES - 1);
    }
}

VOID
NTAPI
USB2_GetPrevMicroFrame(OUT PUCHAR frame,
                       OUT PUCHAR uframe)
{
    *uframe = USB2_MICROFRAMES - 1;

    if (*frame)
        --*frame;
    else
        *frame = USB2_FRAMES - 1;
}

BOOLEAN
NTAPI
USB2_CheckTtEndpointInsert(IN PUSB2_TT_ENDPOINT nextTtEndpoint,
                           IN PUSB2_TT_ENDPOINT TtEndpoint)
{
    ULONG TransferType;

    DPRINT("USB2_CheckTtEndpointInsert: nextTtEndpoint - %p, TtEndpoint - %p\n",
           nextTtEndpoint,
           TtEndpoint);

    ASSERT(TtEndpoint);

    if (TtEndpoint->CalcBusTime >= (USB2_FS_MAX_PERIODIC_ALLOCATION / 2))
    {
        DPRINT1("USB2_CheckTtEndpointInsert: Result - FALSE\n");
        return FALSE;
    }

    if (!nextTtEndpoint)
    {
        DPRINT("USB2_CheckTtEndpointInsert: Result - TRUE\n");
        return TRUE;
    }

    TransferType = TtEndpoint->TtEndpointParams.TransferType;

    if (nextTtEndpoint->ActualPeriod < TtEndpoint->ActualPeriod &&
        TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
    {
        DPRINT("USB2_CheckTtEndpointInsert: Result - TRUE\n");
        return TRUE;
    }

    if ((nextTtEndpoint->ActualPeriod <= TtEndpoint->ActualPeriod &&
        TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS) ||
        nextTtEndpoint == TtEndpoint)
    {
        DPRINT("USB2_CheckTtEndpointInsert: Result - TRUE\n");
        return TRUE;
    }

    DPRINT("USB2_CheckTtEndpointInsert: Result - FALSE\n");
    return FALSE;
}

ULONG
NTAPI
USB2_GetOverhead(IN PUSB2_TT_ENDPOINT TtEndpoint)
{
    ULONG TransferType;
    ULONG Direction;
    ULONG DeviceSpeed;
    ULONG Overhead;
    ULONG HostDelay;

    TransferType = TtEndpoint->TtEndpointParams.TransferType;
    Direction = TtEndpoint->TtEndpointParams.Direction;
    DeviceSpeed = TtEndpoint->TtEndpointParams.DeviceSpeed;

    HostDelay = TtEndpoint->Tt->HcExtension->HcDelayTime;

    if (DeviceSpeed == UsbHighSpeed)
    {
        if (Direction == USBPORT_TRANSFER_DIRECTION_OUT)
        {
            if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
                Overhead = HostDelay + USB2_HS_ISOCHRONOUS_OUT_OVERHEAD;
            else
                Overhead = HostDelay + USB2_HS_INTERRUPT_OUT_OVERHEAD;
        }
        else
        {
            if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
                Overhead = HostDelay + USB2_HS_ISOCHRONOUS_IN_OVERHEAD;
            else
                Overhead = HostDelay + USB2_HS_INTERRUPT_IN_OVERHEAD;
        }
    }
    else if (DeviceSpeed == UsbFullSpeed)
    {
        if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
            Overhead = HostDelay + USB2_FS_ISOCHRONOUS_OVERHEAD;
        else
            Overhead = HostDelay + USB2_FS_INTERRUPT_OVERHEAD;
    }
    else
    {
        Overhead = HostDelay + USB2_LS_INTERRUPT_OVERHEAD;
    }

    return Overhead;
}

VOID
NTAPI
USB2_GetHsOverhead(IN PUSB2_TT_ENDPOINT TtEndpoint,
                   IN PULONG OverheadSS,
                   IN PULONG OverheadCS)
{
    ULONG TransferType;
    ULONG Direction;
    ULONG HostDelay;

    TransferType = TtEndpoint->TtEndpointParams.TransferType;
    Direction = TtEndpoint->TtEndpointParams.Direction;

    HostDelay = TtEndpoint->Tt->HcExtension->HcDelayTime;

    if (Direction == USBPORT_TRANSFER_DIRECTION_OUT)
    {
        if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
        {
            *OverheadSS = HostDelay + USB2_HS_SS_ISOCHRONOUS_OUT_OVERHEAD;
            *OverheadCS = 0;
        }
        else
        {
            *OverheadSS = HostDelay + USB2_HS_SS_INTERRUPT_OUT_OVERHEAD;
            *OverheadCS = HostDelay + USB2_HS_CS_INTERRUPT_OUT_OVERHEAD;
        }
    }
    else
    {
        if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
        {
            *OverheadSS = HostDelay + USB2_HS_SS_ISOCHRONOUS_IN_OVERHEAD;
            *OverheadCS = HostDelay + USB2_HS_CS_ISOCHRONOUS_IN_OVERHEAD;
        }
        else
        {
            *OverheadSS = HostDelay + USB2_HS_SS_INTERRUPT_IN_OVERHEAD;
            *OverheadCS = HostDelay + USB2_HS_CS_INTERRUPT_IN_OVERHEAD;
        }
    }
}

ULONG
NTAPI
USB2_GetLastIsoTime(IN PUSB2_TT_ENDPOINT TtEndpoint,
                    IN ULONG Frame)
{
    PUSB2_TT_ENDPOINT nextTtEndpoint;
    ULONG Result;

    //DPRINT("USB2_GetLastIsoTime: TtEndpoint - %p, Frame - %X\n",
    //       TtEndpoint,
    //       Frame);

    nextTtEndpoint = TtEndpoint->Tt->FrameBudget[Frame].IsoEndpoint->NextTtEndpoint;

    if (nextTtEndpoint ||
        (nextTtEndpoint = TtEndpoint->Tt->FrameBudget[Frame].AltEndpoint) != NULL)
    {
        Result = nextTtEndpoint->StartTime + nextTtEndpoint->CalcBusTime;
    }
    else
    {
        Result = USB2_FS_SOF_TIME;
    }

    return Result;
}

ULONG
NTAPI
USB2_GetStartTime(IN PUSB2_TT_ENDPOINT nextTtEndpoint,
                  IN PUSB2_TT_ENDPOINT TtEndpoint,
                  IN PUSB2_TT_ENDPOINT prevTtEndpoint,
                  IN ULONG Frame)
{
    PUSB2_TT_ENDPOINT ttEndpoint;
    ULONG TransferType;

    DPRINT("USB2_GetStartTime: nextTtEndpoint - %p, TtEndpoint - %p, prevTtEndpoint - %p, Frame - %X\n",
           nextTtEndpoint,
           TtEndpoint,
           prevTtEndpoint,
           Frame);

    TransferType = TtEndpoint->TtEndpointParams.TransferType;

    if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
    {
        if (nextTtEndpoint)
            return nextTtEndpoint->StartTime + nextTtEndpoint->CalcBusTime;

        ttEndpoint = TtEndpoint->Tt->FrameBudget[Frame].AltEndpoint;

        if (ttEndpoint)
            return ttEndpoint->StartTime + ttEndpoint->CalcBusTime;
        else
            return USB2_FS_SOF_TIME;
    }
    else
    {
        ttEndpoint = prevTtEndpoint;

        if (ttEndpoint == TtEndpoint->Tt->FrameBudget[Frame].IntEndpoint)
            return USB2_GetLastIsoTime(TtEndpoint, Frame);
        else
            return ttEndpoint->StartTime + ttEndpoint->CalcBusTime;
    }
}

VOID
NTAPI
USB2_InitTtEndpoint(IN PUSB2_TT_ENDPOINT TtEndpoint,
                    IN UCHAR TransferType,
                    IN UCHAR Direction,
                    IN UCHAR DeviceSpeed,
                    IN USHORT Period,
                    IN USHORT MaxPacketSize,
                    IN PUSB2_TT Tt)
{
    TtEndpoint->TtEndpointParams.TransferType = TransferType;
    TtEndpoint->TtEndpointParams.Direction = Direction;
    TtEndpoint->TtEndpointParams.DeviceSpeed = DeviceSpeed;

    TtEndpoint->Period = Period;
    TtEndpoint->MaxPacketSize = MaxPacketSize;
    TtEndpoint->Tt = Tt;

    TtEndpoint->CalcBusTime = 0;
    TtEndpoint->StartTime = 0;
    TtEndpoint->ActualPeriod = 0;
    TtEndpoint->StartFrame = 0;
    TtEndpoint->StartMicroframe = 0;

    TtEndpoint->Nums.AsULONG = 0;
    TtEndpoint->NextTtEndpoint = NULL;
    TtEndpoint->Reserved2 = 0;
    TtEndpoint->PreviosPeriod = 0;
    TtEndpoint->IsPromoted = FALSE;
}

BOOLEAN
NTAPI
USB2_AllocateHS(IN PUSB2_TT_ENDPOINT TtEndpoint,
                IN LONG Frame)
{
    PUSB2_HC_EXTENSION HcExtension;
    PUSB2_TT Tt;
    ULONG TransferType;
    ULONG Direction;
    ULONG DataTime;
    ULONG DataSize;
    ULONG RemainDataTime;
    ULONG OverheadCS;
    ULONG OverheadSS;
    ULONG ix;
    USHORT PktSize;
    USHORT PktSizeBitStuff;
    UCHAR frame;
    UCHAR uframe;
    BOOL Result = TRUE;

    DPRINT("USB2_AllocateHS: TtEndpoint - %p, Frame - %X, StartFrame - %X\n",
           TtEndpoint,
           Frame,
           TtEndpoint->StartFrame);

    Tt = TtEndpoint->Tt;
    HcExtension = Tt->HcExtension;

    TransferType = TtEndpoint->TtEndpointParams.TransferType;
    Direction = TtEndpoint->TtEndpointParams.Direction;

    if (Frame == 0)
    {
        TtEndpoint->StartMicroframe =
        TtEndpoint->StartTime / USB2_FS_RAW_BYTES_IN_MICROFRAME - 1;

        DPRINT("USB2_AllocateHS: TtEndpoint->StartMicroframe - %X\n",
               TtEndpoint->StartMicroframe);
    }

    USB2_GetHsOverhead(TtEndpoint, &OverheadSS, &OverheadCS);

    if (TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
    {
        if (Frame == 0)
        {
            TtEndpoint->Nums.NumStarts = 1;

            if ((CHAR)TtEndpoint->StartMicroframe < (USB2_MICROFRAMES - 3))
                TtEndpoint->Nums.NumCompletes = 3;
            else
                TtEndpoint->Nums.NumCompletes = 2;
        }
    }
    else
    {
        if (Direction == USBPORT_TRANSFER_DIRECTION_OUT)
        {
            DPRINT("USB2_AllocateHS: ISO UNIMPLEMENTED\n");
            ASSERT(FALSE);
        }
        else
        {
            DPRINT("USB2_AllocateHS: ISO UNIMPLEMENTED\n");
            ASSERT(FALSE);
        }
    }

    frame = TtEndpoint->StartFrame + Frame;
    uframe = TtEndpoint->StartMicroframe;

    if (TtEndpoint->StartMicroframe == USB2_PREV_MICROFRAME)
        USB2_GetPrevMicroFrame(&frame, &uframe);

    for (ix = 0; ix < TtEndpoint->Nums.NumStarts; ix++)
    {
        if (!USB2_AllocateCheck(&HcExtension->TimeUsed[frame][uframe],
                                OverheadSS,
                                USB2_MAX_MICROFRAME_ALLOCATION))
        {
            Result = FALSE;
        }

        if (Tt->NumStartSplits[frame][uframe] >
            (USB2_MAX_FS_LS_TRANSACTIONS_IN_UFRAME - 1))
        {
            DPRINT1("USB2_AllocateHS: Num Start Splits - %X\n",
                    Tt->NumStartSplits[frame][uframe] + 1);

            ASSERT(FALSE);
            Result = FALSE;
        }

        ++Tt->NumStartSplits[frame][uframe];
        USB2_IncMicroFrame(&frame, &uframe);
    }

    frame = TtEndpoint->StartFrame + Frame;
    uframe = TtEndpoint->StartMicroframe + TtEndpoint->Nums.NumStarts + 1;

    for (ix = 0; ix < TtEndpoint->Nums.NumCompletes; ix++)
    {
        if (!USB2_AllocateCheck(&HcExtension->TimeUsed[frame][uframe],
                                OverheadCS,
                                USB2_MAX_MICROFRAME_ALLOCATION))
        {
            Result = FALSE;
        }

        USB2_IncMicroFrame(&frame, &uframe);
    }

    PktSize = TtEndpoint->MaxPacketSize;
    PktSizeBitStuff = USB2_AddDataBitStuff(PktSize);

    if (Direction == USBPORT_TRANSFER_DIRECTION_OUT)
    {
        frame = TtEndpoint->StartFrame + Frame;
        uframe = TtEndpoint->StartMicroframe;

        if (uframe == USB2_PREV_MICROFRAME)
            USB2_GetPrevMicroFrame(&frame, &uframe);

        DataTime = 0;

        for (ix = 0; ix < TtEndpoint->Nums.NumStarts; ix++)
        {
            DataSize = PktSizeBitStuff - DataTime;

            if (DataSize <= USB2_FS_RAW_BYTES_IN_MICROFRAME)
                DataTime = DataSize;
            else
                DataTime = USB2_FS_RAW_BYTES_IN_MICROFRAME;

            DPRINT("USB2_AllocateHS: ix - %X, frame - %X, uframe - %X, TimeUsed - %X\n",
                   ix,
                   frame,
                   uframe,
                   HcExtension->TimeUsed[frame][uframe]);

            if (!USB2_AllocateCheck(&HcExtension->TimeUsed[frame][uframe],
                                    DataTime,
                                    USB2_MAX_MICROFRAME_ALLOCATION))
            {
                Result = FALSE;
            }

            USB2_IncMicroFrame(&frame, &uframe);
            DataTime += USB2_FS_RAW_BYTES_IN_MICROFRAME;
        }
    }
    else
    {
        frame = TtEndpoint->StartFrame + Frame;
        uframe = TtEndpoint->StartMicroframe + TtEndpoint->Nums.NumStarts + 1;

        for (ix = 0; ix < TtEndpoint->Nums.NumCompletes; ix++)
        {
            if (Tt->TimeCS[frame][uframe] < USB2_FS_RAW_BYTES_IN_MICROFRAME)
            {
                RemainDataTime = USB2_FS_RAW_BYTES_IN_MICROFRAME -
                                 Tt->TimeCS[frame][uframe];

                if (RemainDataTime >= PktSizeBitStuff)
                {
                    DataTime = PktSizeBitStuff;
                }
                else if (RemainDataTime > 0)
                {
                    DataTime = RemainDataTime;
                }
                else
                {
                    DataTime = 0;
                }

                if (!USB2_AllocateCheck(&HcExtension->TimeUsed[frame][uframe],
                                        DataTime,
                                        USB2_MAX_MICROFRAME_ALLOCATION))
                {
                    Result = FALSE;
                }
            }

            if (PktSizeBitStuff < USB2_FS_RAW_BYTES_IN_MICROFRAME)
                Tt->TimeCS[frame][uframe] += PktSizeBitStuff;
            else
                Tt->TimeCS[frame][uframe] += USB2_FS_RAW_BYTES_IN_MICROFRAME;

            USB2_IncMicroFrame(&frame, &uframe);
        }
    }

    DPRINT("USB2_AllocateHS: Result - %X\n", Result);
    return Result;
}

VOID
NTAPI
USB2_DeallocateHS(IN PUSB2_TT_ENDPOINT TtEndpoint,
                  IN ULONG Frame)
{
    PUSB2_TT Tt;
    PUSB2_HC_EXTENSION HcExtension;
    ULONG OverheadCS;
    ULONG OverheadSS;
    ULONG Direction;
    ULONG ix;
    ULONG CurrentDataTime;
    ULONG RemainDataTime;
    ULONG DataTime;
    ULONG DataSize;
    USHORT PktSize;
    USHORT PktSizeBitStuff;
    UCHAR uframe;
    UCHAR frame;

    DPRINT("USB2_DeallocateHS: TtEndpoint - %p, Frame - %X\n",
           TtEndpoint,
           Frame);

    Tt = TtEndpoint->Tt;
    HcExtension = Tt->HcExtension;

    USB2_GetHsOverhead(TtEndpoint, &OverheadSS, &OverheadCS);

    frame = TtEndpoint->StartFrame + Frame;
    uframe = TtEndpoint->StartMicroframe;

    if (TtEndpoint->StartMicroframe == USB2_PREV_MICROFRAME)
        USB2_GetPrevMicroFrame(&frame, &uframe);

    for (ix = 0; ix < TtEndpoint->Nums.NumStarts; ix++)
    {
        HcExtension->TimeUsed[frame][uframe] -= OverheadSS;
        --Tt->NumStartSplits[frame][uframe];
        USB2_IncMicroFrame(&frame, &uframe);
    }

    frame = TtEndpoint->StartFrame + Frame;
    uframe = TtEndpoint->StartMicroframe + TtEndpoint->Nums.NumStarts + 1;

    for (ix = 0; ix < TtEndpoint->Nums.NumCompletes; ix++)
    {
        HcExtension->TimeUsed[frame][uframe] -= OverheadCS;
        USB2_IncMicroFrame(&frame, &uframe);
    }

    Direction = TtEndpoint->TtEndpointParams.Direction;
    PktSize = TtEndpoint->MaxPacketSize;
    PktSizeBitStuff = USB2_AddDataBitStuff(PktSize);

    if (Direction == USBPORT_TRANSFER_DIRECTION_OUT)
    {
        frame = TtEndpoint->StartFrame + Frame;
        uframe = TtEndpoint->StartMicroframe;

        if (TtEndpoint->StartMicroframe == USB2_PREV_MICROFRAME)
            USB2_GetPrevMicroFrame(&frame, &uframe);

        DataTime = 0;

        for (ix = 0; ix < TtEndpoint->Nums.NumStarts; ix++)
        {
            DataSize = PktSizeBitStuff - DataTime;

            if (DataSize <= USB2_FS_RAW_BYTES_IN_MICROFRAME)
                CurrentDataTime = PktSizeBitStuff - DataTime;
            else
                CurrentDataTime = USB2_FS_RAW_BYTES_IN_MICROFRAME;

            HcExtension->TimeUsed[frame][uframe] -= CurrentDataTime;
            USB2_IncMicroFrame(&frame, &uframe);
            DataTime += USB2_FS_RAW_BYTES_IN_MICROFRAME;
        }
    }
    else
    {
        frame = TtEndpoint->StartFrame + Frame;
        uframe = TtEndpoint->StartMicroframe + TtEndpoint->Nums.NumStarts + 1;

        for (ix = 0; ix < TtEndpoint->Nums.NumCompletes; ix++)
        {
            if (PktSizeBitStuff >= USB2_FS_RAW_BYTES_IN_MICROFRAME)
                CurrentDataTime = USB2_FS_RAW_BYTES_IN_MICROFRAME;
            else
                CurrentDataTime = PktSizeBitStuff;

            Tt->TimeCS[frame][uframe] -= CurrentDataTime;

            if (Tt->TimeCS[frame][uframe] < USB2_FS_RAW_BYTES_IN_MICROFRAME)
            {
                RemainDataTime = USB2_FS_RAW_BYTES_IN_MICROFRAME -
                                 Tt->TimeCS[frame][uframe];

                if (RemainDataTime >= PktSizeBitStuff)
                    RemainDataTime = PktSizeBitStuff;

                HcExtension->TimeUsed[frame][uframe] -= RemainDataTime;
            }

            USB2_IncMicroFrame(&frame, &uframe);
        }
    }

    return;
}

BOOLEAN
NTAPI
USB2_MoveTtEndpoint(IN PUSB2_TT_ENDPOINT TtEndpoint,
                    IN LONG BusTime,
                    IN PUSB2_REBALANCE Rebalance,
                    IN ULONG RebalanceListEntries,
                    OUT BOOLEAN * OutResult)
{
    ULONG EndBusTime;
    ULONG TransferType;
    ULONG Num;
    UCHAR ix;

    DPRINT("USB2_MoveTtEndpoint: TtEndpoint - %p, BusTime - %X\n",
           TtEndpoint,
           BusTime);

    *OutResult = TRUE;

    for (Num = 0; Rebalance->RebalanceEndpoint[Num]; Num++)
    {
        if (Rebalance->RebalanceEndpoint[Num] == TtEndpoint)
            break;
    }

    DPRINT("USB2_MoveTtEndpoint: Num - %X\n", Num);

    TransferType = TtEndpoint->TtEndpointParams.TransferType;

    if (Rebalance->RebalanceEndpoint[Num] &&
        TtEndpoint->TtEndpointParams.EndpointMoved == TRUE &&
        ((TransferType != USBPORT_TRANSFER_TYPE_INTERRUPT) || BusTime >= 0))
    {
        DPRINT("USB2_MoveTtEndpoint: result - FALSE\n");
        return FALSE;
    }

    for (ix = 0;
         (TtEndpoint->StartFrame + ix) < USB2_FRAMES;
         ix += TtEndpoint->ActualPeriod)
    {
        USB2_DeallocateHS(TtEndpoint, ix);
    }

    TtEndpoint->StartTime += BusTime;

    EndBusTime = TtEndpoint->StartTime + TtEndpoint->CalcBusTime;

    if (EndBusTime > USB2_FS_MAX_PERIODIC_ALLOCATION)
    {
        DPRINT("USB2_MoveTtEndpoint: EndBusTime is too large!\n");
        *OutResult = FALSE;
    }

    TtEndpoint->TtEndpointParams.EndpointMoved = TRUE;

    if (Rebalance->RebalanceEndpoint[Num] == NULL)
    {
        if (Num >= RebalanceListEntries)
        {
            DPRINT("USB2_MoveTtEndpoint: Too many changes!\n");
            *OutResult = FALSE;
        }
        else
        {
            Rebalance->RebalanceEndpoint[Num] = TtEndpoint;
            Rebalance->RebalanceEndpoint[Num + 1] = NULL;
        }
    }

    for (ix = 0;
         (TtEndpoint->StartFrame + ix) < USB2_FRAMES;
         ix += TtEndpoint->ActualPeriod)
    {
        if (!USB2_AllocateHS(TtEndpoint, ix))
        {
            DPRINT("USB2_MoveTtEndpoint: OutResult - FALSE\n");
            OutResult = FALSE;
        }
    }

    DPRINT("USB2_MoveTtEndpoint: result - TRUE\n");
    return TRUE;
}

BOOLEAN
NTAPI
USB2_CommonFrames(IN PUSB2_TT_ENDPOINT NextTtEndpoint,
                  IN PUSB2_TT_ENDPOINT TtEndpoint)
{
    UCHAR Frame;

    DPRINT("USB2_CommonFrames: \n");

    if (NextTtEndpoint->ActualPeriod == ENDPOINT_INTERRUPT_1ms ||
        TtEndpoint->ActualPeriod == ENDPOINT_INTERRUPT_1ms)
    {
        return TRUE;
    }

    if (NextTtEndpoint->ActualPeriod < TtEndpoint->ActualPeriod)
        Frame = TtEndpoint->StartFrame % TtEndpoint->ActualPeriod;
    else
        Frame = NextTtEndpoint->StartFrame % TtEndpoint->ActualPeriod;

    return (Frame == TtEndpoint->StartFrame);
}

VOID
NTAPI
USB2_ConvertFrame(IN UCHAR Frame,
                  IN UCHAR Microframe,
                  OUT PUCHAR HcFrame,
                  OUT PUCHAR HcMicroframe)
{
    DPRINT("USB2_ConvertFrame: Frame - %x, Microframe - %x\n",
           Frame,
           Microframe);

    if (Microframe == USB2_PREV_MICROFRAME)
    {
        *HcFrame = Frame;
        *HcMicroframe = 0;
    }

    if (Microframe <= (USB2_MICROFRAMES - 2))
    {
        *HcFrame = Frame;
        *HcMicroframe = Microframe + 1;
    }

    if (Microframe == (USB2_MICROFRAMES - 1))
    {
        *HcFrame = Frame + 1;
        *HcMicroframe = 0;
    }
}

UCHAR
NTAPI
USB2_GetSMASK(IN PUSB2_TT_ENDPOINT TtEndpoint)
{
    ULONG ix;
    UCHAR SMask = 0;
    UCHAR HcFrame;
    UCHAR HcMicroFrame;

    if (TtEndpoint->TtEndpointParams.DeviceSpeed == UsbHighSpeed)
    {
        SMask = (1 << TtEndpoint->StartMicroframe);
    }
    else
    {
        USB2_ConvertFrame(TtEndpoint->StartFrame,
                          TtEndpoint->StartMicroframe,
                          &HcFrame,
                          &HcMicroFrame);

        for (ix = 0; ix < TtEndpoint->Nums.NumStarts; ix++)
        {
            SMask |= (1 << HcMicroFrame);
            HcMicroFrame++;
        }
    }

    return SMask;
}

UCHAR
NTAPI
USB2_GetCMASK(IN PUSB2_TT_ENDPOINT TtEndpoint)
{
    ULONG NumCompletes;
    ULONG TransferType;
    ULONG DeviceSpeed;
    ULONG Direction;
    UCHAR Result;
    UCHAR MicroFrameCS;
    UCHAR HcFrame;
    UCHAR HcMicroFrame;
    UCHAR MaskCS = 0;

    TransferType = TtEndpoint->TtEndpointParams.TransferType;
    DeviceSpeed = TtEndpoint->TtEndpointParams.DeviceSpeed;
    Direction = TtEndpoint->TtEndpointParams.Direction;

    if (DeviceSpeed == UsbHighSpeed)
        return 0;

    if (TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
    {
        USB2_ConvertFrame(TtEndpoint->StartFrame,
                          TtEndpoint->StartMicroframe,
                          &HcFrame,
                          &HcMicroFrame);

        Result = CMASKS[HcMicroFrame];
    }
    else
    {
        if (Direction == USBPORT_TRANSFER_DIRECTION_OUT)
            return 0;

        USB2_ConvertFrame(TtEndpoint->StartFrame,
                          TtEndpoint->StartMicroframe,
                          &HcFrame,
                          &HcMicroFrame);

        NumCompletes = TtEndpoint->Nums.NumCompletes;

        for (MicroFrameCS = HcMicroFrame + 2;
             MicroFrameCS < USB2_MICROFRAMES;
             MicroFrameCS++)
        {
            MaskCS |= (1 << MicroFrameCS);
            NumCompletes--;

            if (!NumCompletes)
                return MaskCS;
        }

        for (; NumCompletes; NumCompletes--)
        {
            MaskCS |= (1 << (MicroFrameCS - USB2_MICROFRAMES));
        }

        Result = MaskCS;
    }

    return Result;
}

VOID
NTAPI
USB2_RebalanceEndpoint(IN PDEVICE_OBJECT FdoDevice,
                       IN PLIST_ENTRY List)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PUSBPORT_REGISTRATION_PACKET Packet;
    PUSBPORT_ENDPOINT Endpoint;
    PLIST_ENTRY Entry;
    ULONG AllocedBusTime;
    ULONG EndpointBandwidth;
    ULONG Factor;
    ULONG ScheduleOffset;
    ULONG Bandwidth;
    ULONG n;
    ULONG ix;
    KIRQL OldIrql;
    UCHAR NewPeriod;
    UCHAR SMask;
    UCHAR CMask;

    FdoExtension = FdoDevice->DeviceExtension;
    Packet = &FdoExtension->MiniPortInterface->Packet;

    while (!IsListEmpty(List))
    {
        Entry = RemoveHeadList(List);

        Endpoint = CONTAINING_RECORD(Entry,
                                     USBPORT_ENDPOINT,
                                     RebalanceLink.Flink);

        DPRINT("USB2_RebalanceEndpoint: Endpoint - %p\n", Endpoint);

        Endpoint->RebalanceLink.Flink = NULL;
        Endpoint->RebalanceLink.Blink = NULL;

        KeAcquireSpinLock(&Endpoint->EndpointSpinLock,
                          &Endpoint->EndpointOldIrql);

        SMask = USB2_GetSMASK(Endpoint->TtEndpoint);
        CMask = USB2_GetCMASK(Endpoint->TtEndpoint);

        ScheduleOffset = Endpoint->TtEndpoint->StartFrame;
        NewPeriod = Endpoint->TtEndpoint->ActualPeriod;

        AllocedBusTime = Endpoint->TtEndpoint->CalcBusTime;
        EndpointBandwidth = USB2_MICROFRAMES * AllocedBusTime;

        Endpoint->EndpointProperties.InterruptScheduleMask = SMask;
        Endpoint->EndpointProperties.SplitCompletionMask = CMask;

        if (Endpoint->EndpointProperties.Period != NewPeriod)
        {
            ASSERT(Endpoint->EndpointProperties.Period);
            Factor = USB2_FRAMES / Endpoint->EndpointProperties.Period;

            for (ix = 0; ix < Factor; ix++)
            {
                Bandwidth = Endpoint->EndpointProperties.UsbBandwidth;
                n = Factor * Endpoint->EndpointProperties.ScheduleOffset;
                Endpoint->TtExtension->Bandwidth[n + ix] += Bandwidth;
            }

            Endpoint->EndpointProperties.Period = NewPeriod;
            Endpoint->EndpointProperties.ScheduleOffset = ScheduleOffset;
            Endpoint->EndpointProperties.UsbBandwidth = EndpointBandwidth;

            ASSERT(NewPeriod);
            Factor = USB2_FRAMES / NewPeriod;

            for (ix = 0; ix < Factor; ix++)
            {
                n = Factor * ScheduleOffset;
                Endpoint->TtExtension->Bandwidth[n + ix] += EndpointBandwidth;
            }
        }

        KeAcquireSpinLock(&FdoExtension->MiniportSpinLock, &OldIrql);

        Packet->RebalanceEndpoint(FdoExtension->MiniPortExt,
                                  &Endpoint->EndpointProperties,
                                  (PVOID)((ULONG_PTR)Endpoint + sizeof(USBPORT_ENDPOINT)));

        KeReleaseSpinLock(&FdoExtension->MiniportSpinLock, OldIrql);

        KeReleaseSpinLock(&Endpoint->EndpointSpinLock,
                          Endpoint->EndpointOldIrql);
    }
}

VOID
NTAPI
USB2_Rebalance(IN PDEVICE_OBJECT FdoDevice,
               IN PLIST_ENTRY RebalanceList)
{
    PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties;
    PUSBPORT_ENDPOINT Endpoint;
    PLIST_ENTRY Entry;
    LIST_ENTRY BalanceListInt1;
    LIST_ENTRY BalanceListInt2;
    ULONG TransferType;
    ULONG ScheduleOffset;
    UCHAR SMask;
    UCHAR CMask;
    UCHAR ActualPeriod;

    DPRINT("USB2_Rebalance: FdoDevice - %p, RebalanceList - %p\n",
           FdoDevice,
           RebalanceList);

    InitializeListHead(&BalanceListInt1);
    InitializeListHead(&BalanceListInt2);

    while (!IsListEmpty(RebalanceList))
    {
        Entry = RebalanceList->Flink;

        Endpoint = CONTAINING_RECORD(Entry,
                                     USBPORT_ENDPOINT,
                                     RebalanceLink.Flink);

        DPRINT("USBPORT_Rebalance: Entry - %p, Endpoint - %p\n",
               Entry,
               Endpoint);

        RemoveHeadList(RebalanceList);
        Entry->Flink = NULL;
        Entry->Blink = NULL;

        SMask = USB2_GetSMASK(Endpoint->TtEndpoint);
        CMask = USB2_GetCMASK(Endpoint->TtEndpoint);

        ScheduleOffset = Endpoint->TtEndpoint->StartFrame;
        ActualPeriod = Endpoint->TtEndpoint->ActualPeriod;

        EndpointProperties = &Endpoint->EndpointProperties;
        TransferType = EndpointProperties->TransferType;

        switch (TransferType)
        {
            case USBPORT_TRANSFER_TYPE_ISOCHRONOUS:
                DPRINT("USBPORT_Rebalance: USBPORT_TRANSFER_TYPE_ISOCHRONOUS. FIXME\n");
                ASSERT(FALSE);
                break;

            case USBPORT_TRANSFER_TYPE_INTERRUPT:
                if (SMask != EndpointProperties->InterruptScheduleMask ||
                    CMask != EndpointProperties->SplitCompletionMask ||
                    ScheduleOffset != EndpointProperties->ScheduleOffset ||
                    ActualPeriod != EndpointProperties->Period)
                {
                    if (ActualPeriod == EndpointProperties->Period &&
                        ScheduleOffset == EndpointProperties->ScheduleOffset)
                    {
                        InsertTailList(&BalanceListInt1, Entry);
                    }
                    else
                    {
                        InsertTailList(&BalanceListInt2, Entry);
                    }
                }
                break;

            default:
                ASSERT(FALSE);
                break;
        }
    }

    USB2_RebalanceEndpoint(FdoDevice, &BalanceListInt2);
    USB2_RebalanceEndpoint(FdoDevice, &BalanceListInt1);
    //USB2_RebalanceEndpoint(FdoDevice, &BalanceListIso);
}

BOOLEAN
NTAPI
USB2_DeallocateEndpointBudget(IN PUSB2_TT_ENDPOINT TtEndpoint,
                              IN PUSB2_REBALANCE Rebalance,
                              IN PULONG RebalanceListEntries,
                              IN ULONG MaxFrames)
{
    PUSB2_TT Tt;
    PUSB2_HC_EXTENSION HcExtension;
    ULONG Speed;
    ULONG TransferType;
    ULONG Frame;
    ULONG StartMicroframe;
    ULONG ix;
    PUSB2_TT_ENDPOINT endpoint;
    PUSB2_TT_ENDPOINT nextEndpoint;
    PUSB2_TT_ENDPOINT lastEndpoint;
    PUSB2_TT_ENDPOINT tmpEndpoint;
    ULONG endTime;
    ULONG maxEndTime;
    ULONG lastEndTime;
    ULONG Factor;
    ULONG jx;
    UCHAR frame;
    UCHAR uframe;
    USHORT Period;
    BOOLEAN IsMoved = FALSE;

    DPRINT("USB2_DeallocateEndpointBudget: TtEndpoint - %p, MaxFrames - %X, CalcBusTime - %X\n",
           TtEndpoint,
           MaxFrames,
           TtEndpoint->CalcBusTime);

    if (TtEndpoint->CalcBusTime == 0)
    {
        DPRINT("USB2_DeallocateEndpointBudget: TtEndpoint not allocated!\n");
        return FALSE;
    }

    Tt = TtEndpoint->Tt;
    HcExtension = Tt->HcExtension;

    Speed = TtEndpoint->TtEndpointParams.DeviceSpeed;
    DPRINT("USB2_DeallocateEndpointBudget: DeviceSpeed - %X\n", Speed);

    StartMicroframe = TtEndpoint->StartFrame * USB2_MICROFRAMES +
                      TtEndpoint->StartMicroframe;

    if (Speed == UsbHighSpeed)
    {
        for (ix = StartMicroframe;
             ix < USB2_MAX_MICROFRAMES;
             ix += TtEndpoint->ActualPeriod)
        {
            frame = ix / USB2_MICROFRAMES;
            uframe = ix % (USB2_MICROFRAMES - 1);

            HcExtension->TimeUsed[frame][uframe] -= TtEndpoint->CalcBusTime;
        }

        TtEndpoint->CalcBusTime = 0;

        DPRINT("USB2_DeallocateEndpointBudget: return TRUE\n");
        return TRUE;
    }

    /* Speed != UsbHighSpeed (FS/LS) */

    TransferType = TtEndpoint->TtEndpointParams.TransferType;

    for (ix = MaxFrames, Frame = (MaxFrames - 1) - TtEndpoint->StartFrame;
         ix > 0;
         ix--, Frame--)
    {
        frame = TtEndpoint->StartFrame + Frame;

        DPRINT("USB2_DeallocateEndpointBudget: frame - %X, Frame - %X, StartFrame - %X\n",
               frame,
               Frame,
               TtEndpoint->StartFrame);

        if ((Frame % TtEndpoint->ActualPeriod) == 0)
        {
            USB2_DeallocateHS(TtEndpoint, Frame);
            Tt->FrameBudget[frame].TimeUsed -= TtEndpoint->CalcBusTime;
        }

        if (TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
            endpoint = Tt->FrameBudget[frame].IntEndpoint;
        else
            endpoint = Tt->FrameBudget[frame].IsoEndpoint;

        nextEndpoint = endpoint->NextTtEndpoint;

        DPRINT("USB2_DeallocateEndpointBudget: TtEndpoint - %p, nextEndpoint - %p\n",
               TtEndpoint,
               nextEndpoint);

        if (TtEndpoint->CalcBusTime > (USB2_FS_MAX_PERIODIC_ALLOCATION / 2))
        {
            while (nextEndpoint)
            {
                endpoint = nextEndpoint;
                nextEndpoint = nextEndpoint->NextTtEndpoint;
            }

            nextEndpoint = TtEndpoint;

            DPRINT("USB2_DeallocateEndpointBudget: endpoint - %p, nextEndpoint - %p\n",
                   endpoint,
                   nextEndpoint);
        }
        else
        {
            while (nextEndpoint &&
                   !USB2_CheckTtEndpointInsert(nextEndpoint, TtEndpoint))
            {
                endpoint = nextEndpoint;
                nextEndpoint = nextEndpoint->NextTtEndpoint;
            }

            if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS &&
                nextEndpoint)
            {
                DPRINT1("USB2_DeallocateEndpointBudget: Iso Ep UNIMPLEMENTED. FIXME\n");
                ASSERT(FALSE);
            }

            DPRINT("USB2_DeallocateEndpointBudget: endpoint - %p, nextEndpoint - %p\n",
                   endpoint,
                   nextEndpoint);
        }

        if ((Frame % TtEndpoint->ActualPeriod) == 0)
        {
            if (TtEndpoint->CalcBusTime > (USB2_FS_MAX_PERIODIC_ALLOCATION / 2))
            {
                Tt->FrameBudget[frame].AltEndpoint = NULL;
            }
            else if (nextEndpoint)
            {
                nextEndpoint = nextEndpoint->NextTtEndpoint;
                endpoint->NextTtEndpoint = nextEndpoint;

                DPRINT("USB2_DeallocateEndpointBudget: endpoint - %p, nextEndpoint - %p\n",
                       endpoint,
                       nextEndpoint);
            }
        }

        if (TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT)
        {
            if (endpoint == Tt->FrameBudget[frame].IntEndpoint)
            {
                if (Tt->FrameBudget[frame].IsoEndpoint->NextTtEndpoint)
                {
                    endpoint = Tt->FrameBudget[frame].IsoEndpoint->NextTtEndpoint;
                }
                else if (Tt->FrameBudget[frame].AltEndpoint)
                {
                    endpoint = Tt->FrameBudget[frame].AltEndpoint;
                }
            }
        }
        else
        {
            DPRINT1("USB2_DeallocateEndpointBudget: Iso Ep UNIMPLEMENTED. FIXME\n");
            ASSERT(FALSE);
        }

        Period = TtEndpoint->ActualPeriod;

        for (;
             nextEndpoint;
             endpoint = nextEndpoint,
             nextEndpoint = nextEndpoint->NextTtEndpoint)
        {
            DPRINT("USB2_DeallocateEndpointBudget: endpoint - %p, nextEndpoint - %p\n",
                   endpoint,
                   nextEndpoint);

            endTime = endpoint->StartTime + endpoint->CalcBusTime;
            maxEndTime = endTime;

            if (Period > nextEndpoint->ActualPeriod ||
                TtEndpoint->StartFrame != nextEndpoint->StartFrame)
            {
                if (USB2_CommonFrames(nextEndpoint, TtEndpoint))
                    Factor = Period / nextEndpoint->ActualPeriod;
                else
                    Factor = USB2_FRAMES / nextEndpoint->ActualPeriod;

                maxEndTime = endTime;

                for (jx = 0, frame = nextEndpoint->StartFrame;
                     jx < Factor;
                     jx++, frame += nextEndpoint->ActualPeriod)
                {
                    if (nextEndpoint->StartFrame != TtEndpoint->StartFrame)
                    {
                        lastEndpoint = Tt->FrameBudget[frame].IntEndpoint;

                        if (Tt->FrameBudget[frame].IsoEndpoint->NextTtEndpoint)
                        {
                            lastEndpoint = Tt->FrameBudget[frame].IsoEndpoint->NextTtEndpoint;
                        }
                        else if (Tt->FrameBudget[frame].AltEndpoint)
                        {
                            lastEndpoint = Tt->FrameBudget[frame].AltEndpoint;
                        }

                        for (tmpEndpoint = Tt->FrameBudget[frame].IntEndpoint->NextTtEndpoint;
                             tmpEndpoint && tmpEndpoint != nextEndpoint;
                             tmpEndpoint = tmpEndpoint->NextTtEndpoint)
                        {
                            lastEndpoint = tmpEndpoint;
                        }

                        lastEndTime = lastEndpoint->StartTime + lastEndpoint->CalcBusTime;

                        if (endTime < (lastEndTime - 1))
                        {
                            maxEndTime = lastEndTime;
                            endTime = maxEndTime;

                            if (nextEndpoint->StartTime == maxEndTime)
                                break;
                        }
                        else
                        {
                            maxEndTime = endTime;
                        }
                    }
                }
            }

            if (maxEndTime >= nextEndpoint->StartTime)
                break;

            if (!USB2_MoveTtEndpoint(nextEndpoint,
                                     maxEndTime - nextEndpoint->StartTime,
                                     Rebalance,
                                     *RebalanceListEntries,
                                     &IsMoved))
            {
                if (!IsMoved)
                {
                    DPRINT("USB2_DeallocateEndpointBudget: Not moved!\n");
                }

                break;
            }

            if (Period > nextEndpoint->ActualPeriod)
                Period = nextEndpoint->ActualPeriod;
        }
    }

    TtEndpoint->CalcBusTime = 0;

    DPRINT("USB2_DeallocateEndpointBudget: return TRUE\n");
    return TRUE;
}

BOOLEAN
NTAPI
USB2_AllocateTimeForEndpoint(IN PUSB2_TT_ENDPOINT TtEndpoint,
                             IN PUSB2_REBALANCE Rebalance,
                             IN PULONG RebalanceListEntries)
{
    PUSB2_TT Tt;
    PUSB2_HC_EXTENSION HcExtension;
    ULONG Speed;
    ULONG TimeUsed;
    ULONG MinTimeUsed;
    ULONG ix;
    ULONG frame;
    ULONG uframe;
    ULONG Microframe;
    ULONG TransferType;
    ULONG Overhead;
    ULONG LatestStart;
    PUSB2_TT_ENDPOINT prevEndpoint;
    PUSB2_TT_ENDPOINT nextEndpoint;
    PUSB2_TT_ENDPOINT IntEndpoint;
    ULONG StartTime;
    ULONG calcBusTime;
    BOOLEAN Result = TRUE;

    DPRINT("USB2_AllocateTimeForEndpoint: TtEndpoint - %p\n", TtEndpoint);

    Tt = TtEndpoint->Tt;
    HcExtension = Tt->HcExtension;

    TtEndpoint->Nums.NumStarts = 0;
    TtEndpoint->Nums.NumCompletes = 0;

    TtEndpoint->StartFrame = 0;
    TtEndpoint->StartMicroframe = 0;

    if (TtEndpoint->CalcBusTime)
    {
        DPRINT("USB2_AllocateTimeForEndpoint: TtEndpoint already allocated!\n");
        return FALSE;
    }

    Speed = TtEndpoint->TtEndpointParams.DeviceSpeed;

    if (Speed == UsbHighSpeed)
    {
        if (TtEndpoint->Period > USB2_MAX_MICROFRAMES)
            TtEndpoint->ActualPeriod = USB2_MAX_MICROFRAMES;
        else
            TtEndpoint->ActualPeriod = TtEndpoint->Period;

        MinTimeUsed = HcExtension->TimeUsed[0][0];

        for (ix = 1; ix < TtEndpoint->ActualPeriod; ix++)
        {
            frame = ix / USB2_MICROFRAMES;
            uframe = ix % (USB2_MICROFRAMES - 1);

            TimeUsed = HcExtension->TimeUsed[frame][uframe];

            if (TimeUsed < MinTimeUsed)
            {
                MinTimeUsed = TimeUsed;
                TtEndpoint->StartFrame = frame;
                TtEndpoint->StartMicroframe = uframe;
            }
        }

        TtEndpoint->CalcBusTime = USB2_GetOverhead(TtEndpoint) +
                                  USB2_AddDataBitStuff(TtEndpoint->MaxPacketSize);

        DPRINT("USB2_AllocateTimeForEndpoint: StartFrame - %X, StartMicroframe - %X, CalcBusTime - %X\n",
               TtEndpoint->StartFrame,
               TtEndpoint->StartMicroframe,
               TtEndpoint->CalcBusTime);

        Microframe = TtEndpoint->StartFrame * USB2_MICROFRAMES +
                     TtEndpoint->StartMicroframe;

        if (Microframe >= USB2_MAX_MICROFRAMES)
        {
            DPRINT("USB2_AllocateTimeForEndpoint: Microframe >= 256. Result - TRUE\n");
            return TRUE;
        }

        for (ix = Microframe;
             ix < USB2_MAX_MICROFRAMES;
             ix += TtEndpoint->ActualPeriod)
        {
            frame = ix / USB2_MICROFRAMES;
            uframe = ix % (USB2_MICROFRAMES - 1);

            DPRINT("USB2_AllocateTimeForEndpoint: frame - %X, uframe - %X, TimeUsed[f][uf] - %X\n",
                   frame,
                   uframe,
                   HcExtension->TimeUsed[frame][uframe]);

            if (!USB2_AllocateCheck(&HcExtension->TimeUsed[frame][uframe],
                                    TtEndpoint->CalcBusTime,
                                    USB2_MAX_MICROFRAME_ALLOCATION))
            {
                DPRINT("USB2_AllocateTimeForEndpoint: Result = FALSE\n");
                Result = FALSE;
            }
        }

        if (!Result)
        {
            for (ix = Microframe;
                 ix < USB2_MAX_MICROFRAMES;
                 ix += TtEndpoint->ActualPeriod)
            {
                frame = ix / USB2_MICROFRAMES;
                uframe = ix % (USB2_MICROFRAMES - 1);

                HcExtension->TimeUsed[frame][uframe] -= TtEndpoint->CalcBusTime;
            }
        }

        DPRINT("USB2_AllocateTimeForEndpoint: Result - TRUE\n");
        return TRUE;
    }

    /* Speed != UsbHighSpeed (FS/LS) */

    if (TtEndpoint->Period > USB2_FRAMES)
        TtEndpoint->ActualPeriod = USB2_FRAMES;
    else
        TtEndpoint->ActualPeriod = TtEndpoint->Period;

    MinTimeUsed = Tt->FrameBudget[0].TimeUsed;

    for (ix = 1; ix < TtEndpoint->ActualPeriod; ix++)
    {
        if ((Tt->FrameBudget[ix].TimeUsed) < MinTimeUsed)
        {
            MinTimeUsed = Tt->FrameBudget[ix].TimeUsed;
            TtEndpoint->StartFrame = ix;
        }
    }

    TransferType = TtEndpoint->TtEndpointParams.TransferType;

    if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
    {
        if (Speed == UsbFullSpeed)
        {
            Overhead = USB2_FS_ISOCHRONOUS_OVERHEAD + Tt->DelayTime;
        }
        else
        {
            DPRINT("USB2_AllocateTimeForEndpoint: ISO can not be on a LS bus!\n");
            return FALSE;
        }
    }
    else
    {
        if (Speed == UsbFullSpeed)
            Overhead = USB2_FS_INTERRUPT_OVERHEAD + Tt->DelayTime;
        else
            Overhead = USB2_LS_INTERRUPT_OVERHEAD + Tt->DelayTime;
    }

    if (Speed == UsbLowSpeed)
        TtEndpoint->CalcBusTime = TtEndpoint->MaxPacketSize * 8 + Overhead;
    else
        TtEndpoint->CalcBusTime = TtEndpoint->MaxPacketSize + Overhead;

    LatestStart = USB2_HUB_DELAY + USB2_FS_SOF_TIME;

    for (ix = 0;
         (TtEndpoint->StartFrame + ix) < USB2_FRAMES;
         ix += TtEndpoint->ActualPeriod)
    {
        frame = TtEndpoint->StartFrame + ix;

        if (Tt->FrameBudget[frame].AltEndpoint &&
            TtEndpoint->CalcBusTime >= (USB2_FS_MAX_PERIODIC_ALLOCATION / 2))
        {
            DPRINT("USB2_AllocateTimeForEndpoint: return FALSE\n");
            return FALSE;
        }

        if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
            prevEndpoint = Tt->FrameBudget[frame].IsoEndpoint;
        else
            prevEndpoint = Tt->FrameBudget[frame].IntEndpoint;

        for (nextEndpoint = prevEndpoint->NextTtEndpoint;
             nextEndpoint;
             nextEndpoint = nextEndpoint->NextTtEndpoint)
        {
            if (USB2_CheckTtEndpointInsert(nextEndpoint, TtEndpoint))
                break;

            prevEndpoint = nextEndpoint;
        }

        StartTime = USB2_GetStartTime(nextEndpoint,
                                      TtEndpoint,
                                      prevEndpoint,
                                      frame);

        LatestStart = max(LatestStart, StartTime);
    }

    TtEndpoint->StartTime = LatestStart;

    if ((LatestStart + TtEndpoint->CalcBusTime) > USB2_FS_MAX_PERIODIC_ALLOCATION)
    {
        TtEndpoint->CalcBusTime = 0;
        DPRINT("USB2_AllocateTimeForEndpoint: return FALSE\n");
        return FALSE;
    }

    for (ix = 0, frame = -TtEndpoint->StartFrame;
         ix < USB2_FRAMES;
         ix++, frame++)
    {
        DPRINT("USB2_AllocateTimeForEndpoint: ix - %X, frame - %X, StartFrame - %X\n",
               ix,
               frame,
               TtEndpoint->StartFrame);

        if (TransferType == USBPORT_TRANSFER_TYPE_ISOCHRONOUS)
        {
            DPRINT1("USB2_AllocateTimeForEndpoint: Iso Ep UNIMPLEMENTED. FIXME\n");
            ASSERT(FALSE);
        }
        else
        {
            IntEndpoint = Tt->FrameBudget[ix].IntEndpoint;
            nextEndpoint = IntEndpoint->NextTtEndpoint;

            for (nextEndpoint = IntEndpoint->NextTtEndpoint;
                 nextEndpoint;
                 nextEndpoint = nextEndpoint->NextTtEndpoint)
            {
                if (USB2_CheckTtEndpointInsert(nextEndpoint, TtEndpoint))
                    break;
                IntEndpoint = nextEndpoint;
            }

            if ((frame % TtEndpoint->ActualPeriod) == 0)
            {
                calcBusTime = 0;
            }
            else
            {
                if (nextEndpoint)
                {
                    calcBusTime = LatestStart + TtEndpoint->CalcBusTime -
                                  nextEndpoint->StartTime;
                }
                else
                {
                    calcBusTime = TtEndpoint->CalcBusTime;
                }

                if (calcBusTime > 0)
                {
                    TimeUsed = Tt->FrameBudget[ix].TimeUsed;

                    if (!USB2_AllocateCheck(&TimeUsed,
                                            calcBusTime,
                                            USB2_FS_MAX_PERIODIC_ALLOCATION))
                    {
                        DPRINT("USB2_AllocateTimeForEndpoint: Result = FALSE\n");
                        Result = FALSE;
                    }
                }
            }

            if (nextEndpoint != TtEndpoint)
            {
                if ((frame % TtEndpoint->ActualPeriod) == 0)
                {
                    if (frame == 0)
                    {
                        DPRINT("USB2_AllocateTimeForEndpoint: frame == 0\n");
                        TtEndpoint->NextTtEndpoint = nextEndpoint;
                    }

                    IntEndpoint->NextTtEndpoint = TtEndpoint;

                    DPRINT("USB2_AllocateTimeForEndpoint: TtEndpoint - %p, nextEndpoint - %p\n",
                           TtEndpoint,
                           nextEndpoint);
                }

                if (calcBusTime > 0)
                {
                    BOOLEAN IsMoved;
                    BOOLEAN MoveResult;

                    DPRINT("USB2_AllocateTimeForEndpoint: nextEndpoint - %p, calcBusTime - %X\n",
                           nextEndpoint,
                           calcBusTime);

                    for (;
                         nextEndpoint;
                         nextEndpoint = nextEndpoint->NextTtEndpoint)
                    {
                        MoveResult = USB2_MoveTtEndpoint(nextEndpoint,
                                                         calcBusTime,
                                                         Rebalance,
                                                         *RebalanceListEntries,
                                                         &IsMoved);

                        if (!IsMoved)
                        {
                            DPRINT("USB2_AllocateTimeForEndpoint: Result = FALSE\n");
                            Result = FALSE;
                        }

                        if (!MoveResult)
                            break;
                    }
                }
            }
        }

        if ((frame % TtEndpoint->ActualPeriod) == 0)
        {
            if (!USB2_AllocateHS(TtEndpoint, frame))
            {
                DPRINT1("USB2_AllocateTimeForEndpoint: USB2_AllocateHS return FALSE\n");
                Result = FALSE;
            }

            Tt->FrameBudget[ix].TimeUsed += TtEndpoint->CalcBusTime;
        }

        if (Result == FALSE)
        {
            USB2_DeallocateEndpointBudget(TtEndpoint,
                                          Rebalance,
                                          RebalanceListEntries,
                                          ix + 1);

            DPRINT("USB2_AllocateTimeForEndpoint: return FALSE\n");
            return FALSE;
        }
    }

    DPRINT("USB2_AllocateTimeForEndpoint: Result - %X\n", Result);
    return Result;
}

BOOLEAN
NTAPI
USB2_ChangePeriod(IN PUSB2_TT_ENDPOINT TtEndpoint,
                  IN PUSB2_REBALANCE Rebalance,
                  IN PULONG RebalanceListEntries)
{
    BOOLEAN Result;

    DPRINT("USB2_ChangePeriod: RebalanceListEntries - %X\n",
           *RebalanceListEntries);

    USB2_DeallocateEndpointBudget(TtEndpoint,
                                  Rebalance,
                                  RebalanceListEntries,
                                  USB2_FRAMES);

    TtEndpoint->PreviosPeriod = TtEndpoint->Period;
    TtEndpoint->Period = ENDPOINT_INTERRUPT_1ms;

    Result = USB2_AllocateTimeForEndpoint(TtEndpoint,
                                          Rebalance,
                                          RebalanceListEntries);

    return Result;
}

BOOLEAN
NTAPI
USB2_PromotePeriods(IN PUSB2_TT_ENDPOINT TtEndpoint,
                    IN PUSB2_REBALANCE Rebalance,
                    IN PULONG RebalanceListEntries)
{
    PUSB2_TT_ENDPOINT ttEndpoint;
    ULONG TransferType;
    ULONG ix;

    TransferType = TtEndpoint->TtEndpointParams.TransferType;

    if (TtEndpoint->ActualPeriod != ENDPOINT_INTERRUPT_1ms &&
        TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT &&
        (CHAR)TtEndpoint->StartMicroframe > 2 &&
        !USB2_ChangePeriod(TtEndpoint, Rebalance, RebalanceListEntries))
    {
        DPRINT("USB2_PromotePeriods: return FALSE\n");
        return FALSE;
    }

    if (Rebalance->RebalanceEndpoint[0] == NULL)
    {
        DPRINT("USB2_PromotePeriods: return TRUE\n");
        return TRUE;
    }

    DPRINT("USB2_PromotePeriods: RebalanceListEntries - %X\n",
           *RebalanceListEntries);

    for (ix = 0; Rebalance->RebalanceEndpoint[ix]; ix++)
    {
        Rebalance->RebalanceEndpoint[ix]->IsPromoted = FALSE;
    }

    for (ix = 0; ; ix++)
    {
        ttEndpoint = Rebalance->RebalanceEndpoint[ix];
        TransferType = ttEndpoint->TtEndpointParams.TransferType;

        if (ttEndpoint->ActualPeriod != ENDPOINT_INTERRUPT_1ms &&
            TransferType == USBPORT_TRANSFER_TYPE_INTERRUPT &&
            (CHAR)ttEndpoint->StartMicroframe > 2)
        {
            USB2_DeallocateEndpointBudget(ttEndpoint,
                                          Rebalance,
                                          RebalanceListEntries,
                                          USB2_FRAMES);

            ttEndpoint->IsPromoted = TRUE;
            ttEndpoint->PreviosPeriod = ttEndpoint->Period;
            ttEndpoint->Period = ENDPOINT_INTERRUPT_1ms;

            if (!USB2_AllocateTimeForEndpoint(ttEndpoint,
                                              Rebalance,
                                              RebalanceListEntries))
            {
                break;
            }
        }

        if (Rebalance->RebalanceEndpoint[ix + 1] == NULL)
        {
            DPRINT("USB2_PromotePeriods: return TRUE\n");
            return TRUE;
        }
    }

    USB2_DeallocateEndpointBudget(TtEndpoint,
                                  Rebalance,
                                  RebalanceListEntries,
                                  USB2_FRAMES);

    TtEndpoint->Period = TtEndpoint->PreviosPeriod;
    TtEndpoint->PreviosPeriod = 0;

    for (ix = 0; Rebalance->RebalanceEndpoint[ix]; ix++)
    {
        ttEndpoint = Rebalance->RebalanceEndpoint[ix];

        if (ttEndpoint->IsPromoted)
        {
            if (ttEndpoint->CalcBusTime)
            {
                USB2_DeallocateEndpointBudget(ttEndpoint,
                                              Rebalance,
                                              RebalanceListEntries,
                                              USB2_FRAMES);
            }

            TtEndpoint->Period = TtEndpoint->PreviosPeriod;
            TtEndpoint->PreviosPeriod = 0;

            USB2_AllocateTimeForEndpoint(ttEndpoint,
                                         Rebalance,
                                         RebalanceListEntries);
        }
    }

    DPRINT("USB2_PromotePeriods: return FALSE\n");
    return FALSE;
}

VOID
NTAPI
USBPORT_UpdateAllocatedBwTt(IN PUSB2_TT_EXTENSION TtExtension)
{
    ULONG BusBandwidth;
    ULONG NewBusBandwidth;
    ULONG MaxBusBandwidth = 0;
    ULONG MinBusBandwidth;
    ULONG ix;

    DPRINT("USBPORT_UpdateAllocatedBwTt: TtExtension - %p\n", TtExtension);

    BusBandwidth = TtExtension->BusBandwidth;
    MinBusBandwidth = BusBandwidth;

    for (ix = 0; ix < USB2_FRAMES; ix++)
    {
        NewBusBandwidth = BusBandwidth - TtExtension->Bandwidth[ix];

        MaxBusBandwidth = max(MaxBusBandwidth, NewBusBandwidth);
        MinBusBandwidth = min(MinBusBandwidth, NewBusBandwidth);
    }

    TtExtension->MaxBandwidth = MaxBusBandwidth;

    if (MinBusBandwidth == BusBandwidth)
        TtExtension->MinBandwidth = 0;
    else
        TtExtension->MinBandwidth = MinBusBandwidth;
}

BOOLEAN
NTAPI
USBPORT_AllocateBandwidthUSB2(IN PDEVICE_OBJECT FdoDevice,
                              IN PUSBPORT_ENDPOINT Endpoint)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    PUSBPORT_ENDPOINT_PROPERTIES EndpointProperties;
    PUSB2_TT_EXTENSION TtExtension;
    ULONG TransferType;
    PUSB2_REBALANCE Rebalance;
    LIST_ENTRY RebalanceList;
    ULONG RebalanceListEntries;
    PUSB2_TT_ENDPOINT TtEndpoint;
    PUSB2_TT_ENDPOINT RebalanceTtEndpoint;
    PUSB2_TT Tt;
    USB_DEVICE_SPEED DeviceSpeed;
    ULONG Period;
    ULONG AllocedBusTime;
    ULONG EndpointBandwidth;
    ULONG ScheduleOffset;
    ULONG Factor;
    ULONG ix;
    ULONG n;
    BOOLEAN Direction;
    UCHAR SMask;
    UCHAR CMask;
    UCHAR ActualPeriod;
    BOOLEAN Result;

    DPRINT("USBPORT_AllocateBandwidthUSB2: FdoDevice - %p, Endpoint - %p\n",
           FdoDevice,
           Endpoint);

    EndpointProperties = &Endpoint->EndpointProperties;
    EndpointProperties->ScheduleOffset = 0;

    if (Endpoint->Flags & ENDPOINT_FLAG_ROOTHUB_EP0)
    {
        DPRINT("USBPORT_AllocateBandwidthUSB2: ENDPOINT_FLAG_ROOTHUB_EP0\n");
        return TRUE;
    }

    FdoExtension = FdoDevice->DeviceExtension;

    TransferType = EndpointProperties->TransferType;
    DPRINT("USBPORT_AllocateBandwidthUSB2: TransferType - %X\n", TransferType);

    if (TransferType == USBPORT_TRANSFER_TYPE_CONTROL ||
        TransferType == USBPORT_TRANSFER_TYPE_BULK)
    {
        return TRUE;
    }

    if (Endpoint->TtExtension)
        TtExtension = Endpoint->TtExtension;
    else
        TtExtension = NULL;

    InitializeListHead(&RebalanceList);

    Rebalance = ExAllocatePoolWithTag(NonPagedPool,
                                      sizeof(USB2_REBALANCE),
                                      USB_PORT_TAG);

    DPRINT("USBPORT_AllocateBandwidthUSB2: Rebalance - %p, TtExtension - %p\n",
           Rebalance,
           TtExtension);

    if (Rebalance)
    {
        RtlZeroMemory(Rebalance, sizeof(USB2_REBALANCE));

        TtEndpoint = Endpoint->TtEndpoint;
        TtEndpoint->Endpoint = Endpoint;

        Direction = EndpointProperties->Direction == USBPORT_TRANSFER_DIRECTION_OUT;
        DeviceSpeed = EndpointProperties->DeviceSpeed;

        switch (DeviceSpeed)
        {
            case UsbLowSpeed:
            case UsbFullSpeed:
            {
                Tt = &TtExtension->Tt;
                Period = USB2_FRAMES;

                while (Period > 0 && Period > EndpointProperties->Period)
                {
                    Period >>= 1;
                }

                DPRINT("USBPORT_AllocateBandwidthUSB2: Period - %X\n", Period);
                break;
            }

            case UsbHighSpeed:
            {
                Tt = &FdoExtension->Usb2Extension->HcTt;
                Period = EndpointProperties->Period;

                break;
            }

            default:
            {
                DPRINT1("USBPORT_AllocateBandwidthUSB2: DeviceSpeed - %X!\n",
                        DeviceSpeed);

                DbgBreakPoint();

                Tt = &TtExtension->Tt;
                break;
            }
        }

        USB2_InitTtEndpoint(TtEndpoint,
                            TransferType,
                            Direction,
                            DeviceSpeed,
                            Period,
                            EndpointProperties->MaxPacketSize,
                            Tt);

        RebalanceListEntries = USB2_FRAMES - 2;

        Result = USB2_AllocateTimeForEndpoint(TtEndpoint,
                                              Rebalance,
                                              &RebalanceListEntries);

        if (Result)
        {
            Result = USB2_PromotePeriods(TtEndpoint,
                                         Rebalance,
                                         &RebalanceListEntries);
        }

        RebalanceListEntries = 0;

        for (ix = 0; Rebalance->RebalanceEndpoint[ix]; ix++)
        {
            RebalanceListEntries = ix + 1;
        }
    }
    else
    {
        RebalanceListEntries = 0;
        Result = FALSE;
    }

    DPRINT("USBPORT_AllocateBandwidthUSB2: RebalanceListEntries - %X, Result - %X\n",
           RebalanceListEntries,
           Result);

    for (ix = 0; ix < RebalanceListEntries; ix++)
    {
        RebalanceTtEndpoint = Rebalance->RebalanceEndpoint[ix];

        DPRINT("USBPORT_AllocateBandwidthUSB2: RebalanceTtEndpoint[%X] - %p, RebalanceTtEndpoint - %p, RebalanceLink - %p\n",
               ix,
               RebalanceTtEndpoint,
               &RebalanceTtEndpoint->Endpoint->RebalanceLink);

        InsertTailList(&RebalanceList,
                       &RebalanceTtEndpoint->Endpoint->RebalanceLink);
    }

    if (Rebalance)
        ExFreePoolWithTag(Rebalance, USB_PORT_TAG);

    if (Result)
    {
        SMask = USB2_GetSMASK(Endpoint->TtEndpoint);
        EndpointProperties->InterruptScheduleMask = SMask;

        CMask = USB2_GetCMASK(Endpoint->TtEndpoint);
        EndpointProperties->SplitCompletionMask = CMask;

        AllocedBusTime = TtEndpoint->CalcBusTime;

        EndpointBandwidth = USB2_MICROFRAMES * AllocedBusTime;
        EndpointProperties->UsbBandwidth = EndpointBandwidth;

        ActualPeriod = Endpoint->TtEndpoint->ActualPeriod;
        EndpointProperties->Period = ActualPeriod;

        ScheduleOffset = Endpoint->TtEndpoint->StartFrame;
        EndpointProperties->ScheduleOffset = ScheduleOffset;

        ASSERT(ActualPeriod);
        Factor = USB2_FRAMES / ActualPeriod;
        n = ScheduleOffset * Factor;

        if (TtExtension)
        {
            for (ix = 0; ix < Factor; ix++)
            {
                TtExtension->Bandwidth[n + ix] -= EndpointBandwidth;
            }
        }
        else
        {
            for (ix = 1; ix < Factor; ix++)
            {
                FdoExtension->Bandwidth[n + ix] -= EndpointBandwidth;
            }
        }

        USBPORT_DumpingEndpointProperties(EndpointProperties);
        USBPORT_DumpingTtEndpoint(Endpoint->TtEndpoint);

        if (AllocedBusTime >= (USB2_FS_MAX_PERIODIC_ALLOCATION / 2))
        {
            DPRINT1("USBPORT_AllocateBandwidthUSB2: AllocedBusTime >= 0.5 * MAX_ALLOCATION \n");
        }
    }

    USB2_Rebalance(FdoDevice, &RebalanceList);

    if (!TtExtension)
    {
        DPRINT("USBPORT_AllocateBandwidthUSB2: Result - %X\n", Result);
        return Result;
    }

    for (ix = 0; ix < USB2_FRAMES; ix++)
    {
        FdoExtension->Bandwidth[ix] += TtExtension->MaxBandwidth;
    }

    USBPORT_UpdateAllocatedBwTt(TtExtension);

    for (ix = 0; ix < USB2_FRAMES; ix++)
    {
        FdoExtension->Bandwidth[ix] -= TtExtension->MaxBandwidth;
    }

    DPRINT("USBPORT_AllocateBandwidthUSB2: Result - %X\n", Result);

    return Result;
}

VOID
NTAPI
USBPORT_FreeBandwidthUSB2(IN PDEVICE_OBJECT FdoDevice,
                          IN PUSBPORT_ENDPOINT Endpoint)
{
    PUSBPORT_DEVICE_EXTENSION FdoExtension;
    ULONG Period;
    ULONG ScheduleOffset;
    ULONG EndpointBandwidth;
    LIST_ENTRY RebalanceList;
    ULONG TransferType;
    PUSB2_REBALANCE Rebalance;
    ULONG RebalanceListEntries;
    ULONG Factor;
    ULONG ix;
    ULONG n;
    PUSB2_TT_EXTENSION TtExtension;
    PUSB2_TT_ENDPOINT RebalanceTtEndpoint;

    DPRINT("USBPORT_FreeBandwidthUSB2: Endpoint - %p\n", Endpoint);

    FdoExtension = FdoDevice->DeviceExtension;

    Period = Endpoint->EndpointProperties.Period;
    ScheduleOffset = Endpoint->EndpointProperties.ScheduleOffset;
    EndpointBandwidth = Endpoint->EndpointProperties.UsbBandwidth;

    InitializeListHead(&RebalanceList);

    TransferType = Endpoint->EndpointProperties.TransferType;

    if (TransferType == USBPORT_TRANSFER_TYPE_CONTROL ||
        TransferType == USBPORT_TRANSFER_TYPE_BULK ||
        (Endpoint->Flags & ENDPOINT_FLAG_ROOTHUB_EP0))
    {
        return;
    }

    Rebalance = ExAllocatePoolWithTag(NonPagedPool,
                                      sizeof(USB2_REBALANCE),
                                      USB_PORT_TAG);

    if (!Rebalance)
    {
        DPRINT1("USBPORT_FreeBandwidthUSB2: Rebalance == NULL!\n");
        return;
    }

    RtlZeroMemory(Rebalance, sizeof(USB2_REBALANCE));

    ASSERT(Period != 0);
    Factor = USB2_FRAMES / Period;
    n = ScheduleOffset * Factor;

    TtExtension = Endpoint->TtExtension;

    if (TtExtension)
    {
        for (ix = 0; ix < Factor; ix++)
        {
            TtExtension->Bandwidth[n + ix] += EndpointBandwidth;
        }
    }
    else
    {
        for (ix = 1; ix < Factor; ix++)
        {
            FdoExtension->Bandwidth[n + ix] += EndpointBandwidth;
        }
    }

    RebalanceListEntries = USB2_FRAMES - 2;

    USB2_DeallocateEndpointBudget(Endpoint->TtEndpoint,
                                  Rebalance,
                                  &RebalanceListEntries,
                                  USB2_FRAMES);

    RebalanceListEntries = 0;

    for (ix = 0; Rebalance->RebalanceEndpoint[ix]; ix++)
    {
        RebalanceListEntries = ix + 1;
    }

    for (ix = 0; ix < RebalanceListEntries; ix++)
    {
        RebalanceTtEndpoint = Rebalance->RebalanceEndpoint[ix];

        DPRINT("USBPORT_AllocateBandwidthUSB2: RebalanceTtEndpoint[%X] - %p, RebalanceTtEndpoint - %p, RebalanceLink - %p\n",
               ix,
               RebalanceTtEndpoint,
               &RebalanceTtEndpoint->Endpoint->RebalanceLink);

        InsertTailList(&RebalanceList,
                       &RebalanceTtEndpoint->Endpoint->RebalanceLink);
    }

    ExFreePoolWithTag(Rebalance, USB_PORT_TAG);

    USB2_Rebalance(FdoDevice, &RebalanceList);

    if (!TtExtension)
        return;

    for (ix = 0; ix < USB2_FRAMES; ix++)
    {
        FdoExtension->Bandwidth[ix] += TtExtension->MaxBandwidth;
    }

    USBPORT_UpdateAllocatedBwTt(TtExtension);

    for (ix = 0; ix < USB2_FRAMES; ix++)
    {
        FdoExtension->Bandwidth[ix] -= TtExtension->MaxBandwidth;
    }

    DPRINT1("USBPORT_FreeBandwidthUSB2: exit\n");
}

VOID
NTAPI
USB2_InitTT(IN PUSB2_HC_EXTENSION HcExtension,
            IN PUSB2_TT Tt)
{
    ULONG ix;
    ULONG jx;

    DPRINT("USB2_InitTT: HcExtension - %p, Tt - %p\n", HcExtension, Tt);

    Tt->HcExtension = HcExtension;
    Tt->DelayTime = 1;
    Tt->MaxTime = USB2_FS_MAX_PERIODIC_ALLOCATION;

    for (ix = 0; ix < USB2_FRAMES; ix++)
    {
        Tt->FrameBudget[ix].TimeUsed = USB2_MAX_MICROFRAMES;
        Tt->FrameBudget[ix].AltEndpoint = NULL;

        for (jx = 0; jx < USB2_MICROFRAMES; jx++)
        {
            Tt->TimeCS[ix][jx] = 0;
            Tt->NumStartSplits[ix][jx] = 0;
        }

        Tt->FrameBudget[ix].IsoEndpoint = &Tt->IsoEndpoint[ix];

        USB2_InitTtEndpoint(&Tt->IsoEndpoint[ix],
                            USBPORT_TRANSFER_TYPE_ISOCHRONOUS,
                            USBPORT_TRANSFER_DIRECTION_OUT,
                            UsbFullSpeed,
                            USB2_FRAMES,
                            0,
                            Tt);

        Tt->IsoEndpoint[ix].ActualPeriod = USB2_FRAMES;
        Tt->IsoEndpoint[ix].CalcBusTime = USB2_FS_SOF_TIME + USB2_HUB_DELAY;
        Tt->IsoEndpoint[ix].StartFrame = ix;
        Tt->IsoEndpoint[ix].StartMicroframe = USB2_PREV_MICROFRAME;

        Tt->FrameBudget[ix].IntEndpoint = &Tt->IntEndpoint[ix];

        USB2_InitTtEndpoint(&Tt->IntEndpoint[ix],
                            USBPORT_TRANSFER_TYPE_INTERRUPT,
                            USBPORT_TRANSFER_DIRECTION_OUT,
                            UsbFullSpeed,
                            USB2_FRAMES,
                            0,
                            Tt);

        Tt->IntEndpoint[ix].ActualPeriod = USB2_FRAMES;
        Tt->IntEndpoint[ix].CalcBusTime = USB2_FS_SOF_TIME + USB2_HUB_DELAY;
        Tt->IntEndpoint[ix].StartFrame = ix;
        Tt->IntEndpoint[ix].StartMicroframe = USB2_PREV_MICROFRAME;
    }
}

VOID
NTAPI
USB2_InitController(IN PUSB2_HC_EXTENSION HcExtension)
{
    ULONG ix;
    ULONG jx;

    DPRINT("USB2_InitController: HcExtension - %p\n", HcExtension);

    HcExtension->MaxHsBusAllocation = USB2_MAX_MICROFRAME_ALLOCATION;

    for (ix = 0; ix < USB2_FRAMES; ix++)
    {
        for (jx = 0; jx < USB2_MICROFRAMES; jx++)
        {
            HcExtension->TimeUsed[ix][jx] = 0;
        }
    }

    HcExtension->HcDelayTime = USB2_CONTROLLER_DELAY;

    USB2_InitTT(HcExtension, &HcExtension->HcTt);
}