/* $Id: VUSBUrb.cpp $ */
/** @file
 * Virtual USB - URBs.
 */

/*
 * Copyright (C) 2006-2016 Oracle Corporation
 *
 * This file is part of VirtualBox Open Source Edition (OSE), as
 * available from http://www.virtualbox.org. This file is free software;
 * you can redistribute it and/or modify it under the terms of the GNU
 * General Public License (GPL) as published by the Free Software
 * Foundation, in version 2 as it comes in the "COPYING" file of the
 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
 */


/*********************************************************************************************************************************
*   Header Files                                                                                                                 *
*********************************************************************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_VUSB
#include <VBox/vmm/pdm.h>
#include <VBox/vmm/vmapi.h>
#include <VBox/err.h>
#include <iprt/alloc.h>
#include <VBox/log.h>
#include <iprt/time.h>
#include <iprt/thread.h>
#include <iprt/semaphore.h>
#include <iprt/string.h>
#include <iprt/assert.h>
#include <iprt/asm.h>
#include <iprt/env.h>
#include "VUSBInternal.h"



/*********************************************************************************************************************************
*   Global Variables                                                                                                             *
*********************************************************************************************************************************/
/** Strings for the CTLSTAGE enum values. */
const char * const g_apszCtlStates[4] =
{
    "SETUP",
    "DATA",
    "STATUS",
    "N/A"
};


/*********************************************************************************************************************************
*   Internal Functions                                                                                                           *
*********************************************************************************************************************************/


/**
 * Complete a SETUP stage URB.
 *
 * This is used both for dev2host and host2dev kind of transfers.
 * It is used by both the sync and async control paths.
 */
static void vusbMsgSetupCompletion(PVUSBURB pUrb)
{
    PVUSBDEV        pDev   = pUrb->pVUsb->pDev;
    PVUSBPIPE       pPipe  = &pDev->aPipes[pUrb->EndPt];
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;
    PVUSBSETUP      pSetup = pExtra->pMsg;

    LogFlow(("%s: vusbMsgSetupCompletion: cbData=%d wLength=%#x cbLeft=%d pPipe=%p stage %s->DATA\n",
             pUrb->pszDesc, pUrb->cbData, pSetup->wLength, pExtra->cbLeft, pPipe, g_apszCtlStates[pExtra->enmStage])); NOREF(pSetup);
    pExtra->enmStage = CTLSTAGE_DATA;
    pUrb->enmStatus  = VUSBSTATUS_OK;
}

/**
 * Complete a DATA stage URB.
 *
 * This is used both for dev2host and host2dev kind of transfers.
 * It is used by both the sync and async control paths.
 */
static void vusbMsgDataCompletion(PVUSBURB pUrb)
{
    PVUSBDEV        pDev   = pUrb->pVUsb->pDev;
    PVUSBPIPE       pPipe  = &pDev->aPipes[pUrb->EndPt];
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;
    PVUSBSETUP      pSetup = pExtra->pMsg;

    LogFlow(("%s: vusbMsgDataCompletion: cbData=%d wLength=%#x cbLeft=%d pPipe=%p stage DATA\n",
             pUrb->pszDesc, pUrb->cbData, pSetup->wLength, pExtra->cbLeft, pPipe)); NOREF(pSetup);

    pUrb->enmStatus = VUSBSTATUS_OK;
}

/**
 * Complete a STATUS stage URB.
 *
 * This is used both for dev2host and host2dev kind of transfers.
 * It is used by both the sync and async control paths.
 */
static void vusbMsgStatusCompletion(PVUSBURB pUrb)
{
    PVUSBDEV        pDev = pUrb->pVUsb->pDev;
    PVUSBPIPE       pPipe = &pDev->aPipes[pUrb->EndPt];
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;

    if (pExtra->fOk)
    {
        /*
         * vusbDevStdReqSetAddress requests are deferred.
         */
        if (pDev->u8NewAddress != VUSB_INVALID_ADDRESS)
        {
            vusbDevSetAddress(pDev, pDev->u8NewAddress);
            pDev->u8NewAddress = VUSB_INVALID_ADDRESS;
        }

        LogFlow(("%s: vusbMsgStatusCompletion: pDev=%p[%s] pPipe=%p err=OK stage %s->SETUP\n",
                 pUrb->pszDesc, pDev, pDev->pUsbIns->pszName, pPipe, g_apszCtlStates[pExtra->enmStage]));
        pUrb->enmStatus = VUSBSTATUS_OK;
    }
    else
    {
        LogFlow(("%s: vusbMsgStatusCompletion: pDev=%p[%s] pPipe=%p err=STALL stage %s->SETUP\n",
                 pUrb->pszDesc, pDev, pDev->pUsbIns->pszName, pPipe, g_apszCtlStates[pExtra->enmStage]));
        pUrb->enmStatus = VUSBSTATUS_STALL;
    }

    /*
     * Done with this message sequence.
     */
    pExtra->pbCur    = NULL;
    pExtra->enmStage = CTLSTAGE_SETUP;
}

/**
 * This is a worker function for vusbMsgCompletion and
 * vusbMsgSubmitSynchronously used to complete the original URB.
 *
 * @param   pUrb    The URB originating from the HCI.
 */
static void vusbCtrlCompletion(PVUSBURB pUrb)
{
    PVUSBDEV        pDev = pUrb->pVUsb->pDev;
    PVUSBPIPE       pPipe = &pDev->aPipes[pUrb->EndPt];
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;
    LogFlow(("%s: vusbCtrlCompletion: pDev=%p[%s]\n", pUrb->pszDesc, pDev, pDev->pUsbIns->pszName));

    switch (pExtra->enmStage)
    {
        case CTLSTAGE_SETUP:
            vusbMsgSetupCompletion(pUrb);
            break;
        case CTLSTAGE_DATA:
            vusbMsgDataCompletion(pUrb);
            break;
        case CTLSTAGE_STATUS:
            vusbMsgStatusCompletion(pUrb);
            break;
    }
}

/**
 * Called from vusbUrbCompletionRh when it encounters a
 * message type URB.
 *
 * @param   pUrb    The URB within the control pipe extra state data.
 */
static void vusbMsgCompletion(PVUSBURB pUrb)
{
    PVUSBDEV        pDev   = pUrb->pVUsb->pDev;
    PVUSBPIPE       pPipe  = &pDev->aPipes[pUrb->EndPt];

    RTCritSectEnter(&pPipe->CritSectCtrl);
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;

#ifdef LOG_ENABLED
    LogFlow(("%s: vusbMsgCompletion: pDev=%p[%s]\n", pUrb->pszDesc, pDev, pDev->pUsbIns->pszName));
    vusbUrbTrace(pUrb, "vusbMsgCompletion", true);
#endif
    Assert(&pExtra->Urb == pUrb);


    if (pUrb->enmStatus == VUSBSTATUS_OK)
        pExtra->fOk = true;
    else
        pExtra->fOk = false;
    pExtra->cbLeft = pUrb->cbData - sizeof(VUSBSETUP);

    /*
     * Complete the original URB.
     */
    PVUSBURB pCtrlUrb = pUrb->pVUsb->pCtrlUrb;
    pCtrlUrb->enmState = VUSBURBSTATE_REAPED;
    vusbCtrlCompletion(pCtrlUrb);

    /*
     * 'Free' the message URB, i.e. put it back to the allocated state.
     */
    Assert(   pUrb->enmState == VUSBURBSTATE_REAPED
           || pUrb->enmState == VUSBURBSTATE_CANCELLED);
    if (pUrb->enmState != VUSBURBSTATE_CANCELLED)
    {
        pUrb->enmState = VUSBURBSTATE_ALLOCATED;
        pUrb->fCompleting = false;
    }
    RTCritSectLeave(&pPipe->CritSectCtrl);

    /* Complete the original control URB on the root hub now. */
    vusbUrbCompletionRh(pCtrlUrb);
}

/**
 * Deal with URB errors, talking thru the RH to the HCI.
 *
 * @returns true if it could be retried.
 * @returns false if it should be completed with failure.
 * @param   pUrb    The URB in question.
 */
int vusbUrbErrorRh(PVUSBURB pUrb)
{
    PVUSBDEV pDev = pUrb->pVUsb->pDev;
    PVUSBROOTHUB pRh = vusbDevGetRh(pDev);
    AssertPtrReturn(pRh, VERR_VUSB_DEVICE_NOT_ATTACHED);
    LogFlow(("%s: vusbUrbErrorRh: pDev=%p[%s] rh=%p\n", pUrb->pszDesc, pDev, pDev->pUsbIns ? pDev->pUsbIns->pszName : "", pRh));
    return pRh->pIRhPort->pfnXferError(pRh->pIRhPort, pUrb);
}

/**
 * Does URB completion on roothub level.
 *
 * @param   pUrb    The URB to complete.
 */
void vusbUrbCompletionRh(PVUSBURB pUrb)
{
    LogFlow(("%s: vusbUrbCompletionRh: type=%s status=%s\n",
             pUrb->pszDesc, vusbUrbTypeName(pUrb->enmType), vusbUrbStatusName(pUrb->enmStatus)));
    AssertMsg(   pUrb->enmState == VUSBURBSTATE_REAPED
              || pUrb->enmState == VUSBURBSTATE_CANCELLED, ("%d\n", pUrb->enmState));

    if (   pUrb->pVUsb->pDev
        && pUrb->pVUsb->pDev->hSniffer)
    {
        int rc = VUSBSnifferRecordEvent(pUrb->pVUsb->pDev->hSniffer, pUrb,
                                          pUrb->enmStatus == VUSBSTATUS_OK
                                        ? VUSBSNIFFEREVENT_COMPLETE
                                        : VUSBSNIFFEREVENT_ERROR_COMPLETE);
        if (RT_FAILURE(rc))
            LogRel(("VUSB: Capturing URB completion event failed with %Rrc\n", rc));
    }

    PVUSBROOTHUB pRh = vusbDevGetRh(pUrb->pVUsb->pDev);
    AssertPtrReturnVoid(pRh);

    /* If there is a sniffer on the roothub record the completed URB there too. */
    if (pRh->hSniffer != VUSBSNIFFER_NIL)
    {
        int rc = VUSBSnifferRecordEvent(pRh->hSniffer, pUrb,
                                          pUrb->enmStatus == VUSBSTATUS_OK
                                        ? VUSBSNIFFEREVENT_COMPLETE
                                        : VUSBSNIFFEREVENT_ERROR_COMPLETE);
        if (RT_FAILURE(rc))
            LogRel(("VUSB: Capturing URB completion event on the root hub failed with %Rrc\n", rc));
    }

#ifdef VBOX_WITH_STATISTICS
    /*
     * Total and per-type submit statistics.
     */
    if (pUrb->enmType != VUSBXFERTYPE_MSG)
    {
        Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));

        if (    pUrb->enmStatus == VUSBSTATUS_OK
            ||  pUrb->enmStatus == VUSBSTATUS_DATA_UNDERRUN
            ||  pUrb->enmStatus == VUSBSTATUS_DATA_OVERRUN)
        {
            if (pUrb->enmType == VUSBXFERTYPE_ISOC)
            {
                for (unsigned i = 0; i < pUrb->cIsocPkts; i++)
                {
                    const unsigned cb = pUrb->aIsocPkts[i].cb;
                    if (cb)
                    {
                        STAM_COUNTER_ADD(&pRh->Total.StatActBytes, cb);
                        STAM_COUNTER_ADD(&pRh->aTypes[VUSBXFERTYPE_ISOC].StatActBytes, cb);
                        STAM_COUNTER_ADD(&pRh->aStatIsocDetails[i].Bytes, cb);
                        if (pUrb->enmDir == VUSBDIRECTION_IN)
                        {
                            STAM_COUNTER_ADD(&pRh->Total.StatActReadBytes, cb);
                            STAM_COUNTER_ADD(&pRh->aTypes[VUSBXFERTYPE_ISOC].StatActReadBytes, cb);
                        }
                        else
                        {
                            STAM_COUNTER_ADD(&pRh->Total.StatActWriteBytes, cb);
                            STAM_COUNTER_ADD(&pRh->aTypes[VUSBXFERTYPE_ISOC].StatActWriteBytes, cb);
                        }
                        STAM_COUNTER_INC(&pRh->StatIsocActPkts);
                        STAM_COUNTER_INC(&pRh->StatIsocActReadPkts);
                    }
                    STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].Pkts);
                    switch (pUrb->aIsocPkts[i].enmStatus)
                    {
                        case VUSBSTATUS_OK:
                            if (cb)                     STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].Ok);
                            else                        STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].Ok0); break;
                        case VUSBSTATUS_DATA_UNDERRUN:
                            if (cb)                     STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].DataUnderrun);
                            else                        STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].DataUnderrun0); break;
                        case VUSBSTATUS_DATA_OVERRUN:   STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].DataOverrun); break;
                        case VUSBSTATUS_NOT_ACCESSED:   STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].NotAccessed); break;
                        default:                        STAM_COUNTER_INC(&pRh->aStatIsocDetails[i].Misc); break;
                    }
                }
            }
            else
            {
                STAM_COUNTER_ADD(&pRh->Total.StatActBytes, pUrb->cbData);
                STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatActBytes, pUrb->cbData);
                if (pUrb->enmDir == VUSBDIRECTION_IN)
                {
                    STAM_COUNTER_ADD(&pRh->Total.StatActReadBytes, pUrb->cbData);
                    STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatActReadBytes, pUrb->cbData);
                }
                else
                {
                    STAM_COUNTER_ADD(&pRh->Total.StatActWriteBytes, pUrb->cbData);
                    STAM_COUNTER_ADD(&pRh->aTypes[pUrb->enmType].StatActWriteBytes, pUrb->cbData);
                }
            }
        }
        else
        {
            /* (Note. this also counts the cancelled packets) */
            STAM_COUNTER_INC(&pRh->Total.StatUrbsFailed);
            STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsFailed);
        }
    }
#endif /* VBOX_WITH_STATISTICS */

    /*
     * Msg transfers are special virtual transfers associated with
     * vusb, not the roothub
     */
    switch (pUrb->enmType)
    {
        case VUSBXFERTYPE_MSG:
            vusbMsgCompletion(pUrb);
            return;
        case VUSBXFERTYPE_ISOC:
            /* Don't bother with error callback for isochronous URBs. */
            break;

#if 1   /** @todo r=bird: OHCI say "If the Transfer Descriptor is being
         * retired because of an error, the Host Controller must update
         * the Halt bit of the Endpoint Descriptor."
         *
         * So, I'll subject all transfertypes to the same halt stuff now. It could
         * just happen to fix the logitech disconnect trap in win2k.
         */
        default:
#endif
        case VUSBXFERTYPE_BULK:
            if (pUrb->enmStatus != VUSBSTATUS_OK)
                vusbUrbErrorRh(pUrb);
            break;
    }
#ifdef LOG_ENABLED
    vusbUrbTrace(pUrb, "vusbUrbCompletionRh", true);
#endif

    pRh->pIRhPort->pfnXferCompletion(pRh->pIRhPort, pUrb);
    if (pUrb->enmState == VUSBURBSTATE_REAPED)
    {
        LogFlow(("%s: vusbUrbCompletionRh: Freeing URB\n", pUrb->pszDesc));
        pUrb->pVUsb->pfnFree(pUrb);
    }

    vusbRhR3ProcessFrame(pRh, true /* fCallback */);
}


/**
 * Certain control requests must not ever be forwarded to the device because
 * they are required by the vusb core in order to maintain the vusb internal
 * data structures.
 */
DECLINLINE(bool) vusbUrbIsRequestSafe(PCVUSBSETUP pSetup, PVUSBURB pUrb)
{
    if ((pSetup->bmRequestType & VUSB_REQ_MASK) != VUSB_REQ_STANDARD)
        return true;

    switch (pSetup->bRequest)
    {
        case VUSB_REQ_CLEAR_FEATURE:
            return  pUrb->EndPt != 0                   /* not default control pipe */
                ||  pSetup->wValue != 0                /* not ENDPOINT_HALT */
                ||  !pUrb->pVUsb->pDev->pUsbIns->pReg->pfnUsbClearHaltedEndpoint; /* not special need for backend */
        case VUSB_REQ_SET_ADDRESS:
        case VUSB_REQ_SET_CONFIGURATION:
        case VUSB_REQ_GET_CONFIGURATION:
        case VUSB_REQ_SET_INTERFACE:
        case VUSB_REQ_GET_INTERFACE:
            return false;

        /*
         * If the device wishes it, we'll use the cached device and
         * configuration descriptors.  (We return false when we want to use the
         * cache. Yeah, it's a bit weird to read.)
         */
        case VUSB_REQ_GET_DESCRIPTOR:
            if (    !pUrb->pVUsb->pDev->pDescCache->fUseCachedDescriptors
                ||  (pSetup->bmRequestType & VUSB_RECIP_MASK) != VUSB_TO_DEVICE)
                return true;
            switch (pSetup->wValue >> 8)
            {
                case VUSB_DT_DEVICE:
                case VUSB_DT_CONFIG:
                    return false;
                case VUSB_DT_STRING:
                    return !pUrb->pVUsb->pDev->pDescCache->fUseCachedStringsDescriptors;
                default:
                    return true;
            }

        default:
            return true;
    }
}


/**
 * Queues an URB for asynchronous transfer.
 * A list of asynchronous URBs is kept by the roothub.
 *
 * @returns VBox status code (from pfnUrbQueue).
 * @param   pUrb    The URB.
 */
int vusbUrbQueueAsyncRh(PVUSBURB pUrb)
{
#ifdef LOG_ENABLED
    vusbUrbTrace(pUrb, "vusbUrbQueueAsyncRh", false);
#endif

    /* Immediately return in case of error.
     * XXX There is still a race: The Rh might vanish after this point! */
    PVUSBDEV pDev = pUrb->pVUsb->pDev;
    PVUSBROOTHUB pRh = vusbDevGetRh(pDev);
    if (!pRh)
    {
        Log(("vusbUrbQueueAsyncRh returning VERR_OBJECT_DESTROYED\n"));
        return VERR_OBJECT_DESTROYED;
    }

    RTCritSectEnter(&pDev->CritSectAsyncUrbs);
    int rc = pDev->pUsbIns->pReg->pfnUrbQueue(pDev->pUsbIns, pUrb);
    if (RT_FAILURE(rc))
    {
        LogFlow(("%s: vusbUrbQueueAsyncRh: returns %Rrc (queue_urb)\n", pUrb->pszDesc, rc));
        RTCritSectLeave(&pDev->CritSectAsyncUrbs);
        return rc;
    }

    ASMAtomicIncU32(&pDev->aPipes[pUrb->EndPt].async);

    /* Queue the Urb on the roothub */
    RTListAppend(&pDev->LstAsyncUrbs, &pUrb->pVUsb->NdLst);
    RTCritSectLeave(&pDev->CritSectAsyncUrbs);

    return VINF_SUCCESS;
}


/**
 * Send a control message *synchronously*.
 * @return
 */
static void vusbMsgSubmitSynchronously(PVUSBURB pUrb, bool fSafeRequest)
{
    PVUSBDEV        pDev   = pUrb->pVUsb->pDev;
    Assert(pDev);
    PVUSBPIPE       pPipe  = &pDev->aPipes[pUrb->EndPt];
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;
    PVUSBSETUP      pSetup = pExtra->pMsg;
    LogFlow(("%s: vusbMsgSubmitSynchronously: pDev=%p[%s]\n", pUrb->pszDesc, pDev, pDev->pUsbIns ? pDev->pUsbIns->pszName : ""));

    uint8_t *pbData = (uint8_t *)pExtra->pMsg + sizeof(*pSetup);
    uint32_t cbData = pSetup->wLength;
    bool    fOk = false;
    if (!fSafeRequest)
        fOk = vusbDevStandardRequest(pDev, pUrb->EndPt, pSetup, pbData, &cbData);
    else
        AssertMsgFailed(("oops\n"));

    pUrb->enmState = VUSBURBSTATE_REAPED;
    if (fOk)
    {
        pSetup->wLength = cbData;
        pUrb->enmStatus = VUSBSTATUS_OK;
        pExtra->fOk = true;
    }
    else
    {
        pUrb->enmStatus = VUSBSTATUS_STALL;
        pExtra->fOk = false;
    }
    pExtra->cbLeft = cbData; /* used by IN only */

    vusbCtrlCompletion(pUrb);
    vusbUrbCompletionRh(pUrb);

    /*
     * 'Free' the message URB, i.e. put it back to the allocated state.
     */
    pExtra->Urb.enmState = VUSBURBSTATE_ALLOCATED;
    pExtra->Urb.fCompleting = false;
}

/**
 * Callback for dealing with device reset.
 */
void vusbMsgResetExtraData(PVUSBCTRLEXTRA pExtra)
{
    if (!pExtra)
        return;
    pExtra->enmStage = CTLSTAGE_SETUP;
    if (pExtra->Urb.enmState != VUSBURBSTATE_CANCELLED)
    {
        pExtra->Urb.enmState = VUSBURBSTATE_ALLOCATED;
        pExtra->Urb.fCompleting = false;
    }
}


/**
 * Callback to free a cancelled message URB.
 *
 * This is yet another place we're we have to performance acrobatics to
 * deal with cancelled URBs. sigh.
 *
 * The deal here is that we never free message URBs since they are integrated
 * into the message pipe state. But since cancel can leave URBs unreaped and in
 * a state which require them not to be freed, we'll have to do two things.
 * First, if a new message URB is processed we'll have to get a new message
 * pipe state. Second, we cannot just free the damn state structure because
 * that might lead to heap corruption since it might still be in-flight.
 *
 * The URB embedded into the message pipe control structure will start in an
 * ALLOCATED state. When submitted it will be go to the IN-FLIGHT state. When
 * reaped it will go from REAPED to ALLOCATED. When completed in the CANCELLED
 * state it will remain in that state (as does normal URBs).
 *
 * If a new message urb comes up while it's in the CANCELLED state, we will
 * orphan it and it will be freed here in vusbMsgFreeUrb. We indicate this
 * by setting pVUsb->pvFreeCtx to NULL.
 *
 * If we have to free the message state structure because of device destruction,
 * configuration changes, or similar, we will orphan the message pipe state in
 * the same way by setting pVUsb->pvFreeCtx to NULL and let this function free it.
 *
 * @param   pUrb
 */
static DECLCALLBACK(void) vusbMsgFreeUrb(PVUSBURB pUrb)
{
    vusbUrbAssert(pUrb);
    PVUSBCTRLEXTRA pExtra = (PVUSBCTRLEXTRA)((uint8_t *)pUrb - RT_OFFSETOF(VUSBCTRLEXTRA, Urb));
    if (    pUrb->enmState == VUSBURBSTATE_CANCELLED
        &&  !pUrb->pVUsb->pvFreeCtx)
    {
        LogFlow(("vusbMsgFreeUrb: Freeing orphan: %p (pUrb=%p)\n", pExtra, pUrb));
        RTMemFree(pExtra);
    }
    else
    {
        Assert(pUrb->pVUsb->pvFreeCtx == &pExtra->Urb);
        pUrb->enmState = VUSBURBSTATE_ALLOCATED;
        pUrb->fCompleting = false;
    }
}

/**
 * Frees the extra state data associated with a message pipe.
 *
 * @param   pExtra      The data.
 */
void vusbMsgFreeExtraData(PVUSBCTRLEXTRA pExtra)
{
    if (!pExtra)
        return;
    if (pExtra->Urb.enmState != VUSBURBSTATE_CANCELLED)
    {
        pExtra->Urb.u32Magic = 0;
        pExtra->Urb.enmState = VUSBURBSTATE_FREE;
        if (pExtra->Urb.pszDesc)
            RTStrFree(pExtra->Urb.pszDesc);
        RTMemFree(pExtra);
    }
    else
        pExtra->Urb.pVUsb->pvFreeCtx = NULL; /* see vusbMsgFreeUrb */
}

/**
 * Allocates the extra state data required for a control pipe.
 *
 * @returns Pointer to the allocated and initialized state data.
 * @returns NULL on out of memory condition.
 * @param   pUrb    A URB we can copy default data from.
 */
static PVUSBCTRLEXTRA vusbMsgAllocExtraData(PVUSBURB pUrb)
{
/** @todo reuse these? */
    PVUSBCTRLEXTRA pExtra;
    const size_t cbMax = sizeof(VUSBURBVUSBINT) + sizeof(pExtra->Urb.abData) + sizeof(VUSBSETUP);
    pExtra = (PVUSBCTRLEXTRA)RTMemAllocZ(RT_OFFSETOF(VUSBCTRLEXTRA, Urb.abData[cbMax]));
    if (pExtra)
    {
        pExtra->enmStage = CTLSTAGE_SETUP;
        //pExtra->fOk = false;
        pExtra->pMsg = (PVUSBSETUP)pExtra->Urb.abData;
        pExtra->pbCur = (uint8_t *)(pExtra->pMsg + 1);
        //pExtra->cbLeft = 0;
        pExtra->cbMax = cbMax;

        //pExtra->Urb.Dev.pvProxyUrb = NULL;
        pExtra->Urb.u32Magic = VUSBURB_MAGIC;
        pExtra->Urb.enmState = VUSBURBSTATE_ALLOCATED;
        pExtra->Urb.fCompleting = false;
#ifdef LOG_ENABLED
        RTStrAPrintf(&pExtra->Urb.pszDesc, "URB %p msg->%p", &pExtra->Urb, pUrb);
#endif
        pExtra->Urb.pVUsb = (PVUSBURBVUSB)&pExtra->Urb.abData[sizeof(pExtra->Urb.abData) + sizeof(VUSBSETUP)];
        //pExtra->Urb.pVUsb->pCtrlUrb = NULL;
        //pExtra->Urb.pVUsb->pNext = NULL;
        //pExtra->Urb.pVUsb->ppPrev = NULL;
        pExtra->Urb.pVUsb->pUrb = &pExtra->Urb;
        pExtra->Urb.pVUsb->pDev = pUrb->pVUsb->pDev;
        pExtra->Urb.pVUsb->pfnFree = vusbMsgFreeUrb;
        pExtra->Urb.pVUsb->pvFreeCtx = &pExtra->Urb;
        //pExtra->Urb.Hci = {0};
        //pExtra->Urb.Dev.pvProxyUrb = NULL;
        pExtra->Urb.DstAddress = pUrb->DstAddress;
        pExtra->Urb.EndPt = pUrb->EndPt;
        pExtra->Urb.enmType = VUSBXFERTYPE_MSG;
        pExtra->Urb.enmDir = VUSBDIRECTION_INVALID;
        //pExtra->Urb.fShortNotOk = false;
        pExtra->Urb.enmStatus = VUSBSTATUS_INVALID;
        //pExtra->Urb.cbData = 0;
        vusbUrbAssert(&pExtra->Urb);
    }
    return pExtra;
}

/**
 * Sets up the message.
 *
 * The message is associated with the pipe, in what's currently called
 * control pipe extra state data (pointed to by pPipe->pCtrl). If this
 * is a OUT message, we will no go on collecting data URB. If it's a
 * IN message, we'll send it and then queue any incoming data for the
 * URBs collecting it.
 *
 * @returns Success indicator.
 */
static bool vusbMsgSetup(PVUSBPIPE pPipe, const void *pvBuf, uint32_t cbBuf)
{
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;
    const VUSBSETUP *pSetupIn = (PVUSBSETUP)pvBuf;

    /*
     * Validate length.
     */
    if (cbBuf < sizeof(VUSBSETUP))
    {
        LogFlow(("vusbMsgSetup: pPipe=%p cbBuf=%u < %u (failure) !!!\n",
                 pPipe, cbBuf, sizeof(VUSBSETUP)));
        return false;
    }

    /*
     * Check if we've got an cancelled message URB. Allocate a new one in that case.
     */
    if (pExtra->Urb.enmState == VUSBURBSTATE_CANCELLED)
    {
        void *pvNew = RTMemDup(pExtra, RT_OFFSETOF(VUSBCTRLEXTRA, Urb.abData[pExtra->cbMax]));
        if (!pvNew)
        {
            Log(("vusbMsgSetup: out of memory!!! cbReq=%u\n", RT_OFFSETOF(VUSBCTRLEXTRA, Urb.abData[pExtra->cbMax])));
            return false;
        }
        pExtra->Urb.pVUsb->pvFreeCtx = NULL;
        LogFlow(("vusbMsgSetup: Replacing canceled pExtra=%p with %p.\n", pExtra, pvNew));
        pPipe->pCtrl = pExtra = (PVUSBCTRLEXTRA)pvNew;
        pExtra->Urb.pVUsb = (PVUSBURBVUSB)&pExtra->Urb.abData[sizeof(pExtra->Urb.abData) + sizeof(VUSBSETUP)];
        pExtra->Urb.pVUsb->pUrb = &pExtra->Urb;
        pExtra->pMsg = (PVUSBSETUP)pExtra->Urb.abData;
        pExtra->Urb.enmState = VUSBURBSTATE_ALLOCATED;
        pExtra->Urb.fCompleting = false;
    }

    /*
     * Check that we've got sufficient space in the message URB.
     */
    if (pExtra->cbMax < cbBuf + pSetupIn->wLength + sizeof(VUSBURBVUSBINT))
    {
        uint32_t cbReq = RT_ALIGN_32(cbBuf + pSetupIn->wLength + sizeof(VUSBURBVUSBINT), 1024);
        PVUSBCTRLEXTRA pNew = (PVUSBCTRLEXTRA)RTMemRealloc(pExtra, RT_OFFSETOF(VUSBCTRLEXTRA, Urb.abData[cbReq]));
        if (!pNew)
        {
            Log(("vusbMsgSetup: out of memory!!! cbReq=%u %u\n",
                 cbReq, RT_OFFSETOF(VUSBCTRLEXTRA, Urb.abData[cbReq])));
            return false;
        }
        if (pExtra != pNew)
        {
            pNew->pMsg = (PVUSBSETUP)pNew->Urb.abData;
            pExtra = pNew;
        }
        pExtra->Urb.pVUsb = (PVUSBURBVUSB)&pExtra->Urb.abData[cbBuf + pSetupIn->wLength];
        pExtra->Urb.pVUsb->pUrb = &pExtra->Urb;
        pExtra->cbMax = cbReq;
    }
    Assert(pExtra->Urb.enmState == VUSBURBSTATE_ALLOCATED);

    /*
     * Copy the setup data and prepare for data.
     */
    PVUSBSETUP pSetup = pExtra->pMsg;
    pExtra->fSubmitted      = false;
    pExtra->Urb.enmState    = VUSBURBSTATE_IN_FLIGHT;
    pExtra->pbCur           = (uint8_t *)(pSetup + 1);
    pSetup->bmRequestType   = pSetupIn->bmRequestType;
    pSetup->bRequest        = pSetupIn->bRequest;
    pSetup->wValue          = RT_LE2H_U16(pSetupIn->wValue);
    pSetup->wIndex          = RT_LE2H_U16(pSetupIn->wIndex);
    pSetup->wLength         = RT_LE2H_U16(pSetupIn->wLength);

    LogFlow(("vusbMsgSetup(%p,,%d): bmRequestType=%#04x bRequest=%#04x wValue=%#06x wIndex=%#06x wLength=0x%.4x\n",
             pPipe, cbBuf, pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
    return true;
}

/**
 * Build the message URB from the given control URB and accompanying message
 * pipe state which we grab from the device for the URB.
 *
 * @param   pUrb        The URB to submit.
 */
static void vusbMsgDoTransfer(PVUSBURB pUrb, PVUSBSETUP pSetup, PVUSBCTRLEXTRA pExtra, PVUSBPIPE pPipe)
{
    RT_NOREF(pPipe);

    /*
     * Mark this transfer as sent (cleared at setup time).
     */
    Assert(!pExtra->fSubmitted);
    pExtra->fSubmitted = true;

    /*
     * Do we have to do this synchronously?
     */
    bool fSafeRequest = vusbUrbIsRequestSafe(pSetup, pUrb);
    if (!fSafeRequest)
    {
        vusbMsgSubmitSynchronously(pUrb, fSafeRequest);
        return;
    }

    /*
     * Do it asynchronously.
     */
    LogFlow(("%s: vusbMsgDoTransfer: ep=%d pMsgUrb=%p pPipe=%p stage=%s\n",
             pUrb->pszDesc, pUrb->EndPt, &pExtra->Urb, pPipe, g_apszCtlStates[pExtra->enmStage]));
    Assert(pExtra->Urb.enmType == VUSBXFERTYPE_MSG);
    Assert(pExtra->Urb.EndPt == pUrb->EndPt);
    pExtra->Urb.enmDir  = (pSetup->bmRequestType & VUSB_DIR_TO_HOST) ? VUSBDIRECTION_IN : VUSBDIRECTION_OUT;
    pExtra->Urb.cbData  = pSetup->wLength + sizeof(*pSetup);
    pExtra->Urb.pVUsb->pCtrlUrb = pUrb;
    int rc = vusbUrbQueueAsyncRh(&pExtra->Urb);
    if (RT_FAILURE(rc))
    {
        /*
         * If we fail submitting it, will not retry but fail immediately.
         *
         * This keeps things simple. The host OS will have retried if
         * it's a proxied device, and if it's a virtual one it really means
         * it if it's failing a control message.
         */
        LogFlow(("%s: vusbMsgDoTransfer: failed submitting urb! failing it with %s (rc=%Rrc)!!!\n",
                 pUrb->pszDesc, rc == VERR_VUSB_DEVICE_NOT_ATTACHED ? "DNR" : "CRC", rc));
        pExtra->Urb.enmStatus = rc == VERR_VUSB_DEVICE_NOT_ATTACHED ? VUSBSTATUS_DNR : VUSBSTATUS_CRC;
        pExtra->Urb.enmState = VUSBURBSTATE_REAPED;
        vusbMsgCompletion(&pExtra->Urb);
    }
}

/**
 * Fails a URB request with a pipe STALL error.
 *
 * @returns VINF_SUCCESS indicating that we've completed the URB.
 * @param   pUrb    The URB in question.
 */
static int vusbMsgStall(PVUSBURB pUrb)
{
    PVUSBPIPE       pPipe = &pUrb->pVUsb->pDev->aPipes[pUrb->EndPt];
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;
    LogFlow(("%s: vusbMsgStall: pPipe=%p err=STALL stage %s->SETUP\n",
             pUrb->pszDesc, pPipe, g_apszCtlStates[pExtra->enmStage]));

    pExtra->pbCur    = NULL;
    pExtra->enmStage = CTLSTAGE_SETUP;
    pUrb->enmState = VUSBURBSTATE_REAPED;
    pUrb->enmStatus  = VUSBSTATUS_STALL;
    vusbUrbCompletionRh(pUrb);
    return VINF_SUCCESS;
}

/**
 * Submit a control message.
 *
 * Here we implement the USB defined traffic that occurs in message pipes
 * (aka control endpoints). We want to provide a single function for device
 * drivers so that they don't all have to reimplement the usb logic for
 * themselves. This means we need to keep a little bit of state information
 * because control transfers occur over multiple bus transactions. We may
 * also need to buffer data over multiple data stages.
 *
 * @returns VBox status code.
 * @param   pUrb        The URB to submit.
 */
static int vusbUrbSubmitCtrl(PVUSBURB pUrb)
{
#ifdef LOG_ENABLED
    vusbUrbTrace(pUrb, "vusbUrbSubmitCtrl", false);
#endif
    PVUSBDEV        pDev = pUrb->pVUsb->pDev;
    PVUSBPIPE       pPipe = &pDev->aPipes[pUrb->EndPt];

    RTCritSectEnter(&pPipe->CritSectCtrl);
    PVUSBCTRLEXTRA  pExtra = pPipe->pCtrl;

    if (!pExtra && !(pExtra = pPipe->pCtrl = vusbMsgAllocExtraData(pUrb)))
    {
        RTCritSectLeave(&pPipe->CritSectCtrl);
        return VERR_VUSB_NO_URB_MEMORY;
    }
    PVUSBSETUP      pSetup = pExtra->pMsg;

    if (pPipe->async)
    {
        AssertMsgFailed(("%u\n", pPipe->async));
        RTCritSectLeave(&pPipe->CritSectCtrl);
        return VERR_GENERAL_FAILURE;
    }

    /*
     * A setup packet always resets the transaction and the
     * end of data transmission is signified by change in
     * data direction.
     */
    if (pUrb->enmDir == VUSBDIRECTION_SETUP)
    {
        LogFlow(("%s: vusbUrbSubmitCtrl: pPipe=%p state %s->SETUP\n",
                 pUrb->pszDesc, pPipe, g_apszCtlStates[pExtra->enmStage]));
        pExtra->enmStage = CTLSTAGE_SETUP;
    }
    else if (   pExtra->enmStage == CTLSTAGE_DATA
                /* (the STATUS stage direction goes the other way) */
             && !!(pSetup->bmRequestType & VUSB_DIR_TO_HOST) != (pUrb->enmDir == VUSBDIRECTION_IN))
    {
        LogFlow(("%s: vusbUrbSubmitCtrl: pPipe=%p state %s->STATUS\n",
                 pUrb->pszDesc, pPipe, g_apszCtlStates[pExtra->enmStage]));
        pExtra->enmStage = CTLSTAGE_STATUS;
    }

    /*
     * Act according to the current message stage.
     */
    switch (pExtra->enmStage)
    {
        case CTLSTAGE_SETUP:
            /*
             * When stall handshake is returned, all subsequent packets
             * must generate stall until a setup packet arrives.
             */
            if (pUrb->enmDir != VUSBDIRECTION_SETUP)
            {
                Log(("%s: vusbUrbSubmitCtrl: Stall at setup stage (dir=%#x)!!\n", pUrb->pszDesc, pUrb->enmDir));
                vusbMsgStall(pUrb);
                break;
            }

            /* Store setup details, return DNR if corrupt */
            if (!vusbMsgSetup(pPipe, pUrb->abData, pUrb->cbData))
            {
                pUrb->enmState = VUSBURBSTATE_REAPED;
                pUrb->enmStatus = VUSBSTATUS_DNR;
                vusbUrbCompletionRh(pUrb);
                break;
            }
            if (pPipe->pCtrl != pExtra)
            {
                pExtra = pPipe->pCtrl;
                pSetup = pExtra->pMsg;
            }

            /* pre-buffer our output if it's device-to-host */
            if (pSetup->bmRequestType & VUSB_DIR_TO_HOST)
                vusbMsgDoTransfer(pUrb, pSetup, pExtra, pPipe);
            else if (pSetup->wLength)
            {
                LogFlow(("%s: vusbUrbSubmitCtrl: stage=SETUP - to dev: need data\n", pUrb->pszDesc));
                pUrb->enmState = VUSBURBSTATE_REAPED;
                vusbMsgSetupCompletion(pUrb);
                vusbUrbCompletionRh(pUrb);
            }
            /*
             * If there is no DATA stage, we must send it now since there are
             * no requirement of a STATUS stage.
             */
            else
            {
                LogFlow(("%s: vusbUrbSubmitCtrl: stage=SETUP - to dev: sending\n", pUrb->pszDesc));
                vusbMsgDoTransfer(pUrb, pSetup, pExtra, pPipe);
            }
            break;

        case CTLSTAGE_DATA:
        {
            /*
             * If a data stage exceeds the target buffer indicated in
             * setup return stall, if data stage returns stall there
             * will be no status stage.
             */
            uint8_t *pbData = (uint8_t *)(pExtra->pMsg + 1);
            if (&pExtra->pbCur[pUrb->cbData] > &pbData[pSetup->wLength])
            {
                if (!pSetup->wLength) /* happens during iPhone detection with iTunes (correct?) */
                {
                    Log(("%s: vusbUrbSubmitCtrl: pSetup->wLength == 0!! (iPhone)\n", pUrb->pszDesc));
                    pSetup->wLength = pUrb->cbData;
                }

                /* Variable length data transfers */
                if (    (pSetup->bmRequestType & VUSB_DIR_TO_HOST)
                    ||  pSetup->wLength == 0
                    ||  (pUrb->cbData % pSetup->wLength) == 0)  /* magic which need explaining... */
                {
                    uint8_t *pbEnd = pbData + pSetup->wLength;
                    int cbLeft = pbEnd - pExtra->pbCur;
                    LogFlow(("%s: vusbUrbSubmitCtrl: Var DATA, pUrb->cbData %d -> %d\n", pUrb->pszDesc, pUrb->cbData, cbLeft));
                    pUrb->cbData = cbLeft;
                }
                else
                {
                    Log(("%s: vusbUrbSubmitCtrl: Stall at data stage!!\n", pUrb->pszDesc));
                    vusbMsgStall(pUrb);
                    break;
                }
            }

            if (pUrb->enmDir == VUSBDIRECTION_IN)
            {
                /* put data received from the device. */
                const uint32_t cbRead = RT_MIN(pUrb->cbData, pExtra->cbLeft);
                memcpy(pUrb->abData, pExtra->pbCur, cbRead);

                /* advance */
                pExtra->pbCur += cbRead;
                if (pUrb->cbData == cbRead)
                    pExtra->cbLeft -= pUrb->cbData;
                else
                {
                    /* adjust the pUrb->cbData to reflect the number of bytes containing actual data. */
                    LogFlow(("%s: vusbUrbSubmitCtrl: adjusting last DATA pUrb->cbData, %d -> %d\n",
                             pUrb->pszDesc, pUrb->cbData, pExtra->cbLeft));
                    pUrb->cbData = cbRead;
                    pExtra->cbLeft = 0;
                }
            }
            else
            {
                /* get data for sending when completed. */
                memcpy(pExtra->pbCur, pUrb->abData, pUrb->cbData);

                /* advance */
                pExtra->pbCur += pUrb->cbData;

                /*
                 * If we've got the necessary data, we'll send it now since there are
                 * no requirement of a STATUS stage.
                 */
                if (    !pExtra->fSubmitted
                    &&  pExtra->pbCur - pbData >= pSetup->wLength)
                {
                    LogFlow(("%s: vusbUrbSubmitCtrl: stage=DATA - to dev: sending\n", pUrb->pszDesc));
                    vusbMsgDoTransfer(pUrb, pSetup, pExtra, pPipe);
                    break;
                }
            }

            pUrb->enmState = VUSBURBSTATE_REAPED;
            vusbMsgDataCompletion(pUrb);
            vusbUrbCompletionRh(pUrb);
            break;
        }

        case CTLSTAGE_STATUS:
            if (    (pSetup->bmRequestType & VUSB_DIR_TO_HOST)
                ||  pExtra->fSubmitted)
            {
                Assert(pExtra->fSubmitted);
                pUrb->enmState = VUSBURBSTATE_REAPED;
                vusbMsgStatusCompletion(pUrb);
                vusbUrbCompletionRh(pUrb);
            }
            else
            {
                LogFlow(("%s: vusbUrbSubmitCtrl: stage=STATUS - to dev: sending\n", pUrb->pszDesc));
                vusbMsgDoTransfer(pUrb, pSetup, pExtra, pPipe);
            }
            break;
    }

    RTCritSectLeave(&pPipe->CritSectCtrl);
    return VINF_SUCCESS;
}


/**
 * Submit a interrupt URB.
 *
 * @returns VBox status code.
 * @param   pUrb        The URB to submit.
 */
static int vusbUrbSubmitInterrupt(PVUSBURB pUrb)
{
    LogFlow(("%s: vusbUrbSubmitInterrupt: (sync)\n", pUrb->pszDesc));
    return vusbUrbQueueAsyncRh(pUrb);
}


/**
 * Submit a bulk URB.
 *
 * @returns VBox status code.
 * @param   pUrb        The URB to submit.
 */
static int vusbUrbSubmitBulk(PVUSBURB pUrb)
{
    LogFlow(("%s: vusbUrbSubmitBulk: (async)\n", pUrb->pszDesc));
    return vusbUrbQueueAsyncRh(pUrb);
}


/**
 * Submit an isochronous URB.
 *
 * @returns VBox status code.
 * @param   pUrb        The URB to submit.
 */
static int vusbUrbSubmitIsochronous(PVUSBURB pUrb)
{
    LogFlow(("%s: vusbUrbSubmitIsochronous: (async)\n", pUrb->pszDesc));
    return vusbUrbQueueAsyncRh(pUrb);
}


/**
 * Fail a URB with a 'hard-error' sort of error.
 *
 * @return VINF_SUCCESS (the Urb status indicates the error).
 * @param   pUrb    The URB.
 */
int vusbUrbSubmitHardError(PVUSBURB pUrb)
{
    /* FIXME: Find out the correct return code from the spec */
    pUrb->enmState = VUSBURBSTATE_REAPED;
    pUrb->enmStatus = VUSBSTATUS_DNR;
    vusbUrbCompletionRh(pUrb);
    return VINF_SUCCESS;
}


/**
 * Submit a URB.
 */
int vusbUrbSubmit(PVUSBURB pUrb)
{
    vusbUrbAssert(pUrb);
    Assert(pUrb->enmState == VUSBURBSTATE_ALLOCATED);
    PVUSBDEV pDev = pUrb->pVUsb->pDev;
    PVUSBPIPE pPipe = NULL;
    Assert(pDev);

    /*
     * Check that the device is in a valid state.
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (enmState == VUSB_DEVICE_STATE_RESET)
    {
        LogRel(("VUSB: %s: power off ignored, the device is resetting!\n", pDev->pUsbIns->pszName));
        pUrb->enmStatus = VUSBSTATUS_DNR;
        /* This will postpone the TDs until we're done with the resetting. */
        return VERR_VUSB_DEVICE_IS_RESETTING;
    }

#ifdef LOG_ENABLED
    /* stamp it */
    pUrb->pVUsb->u64SubmitTS = RTTimeNanoTS();
#endif

    /** @todo Check max packet size here too? */

    /*
     * Validate the pipe.
     */
    if (pUrb->EndPt >= VUSB_PIPE_MAX)
    {
        Log(("%s: pDev=%p[%s]: SUBMIT: ep %i >= %i!!!\n", pUrb->pszDesc, pDev, pDev->pUsbIns->pszName, pUrb->EndPt, VUSB_PIPE_MAX));
        return vusbUrbSubmitHardError(pUrb);
    }
    PCVUSBDESCENDPOINTEX pEndPtDesc;
    switch (pUrb->enmDir)
    {
        case VUSBDIRECTION_IN:
            pEndPtDesc = pDev->aPipes[pUrb->EndPt].in;
            pPipe = &pDev->aPipes[pUrb->EndPt];
            break;
        case VUSBDIRECTION_SETUP:
        case VUSBDIRECTION_OUT:
        default:
            pEndPtDesc = pDev->aPipes[pUrb->EndPt].out;
            pPipe = &pDev->aPipes[pUrb->EndPt];
            break;
    }
    if (!pEndPtDesc)
    {
        Log(("%s: pDev=%p[%s]: SUBMIT: no endpoint!!! dir=%s e=%i\n",
             pUrb->pszDesc, pDev, pDev->pUsbIns->pszName, vusbUrbDirName(pUrb->enmDir), pUrb->EndPt));
        return vusbUrbSubmitHardError(pUrb);
    }

    /*
     * Check for correct transfer types.
     * Our type codes are the same - what a coincidence.
     */
    if ((pEndPtDesc->Core.bmAttributes & 0x3) != pUrb->enmType)
    {
        /* Bulk and interrupt transfers are identical on the bus level (the only difference
         * is in how they are scheduled by the HCD/HC) and need an exemption.
         * Atheros AR9271 is a known offender; its configuration descriptors include
         * interrupt endpoints, but drivers (Win7/8, Linux kernel pre-3.05) treat them
         * as bulk endpoints.
         */
        if (   (pUrb->enmType == VUSBXFERTYPE_BULK && (pEndPtDesc->Core.bmAttributes & 0x3) == VUSBXFERTYPE_INTR)
            || (pUrb->enmType == VUSBXFERTYPE_INTR && (pEndPtDesc->Core.bmAttributes & 0x3) == VUSBXFERTYPE_BULK))
        {
            Log2(("%s: pDev=%p[%s]: SUBMIT: mixing bulk/interrupt transfers on DstAddress=%i ep=%i dir=%s\n",
                  pUrb->pszDesc, pDev, pDev->pUsbIns->pszName,
                  pUrb->DstAddress, pUrb->EndPt, vusbUrbDirName(pUrb->enmDir)));
        }
        else
        {
            Log(("%s: pDev=%p[%s]: SUBMIT: %s transfer requested for %#x endpoint on DstAddress=%i ep=%i dir=%s\n",
                 pUrb->pszDesc, pDev, pDev->pUsbIns->pszName, vusbUrbTypeName(pUrb->enmType), pEndPtDesc->Core.bmAttributes,
                 pUrb->DstAddress, pUrb->EndPt, vusbUrbDirName(pUrb->enmDir)));
            return vusbUrbSubmitHardError(pUrb);
        }
    }

    /*
     * If there's a URB in the read-ahead buffer, use it.
     */
    int rc;

    if (pDev->hSniffer)
    {
        rc = VUSBSnifferRecordEvent(pDev->hSniffer, pUrb, VUSBSNIFFEREVENT_SUBMIT);
        if (RT_FAILURE(rc))
            LogRel(("VUSB: Capturing URB submit event failed with %Rrc\n", rc));
    }

#ifdef VBOX_WITH_USB
    if (pPipe && pPipe->hBuffer)
    {
        rc = vusbBufferedPipeSubmitUrb(pPipe->hBuffer, pUrb);
        return rc;
    }
#endif

    /*
     * Take action based on type.
     */
    pUrb->enmState = VUSBURBSTATE_IN_FLIGHT;
    switch (pUrb->enmType)
    {
        case VUSBXFERTYPE_CTRL:
            rc = vusbUrbSubmitCtrl(pUrb);
            break;
        case VUSBXFERTYPE_BULK:
            rc = vusbUrbSubmitBulk(pUrb);
            break;
        case VUSBXFERTYPE_INTR:
            rc = vusbUrbSubmitInterrupt(pUrb);
            break;
        case VUSBXFERTYPE_ISOC:
            rc = vusbUrbSubmitIsochronous(pUrb);
            break;
        default:
            AssertMsgFailed(("Unexpected pUrb type %d\n", pUrb->enmType));
            return vusbUrbSubmitHardError(pUrb);
    }

    /*
     * The device was detached, so we fail everything.
     * (We should really detach and destroy the device, but we'll have to wait till Main reacts.)
     */
    if (rc == VERR_VUSB_DEVICE_NOT_ATTACHED)
        rc = vusbUrbSubmitHardError(pUrb);
    /*
     * We don't increment error count if async URBs are in flight, in
     * this case we just assume we need to throttle back, this also
     * makes sure we don't halt bulk endpoints at the wrong time.
     */
    else if (   RT_FAILURE(rc)
             && !ASMAtomicReadU32(&pDev->aPipes[pUrb->EndPt].async)
             /* && pUrb->enmType == VUSBXFERTYPE_BULK ?? */
             && !vusbUrbErrorRh(pUrb))
    {
        /* don't retry it anymore. */
        pUrb->enmState = VUSBURBSTATE_REAPED;
        pUrb->enmStatus = VUSBSTATUS_CRC;
        vusbUrbCompletionRh(pUrb);
        return VINF_SUCCESS;
    }

    return rc;
}


/**
 * Reap in-flight URBs.
 *
 * @param   pUrbLst     Pointer to the head of the URB list.
 * @param   cMillies    Number of milliseconds to block in each reap operation.
 *                      Use 0 to not block at all.
 */
void vusbUrbDoReapAsync(PRTLISTANCHOR pUrbLst, RTMSINTERVAL cMillies)
{
    PVUSBURBVUSB pVUsbUrb = RTListGetFirst(pUrbLst, VUSBURBVUSBINT, NdLst);
    while (pVUsbUrb)
    {
        vusbUrbAssert(pVUsbUrb->pUrb);
        PVUSBURBVUSB pVUsbUrbNext = RTListGetNext(pUrbLst, pVUsbUrb, VUSBURBVUSBINT, NdLst);
        PVUSBDEV pDev = pVUsbUrb->pDev;

        /* Don't touch resetting devices - paranoid safety precaution. */
        if (vusbDevGetState(pDev) != VUSB_DEVICE_STATE_RESET)
        {
            /*
             * Reap most URBs pending on a single device.
             */
            PVUSBURB pRipe;

            /**
             * This is workaround for race(should be fixed) detach on one EMT thread and frame boundary timer on other
             * and leaked URBs (shouldn't be affected by leaked URBs).
             */
            Assert(pDev->pUsbIns);
            while (   pDev->pUsbIns
                   && ((pRipe = pDev->pUsbIns->pReg->pfnUrbReap(pDev->pUsbIns, cMillies)) != NULL))
            {
                vusbUrbAssert(pRipe);
                if (pRipe == pVUsbUrbNext->pUrb)
                    pVUsbUrbNext = RTListGetNext(pUrbLst, pVUsbUrb, VUSBURBVUSBINT, NdLst);
                vusbUrbRipe(pRipe);
            }
        }

        /* next */
        pVUsbUrb = pVUsbUrbNext;
    }
}

/**
 * Reap URBs on a per device level.
 *
 * @returns nothing.
 * @param   pDev        The device instance to reap URBs for.
 * @param   cMillies    Number of milliseconds to block in each reap operation.
 *                      Use 0 to not block at all.
 */
void vusbUrbDoReapAsyncDev(PVUSBDEV pDev, RTMSINTERVAL cMillies)
{
    Assert(pDev->enmState != VUSB_DEVICE_STATE_RESET);

    /*
     * Reap most URBs pending on a single device.
     */
    PVUSBURB pRipe;

    /**
     * This is workaround for race(should be fixed) detach on one EMT thread and frame boundary timer on other
     * and leaked URBs (shouldn't be affected by leaked URBs).
     */

    if (ASMAtomicXchgBool(&pDev->fWokenUp, false))
        return;

    Assert(pDev->pUsbIns);
    while (   pDev->pUsbIns
           && ((pRipe = pDev->pUsbIns->pReg->pfnUrbReap(pDev->pUsbIns, cMillies)) != NULL))
    {
        vusbUrbAssert(pRipe);
        vusbUrbRipe(pRipe);
        if (ASMAtomicXchgBool(&pDev->fWokenUp, false))
            break;
    }
}

/**
 * Completes the URB.
 */
static void vusbUrbCompletion(PVUSBURB pUrb)
{
    Assert(pUrb->pVUsb->pDev->aPipes);
    ASMAtomicDecU32(&pUrb->pVUsb->pDev->aPipes[pUrb->EndPt].async);

    if (pUrb->enmState == VUSBURBSTATE_REAPED)
        vusbUrbUnlink(pUrb);
#ifdef VBOX_WITH_USB
    // Read-ahead URBs are handled differently
    if (pUrb->pVUsb->pvBuffered)
        vusbBufferedPipeCompleteUrb(pUrb);
    else
#endif
        vusbUrbCompletionRh(pUrb);
}

/**
 * The worker for vusbUrbCancel() which is executed on the I/O thread.
 *
 * @returns IPRT status code.
 * @param   pUrb        The URB to cancel.
 * @param   enmMode     The way the URB should be canceled.
 */
DECLHIDDEN(int) vusbUrbCancelWorker(PVUSBURB pUrb, CANCELMODE enmMode)
{
    vusbUrbAssert(pUrb);
#ifdef VBOX_WITH_STATISTICS
    PVUSBROOTHUB pRh = vusbDevGetRh(pUrb->pVUsb->pDev);
#endif
    if (pUrb->enmState == VUSBURBSTATE_IN_FLIGHT)
    {
        LogFlow(("%s: vusbUrbCancel: Canceling in-flight\n", pUrb->pszDesc));
        STAM_COUNTER_INC(&pRh->Total.StatUrbsCancelled);
        if (pUrb->enmType != VUSBXFERTYPE_MSG)
        {
            STAM_STATS({Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));});
            STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsCancelled);
        }

        pUrb->enmState = VUSBURBSTATE_CANCELLED;
        PPDMUSBINS pUsbIns = pUrb->pVUsb->pDev->pUsbIns;
        pUsbIns->pReg->pfnUrbCancel(pUsbIns, pUrb);
        Assert(pUrb->enmState == VUSBURBSTATE_CANCELLED || pUrb->enmState == VUSBURBSTATE_REAPED);

        pUrb->enmStatus = VUSBSTATUS_CRC;
        vusbUrbCompletion(pUrb);
    }
    else if (pUrb->enmState == VUSBURBSTATE_REAPED)
    {
        LogFlow(("%s: vusbUrbCancel: Canceling reaped urb\n", pUrb->pszDesc));
        STAM_COUNTER_INC(&pRh->Total.StatUrbsCancelled);
        if (pUrb->enmType != VUSBXFERTYPE_MSG)
        {
            STAM_STATS({Assert(pUrb->enmType >= 0 && pUrb->enmType < (int)RT_ELEMENTS(pRh->aTypes));});
            STAM_COUNTER_INC(&pRh->aTypes[pUrb->enmType].StatUrbsCancelled);
        }

        pUrb->enmStatus = VUSBSTATUS_CRC;
        vusbUrbCompletion(pUrb);
    }
    else
    {
        AssertMsg(pUrb->enmState == VUSBURBSTATE_CANCELLED, ("Invalid state %d, pUrb=%p\n", pUrb->enmState, pUrb));
        switch (enmMode)
        {
            default:
                AssertMsgFailed(("Invalid cancel mode\n"));
                RT_FALL_THRU();
            case CANCELMODE_FAIL:
                pUrb->enmStatus = VUSBSTATUS_CRC;
                break;
            case CANCELMODE_UNDO:
                pUrb->enmStatus = VUSBSTATUS_UNDO;
                break;

        }
    }
    return VINF_SUCCESS;
}

/**
 * Cancels an URB with CRC failure.
 *
 * Cancelling an URB is a tricky thing. The USBProxy backend can not
 * all cancel it and we must keep the URB around until it's ripe and
 * can be reaped the normal way. However, we must complete the URB
 * now, before leaving this function. This is not nice. sigh.
 *
 * This function will cancel the URB if it's in-flight and complete
 * it. The device will in its pfnCancel method be given the chance to
 * say that the URB doesn't need reaping and should be unlinked.
 *
 * An URB which is in the cancel state after pfnCancel will remain in that
 * state and in the async list until its reaped. When it's finally reaped
 * it will be unlinked and freed without doing any completion.
 *
 * There are different modes of canceling an URB. When devices are being
 * disconnected etc., they will be completed with an error (CRC). However,
 * when the HC needs to temporarily halt communication with a device, the
 * URB/TD must be left alone if possible.
 *
 * @param   pUrb        The URB to cancel.
 * @param   mode        The way the URB should be canceled.
 */
void vusbUrbCancel(PVUSBURB pUrb, CANCELMODE mode)
{
    int rc = vusbDevIoThreadExecSync(pUrb->pVUsb->pDev, (PFNRT)vusbUrbCancelWorker, 2, pUrb, mode);
    AssertRC(rc);
}


/**
 * Async version of vusbUrbCancel() - doesn't wait for the cancelling to be complete.
 */
void vusbUrbCancelAsync(PVUSBURB pUrb, CANCELMODE mode)
{
    /* Don't try to cancel the URB when completion is in progress at the moment. */
    if (!ASMAtomicXchgBool(&pUrb->fCompleting, true))
    {
        int rc = vusbDevIoThreadExec(pUrb->pVUsb->pDev, 0 /* fFlags */, (PFNRT)vusbUrbCancelWorker, 2, pUrb, mode);
        AssertRC(rc);
    }
}


/**
 * Deals with a ripe URB (i.e. after reaping it).
 *
 * If an URB is in the reaped or in-flight state, we'll
 * complete it. If it's cancelled, we'll simply free it.
 * Any other states should never get here.
 *
 * @param   pUrb    The URB.
 */
void vusbUrbRipe(PVUSBURB pUrb)
{
    if (    pUrb->enmState == VUSBURBSTATE_IN_FLIGHT
        ||  pUrb->enmState == VUSBURBSTATE_REAPED)
    {
        pUrb->enmState = VUSBURBSTATE_REAPED;
        if (!ASMAtomicXchgBool(&pUrb->fCompleting, true))
            vusbUrbCompletion(pUrb);
    }
    else if (pUrb->enmState == VUSBURBSTATE_CANCELLED)
    {
        vusbUrbUnlink(pUrb);
        LogFlow(("%s: vusbUrbRipe: Freeing cancelled URB\n", pUrb->pszDesc));
        pUrb->pVUsb->pfnFree(pUrb);
    }
    else
        AssertMsgFailed(("Invalid URB state %d; %s\n", pUrb->enmState, pUrb->pszDesc));
}


/*
 * Local Variables:
 *  mode: c
 *  c-file-style: "bsd"
 *  c-basic-offset: 4
 *  tab-width: 4
 *  indent-tabs-mode: s
 * End:
 */

