/* $Id: VUSBDevice.cpp $ */
/** @file
 * Virtual USB - Device.
 */

/*
 * 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 <VBox/log.h>
#include <iprt/alloc.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 "VUSBInternal.h"

#include "VUSBSniffer.h"


/*********************************************************************************************************************************
*   Structures and Typedefs                                                                                                      *
*********************************************************************************************************************************/
/**
 * Argument package of vusbDevResetThread().
 */
typedef struct vusb_reset_args
{
    /** Pointer to the device which is being reset. */
    PVUSBDEV            pDev;
    /** The reset return code. */
    int                 rc;
    /** Pointer to the completion callback. */
    PFNVUSBRESETDONE    pfnDone;
    /** User argument to pfnDone. */
    void               *pvUser;
} VUSBRESETARGS, *PVUSBRESETARGS;


/*********************************************************************************************************************************
*   Global Variables                                                                                                             *
*********************************************************************************************************************************/
/** Default message pipe. */
const VUSBDESCENDPOINTEX g_Endpoint0 =
{
    {
        /* .bLength = */            VUSB_DT_ENDPOINT_MIN_LEN,
        /* .bDescriptorType = */    VUSB_DT_ENDPOINT,
        /* .bEndpointAddress = */   0,
        /* .bmAttributes = */       0,
        /* .wMaxPacketSize = */     64,
        /* .bInterval = */          0
    },
    NULL
};

/** Default configuration. */
const VUSBDESCCONFIGEX g_Config0 =
{
    {
        /* .bLength = */            VUSB_DT_CONFIG_MIN_LEN,
        /* .bDescriptorType = */    VUSB_DT_CONFIG,
        /* .WTotalLength = */       0, /* (auto-calculated) */
        /* .bNumInterfaces = */     0,
        /* .bConfigurationValue =*/ 0,
        /* .iConfiguration = */     0,
        /* .bmAttributes = */       0x80,
        /* .MaxPower = */           14
    },
    NULL,
    NULL
};



static PCVUSBDESCCONFIGEX vusbDevFindCfgDesc(PVUSBDEV pDev, int iCfg)
{
    if (iCfg == 0)
        return &g_Config0;

    for (unsigned i = 0; i < pDev->pDescCache->pDevice->bNumConfigurations; i++)
        if (pDev->pDescCache->paConfigs[i].Core.bConfigurationValue == iCfg)
            return &pDev->pDescCache->paConfigs[i];
    return NULL;
}

static PVUSBINTERFACESTATE vusbDevFindIfState(PVUSBDEV pDev, int iIf)
{
    for (unsigned i = 0; i < pDev->pCurCfgDesc->Core.bNumInterfaces; i++)
        if (pDev->paIfStates[i].pIf->paSettings[0].Core.bInterfaceNumber == iIf)
            return &pDev->paIfStates[i];
    return NULL;
}

static PCVUSBDESCINTERFACEEX vusbDevFindAltIfDesc(PCVUSBINTERFACESTATE pIfState, int iAlt)
{
    for (uint32_t i = 0; i < pIfState->pIf->cSettings; i++)
        if (pIfState->pIf->paSettings[i].Core.bAlternateSetting == iAlt)
            return &pIfState->pIf->paSettings[i];
    return NULL;
}

void vusbDevMapEndpoint(PVUSBDEV pDev, PCVUSBDESCENDPOINTEX pEndPtDesc)
{
    uint8_t i8Addr = pEndPtDesc->Core.bEndpointAddress & 0xF;
    PVUSBPIPE pPipe = &pDev->aPipes[i8Addr];
    LogFlow(("vusbDevMapEndpoint: pDev=%p[%s] pEndPtDesc=%p{.bEndpointAddress=%#x, .bmAttributes=%#x} p=%p stage %s->SETUP\n",
             pDev, pDev->pUsbIns->pszName, pEndPtDesc, pEndPtDesc->Core.bEndpointAddress, pEndPtDesc->Core.bmAttributes,
             pPipe, g_apszCtlStates[pPipe->pCtrl ? pPipe->pCtrl->enmStage : 3]));

    if ((pEndPtDesc->Core.bmAttributes & 0x3) == 0)
    {
        Log(("vusb: map message pipe on address %u\n", i8Addr));
        pPipe->in  = pEndPtDesc;
        pPipe->out = pEndPtDesc;
    }
    else if (pEndPtDesc->Core.bEndpointAddress & 0x80)
    {
        Log(("vusb: map input pipe on address %u\n", i8Addr));
        pPipe->in = pEndPtDesc;
    }
    else
    {
        Log(("vusb: map output pipe on address %u\n", i8Addr));
        pPipe->out = pEndPtDesc;

#if 0
        if ((pEndPtDesc->Core.bmAttributes & 0x03) == 1)
        {
            int rc = vusbBufferedPipeCreate(pDev, pPipe, VUSBDIRECTION_OUT, pDev->pUsbIns->enmSpeed,
                                            32 /* cLatencyMs*/, &pPipe->hBuffer);
            if (RT_SUCCESS(rc))
                LogRel(("VUSB: Created a buffered pipe for isochronous output endpoint\n"));
            else
                LogRel(("VUSB: Failed to create a buffered pipe for isochronous output endpoint with rc=%Rrc\n", rc));
        }
#endif
    }

    if (pPipe->pCtrl)
    {
        vusbMsgFreeExtraData(pPipe->pCtrl);
        pPipe->pCtrl = NULL;
    }
}

static void unmap_endpoint(PVUSBDEV pDev, PCVUSBDESCENDPOINTEX pEndPtDesc)
{
    uint8_t     EndPt = pEndPtDesc->Core.bEndpointAddress & 0xF;
    PVUSBPIPE   pPipe = &pDev->aPipes[EndPt];
    LogFlow(("unmap_endpoint: pDev=%p[%s] pEndPtDesc=%p{.bEndpointAddress=%#x, .bmAttributes=%#x} p=%p stage %s->SETUP\n",
             pDev, pDev->pUsbIns->pszName, pEndPtDesc, pEndPtDesc->Core.bEndpointAddress, pEndPtDesc->Core.bmAttributes,
             pPipe, g_apszCtlStates[pPipe->pCtrl ? pPipe->pCtrl->enmStage : 3]));

    if ((pEndPtDesc->Core.bmAttributes & 0x3) == 0)
    {
        Log(("vusb: unmap MSG pipe from address %u (%#x)\n", EndPt, pEndPtDesc->Core.bEndpointAddress));
        pPipe->in = NULL;
        pPipe->out = NULL;
    }
    else if (pEndPtDesc->Core.bEndpointAddress & 0x80)
    {
        Log(("vusb: unmap IN pipe from address %u (%#x)\n", EndPt, pEndPtDesc->Core.bEndpointAddress));
        pPipe->in = NULL;

        /* Terminate the pipe buffer if created. */
        if (pPipe->hBuffer)
        {
            vusbBufferedPipeDestroy(pPipe->hBuffer);
            pPipe->hBuffer = NULL;
        }
    }
    else
    {
        Log(("vusb: unmap OUT pipe from address %u (%#x)\n", EndPt, pEndPtDesc->Core.bEndpointAddress));
        pPipe->out = NULL;

        /* Terminate the pipe buffer if created. */
        if (pPipe->hBuffer)
        {
            vusbBufferedPipeDestroy(pPipe->hBuffer);
            pPipe->hBuffer = NULL;
        }
    }

    if (pPipe->pCtrl)
    {
        vusbMsgFreeExtraData(pPipe->pCtrl);
        pPipe->pCtrl = NULL;
    }
}

static void map_interface(PVUSBDEV pDev, PCVUSBDESCINTERFACEEX pIfDesc)
{
    LogFlow(("map_interface: pDev=%p[%s] pIfDesc=%p:{.iInterface=%d, .bAlternateSetting=%d}\n",
             pDev, pDev->pUsbIns->pszName, pIfDesc, pIfDesc->Core.iInterface, pIfDesc->Core.bAlternateSetting));

    for (unsigned i = 0; i < pIfDesc->Core.bNumEndpoints; i++)
    {
        if ((pIfDesc->paEndpoints[i].Core.bEndpointAddress & 0xF) == VUSB_PIPE_DEFAULT)
            Log(("vusb: Endpoint 0x%x on interface %u.%u tried to override the default message pipe!!!\n",
                pIfDesc->paEndpoints[i].Core.bEndpointAddress, pIfDesc->Core.bInterfaceNumber, pIfDesc->Core.bAlternateSetting));
        else
            vusbDevMapEndpoint(pDev, &pIfDesc->paEndpoints[i]);
    }
}


/**
 * Worker that resets the pipe data on select config and detach.
 *
 * This leaves the critical section unmolested
 *
 * @param   pPipe               The pipe which data should be reset.
 */
static void vusbDevResetPipeData(PVUSBPIPE pPipe)
{
    vusbMsgFreeExtraData(pPipe->pCtrl);
    pPipe->pCtrl = NULL;

    if (pPipe->hBuffer)
    {
        vusbBufferedPipeDestroy(pPipe->hBuffer);
        pPipe->hBuffer = NULL;
    }

    RT_ZERO(pPipe->in);
    RT_ZERO(pPipe->out);
    pPipe->async = 0;
}


bool vusbDevDoSelectConfig(PVUSBDEV pDev, PCVUSBDESCCONFIGEX pCfgDesc)
{
    LogFlow(("vusbDevDoSelectConfig: pDev=%p[%s] pCfgDesc=%p:{.iConfiguration=%d}\n",
             pDev, pDev->pUsbIns->pszName, pCfgDesc, pCfgDesc->Core.iConfiguration));

    /*
     * Clean up all pipes and interfaces.
     */
    unsigned i;
    for (i = 0; i < VUSB_PIPE_MAX; i++)
        if (i != VUSB_PIPE_DEFAULT)
            vusbDevResetPipeData(&pDev->aPipes[i]);
    memset(pDev->paIfStates, 0, pCfgDesc->Core.bNumInterfaces * sizeof(pDev->paIfStates[0]));

    /*
     * Map in the default setting for every interface.
     */
    for (i = 0; i < pCfgDesc->Core.bNumInterfaces; i++)
    {
        PCVUSBINTERFACE pIf;
        struct vusb_interface_state *pIfState;

        pIf = &pCfgDesc->paIfs[i];
        pIfState = &pDev->paIfStates[i];
        pIfState->pIf = pIf;

        /*
         * Find the 0 setting, if it is not present we just use
         * the lowest numbered one.
         */
        for (uint32_t j = 0; j < pIf->cSettings; j++)
        {
            if (    !pIfState->pCurIfDesc
                ||  pIf->paSettings[j].Core.bAlternateSetting < pIfState->pCurIfDesc->Core.bAlternateSetting)
                pIfState->pCurIfDesc = &pIf->paSettings[j];
            if (pIfState->pCurIfDesc->Core.bAlternateSetting == 0)
                break;
        }

        if (pIfState->pCurIfDesc)
            map_interface(pDev, pIfState->pCurIfDesc);
    }

    pDev->pCurCfgDesc = pCfgDesc;

    if (pCfgDesc->Core.bmAttributes & 0x40)
        pDev->u16Status |= (1 << VUSB_DEV_SELF_POWERED);
    else
        pDev->u16Status &= ~(1 << VUSB_DEV_SELF_POWERED);

    return true;
}

/**
 * Standard device request: SET_CONFIGURATION
 * @returns success indicator.
 */
static bool vusbDevStdReqSetConfig(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(EndPt, pbBuf, pcbBuf);
    unsigned iCfg = pSetup->wValue & 0xff;

    if ((pSetup->bmRequestType & VUSB_RECIP_MASK) != VUSB_TO_DEVICE)
    {
        Log(("vusb: error: %s: SET_CONFIGURATION - invalid request (dir) !!!\n", pDev->pUsbIns->pszName));
        return false;
    }

    /*
     * Check that the device is in a valid state.
     * (The caller has already checked that it's not being reset.)
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (enmState == VUSB_DEVICE_STATE_DEFAULT)
    {
        LogFlow(("vusbDevStdReqSetConfig: %s: default dev state !!?\n", pDev->pUsbIns->pszName));
        return false;
    }

    PCVUSBDESCCONFIGEX pNewCfgDesc = vusbDevFindCfgDesc(pDev, iCfg);
    if (!pNewCfgDesc)
    {
        Log(("vusb: error: %s: config %i not found !!!\n", pDev->pUsbIns->pszName, iCfg));
        return false;
    }

    if (iCfg == 0)
        vusbDevSetState(pDev, VUSB_DEVICE_STATE_ADDRESS);
    else
        vusbDevSetState(pDev, VUSB_DEVICE_STATE_CONFIGURED);
    if (pDev->pUsbIns->pReg->pfnUsbSetConfiguration)
    {
        RTCritSectEnter(&pDev->pHub->pRootHub->CritSectDevices);
        int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)pDev->pUsbIns->pReg->pfnUsbSetConfiguration, 5,
                                         pDev->pUsbIns, pNewCfgDesc->Core.bConfigurationValue,
                                         pDev->pCurCfgDesc, pDev->paIfStates, pNewCfgDesc);
        RTCritSectLeave(&pDev->pHub->pRootHub->CritSectDevices);
        if (RT_FAILURE(rc))
        {
            Log(("vusb: error: %s: failed to set config %i (%Rrc) !!!\n", pDev->pUsbIns->pszName, iCfg, rc));
            return false;
        }
    }
    Log(("vusb: %p[%s]: SET_CONFIGURATION: Selected config %u\n", pDev, pDev->pUsbIns->pszName, iCfg));
    return vusbDevDoSelectConfig(pDev, pNewCfgDesc);
}


/**
 * Standard device request: GET_CONFIGURATION
 * @returns success indicator.
 */
static bool vusbDevStdReqGetConfig(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(EndPt);
    if ((pSetup->bmRequestType & VUSB_RECIP_MASK) != VUSB_TO_DEVICE)
    {
        Log(("vusb: error: %s: GET_CONFIGURATION - invalid request (dir) !!!\n", pDev->pUsbIns->pszName));
        return false;
    }

    /*
     * Check that the device is in a valid state.
     * (The caller has already checked that it's not being reset.)
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (    enmState != VUSB_DEVICE_STATE_CONFIGURED
        &&  enmState != VUSB_DEVICE_STATE_ADDRESS)
    {
        LogFlow(("vusbDevStdReqGetConfig: error: %s: invalid device state %d!!!\n", pDev->pUsbIns->pszName, enmState));
        return false;
    }

    if (*pcbBuf < 1)
    {
        LogFlow(("vusbDevStdReqGetConfig: %s: no space for data!\n", pDev->pUsbIns->pszName));
        return true;
    }

    uint8_t iCfg;
    if (enmState == VUSB_DEVICE_STATE_ADDRESS)
        iCfg = 0;
    else
        iCfg = pDev->pCurCfgDesc->Core.bConfigurationValue;

    *pbBuf = iCfg;
    *pcbBuf = 1;
    LogFlow(("vusbDevStdReqGetConfig: %s: returns iCfg=%d\n", pDev->pUsbIns->pszName, iCfg));
    return true;
}

/**
 * Standard device request: GET_INTERFACE
 * @returns success indicator.
 */
static bool vusbDevStdReqGetInterface(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(EndPt);
    if ((pSetup->bmRequestType & VUSB_RECIP_MASK) != VUSB_TO_INTERFACE)
    {
        Log(("vusb: error: %s: GET_INTERFACE - invalid request (dir) !!!\n", pDev->pUsbIns->pszName));
        return false;
    }

    /*
     * Check that the device is in a valid state.
     * (The caller has already checked that it's not being reset.)
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (enmState != VUSB_DEVICE_STATE_CONFIGURED)
    {
        LogFlow(("vusbDevStdReqGetInterface: error: %s: invalid device state %d!!!\n", pDev->pUsbIns->pszName, enmState));
        return false;
    }

    if (*pcbBuf < 1)
    {
        LogFlow(("vusbDevStdReqGetInterface: %s: no space for data!\n", pDev->pUsbIns->pszName));
        return true;
    }

    for (unsigned i = 0; i < pDev->pCurCfgDesc->Core.bNumInterfaces; i++)
    {
        PCVUSBDESCINTERFACEEX pIfDesc = pDev->paIfStates[i].pCurIfDesc;
        if (    pIfDesc
            &&  pSetup->wIndex == pIfDesc->Core.bInterfaceNumber)
        {
            *pbBuf = pIfDesc->Core.bAlternateSetting;
            *pcbBuf = 1;
            Log(("vusb: %s: GET_INTERFACE: %u.%u\n", pDev->pUsbIns->pszName, pIfDesc->Core.bInterfaceNumber, *pbBuf));
            return true;
        }
    }

    Log(("vusb: error: %s: GET_INTERFACE - unknown iface %u !!!\n", pDev->pUsbIns->pszName, pSetup->wIndex));
    return false;
}

/**
 * Standard device request: SET_INTERFACE
 * @returns success indicator.
 */
static bool vusbDevStdReqSetInterface(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(EndPt, pbBuf, pcbBuf);
    if ((pSetup->bmRequestType & VUSB_RECIP_MASK) != VUSB_TO_INTERFACE)
    {
        Log(("vusb: error: %s: SET_INTERFACE - invalid request (dir) !!!\n", pDev->pUsbIns->pszName));
        return false;
    }

    /*
     * Check that the device is in a valid state.
     * (The caller has already checked that it's not being reset.)
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (enmState != VUSB_DEVICE_STATE_CONFIGURED)
    {
        LogFlow(("vusbDevStdReqSetInterface: error: %s: invalid device state %d !!!\n", pDev->pUsbIns->pszName, enmState));
        return false;
    }

    /*
     * Find the interface.
     */
    uint8_t iIf = pSetup->wIndex;
    PVUSBINTERFACESTATE pIfState = vusbDevFindIfState(pDev, iIf);
    if (!pIfState)
    {
        LogFlow(("vusbDevStdReqSetInterface: error: %s: couldn't find interface %u !!!\n", pDev->pUsbIns->pszName, iIf));
        return false;
    }
    uint8_t iAlt = pSetup->wValue;
    PCVUSBDESCINTERFACEEX pIfDesc = vusbDevFindAltIfDesc(pIfState, iAlt);
    if (!pIfDesc)
    {
        LogFlow(("vusbDevStdReqSetInterface: error: %s: couldn't find alt interface %u.%u !!!\n", pDev->pUsbIns->pszName, iIf, iAlt));
        return false;
    }

    if (pDev->pUsbIns->pReg->pfnUsbSetInterface)
    {
        RTCritSectEnter(&pDev->pHub->pRootHub->CritSectDevices);
        int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)pDev->pUsbIns->pReg->pfnUsbSetInterface, 3, pDev->pUsbIns, iIf, iAlt);
        RTCritSectLeave(&pDev->pHub->pRootHub->CritSectDevices);
        if (RT_FAILURE(rc))
        {
            LogFlow(("vusbDevStdReqSetInterface: error: %s: couldn't find alt interface %u.%u (%Rrc)\n", pDev->pUsbIns->pszName, iIf, iAlt, rc));
            return false;
        }
    }

    for (unsigned i = 0; i < pIfState->pCurIfDesc->Core.bNumEndpoints; i++)
        unmap_endpoint(pDev, &pIfState->pCurIfDesc->paEndpoints[i]);

    Log(("vusb: SET_INTERFACE: Selected %u.%u\n", iIf, iAlt));

    map_interface(pDev, pIfDesc);
    pIfState->pCurIfDesc = pIfDesc;

    return true;
}

/**
 * Standard device request: SET_ADDRESS
 * @returns success indicator.
 */
static bool vusbDevStdReqSetAddress(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(EndPt, pbBuf, pcbBuf);
    if ((pSetup->bmRequestType & VUSB_RECIP_MASK) != VUSB_TO_DEVICE)
    {
        Log(("vusb: error: %s: SET_ADDRESS - invalid request (dir) !!!\n", pDev->pUsbIns->pszName));
        return false;
    }

    /*
     * Check that the device is in a valid state.
     * (The caller has already checked that it's not being reset.)
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (    enmState != VUSB_DEVICE_STATE_DEFAULT
        &&  enmState != VUSB_DEVICE_STATE_ADDRESS)
    {
        LogFlow(("vusbDevStdReqSetAddress: error: %s: invalid device state %d !!!\n", pDev->pUsbIns->pszName, enmState));
        return false;
    }

    pDev->u8NewAddress = pSetup->wValue;
    return true;
}

/**
 * Standard device request: CLEAR_FEATURE
 * @returns success indicator.
 *
 * @remark This is only called for VUSB_TO_ENDPOINT && ep == 0 && wValue == ENDPOINT_HALT.
 *         All other cases of CLEAR_FEATURE is handled in the normal async/sync manner.
 */
static bool vusbDevStdReqClearFeature(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(pbBuf, pcbBuf);
    switch (pSetup->bmRequestType & VUSB_RECIP_MASK)
    {
        case VUSB_TO_DEVICE:
            Log(("vusb: ClearFeature: dev(%u): selector=%u\n", pSetup->wIndex, pSetup->wValue));
            break;
        case VUSB_TO_INTERFACE:
            Log(("vusb: ClearFeature: iface(%u): selector=%u\n", pSetup->wIndex, pSetup->wValue));
            break;
        case VUSB_TO_ENDPOINT:
            Log(("vusb: ClearFeature: ep(%u): selector=%u\n", pSetup->wIndex, pSetup->wValue));
            if (    !EndPt /* Default control pipe only */
                &&  pSetup->wValue == 0 /* ENDPOINT_HALT */
                &&  pDev->pUsbIns->pReg->pfnUsbClearHaltedEndpoint)
            {
                RTCritSectEnter(&pDev->pHub->pRootHub->CritSectDevices);
                int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)pDev->pUsbIns->pReg->pfnUsbClearHaltedEndpoint,
                                                 2, pDev->pUsbIns, pSetup->wIndex);
                RTCritSectLeave(&pDev->pHub->pRootHub->CritSectDevices);
                return RT_SUCCESS(rc);
            }
            break;
        default:
            AssertMsgFailed(("VUSB_TO_OTHER!\n"));
            break;
    }

    AssertMsgFailed(("Invalid safe check !!!\n"));
    return false;
}

/**
 * Standard device request: SET_FEATURE
 * @returns success indicator.
 */
static bool vusbDevStdReqSetFeature(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(pDev, EndPt, pbBuf, pcbBuf);
    switch (pSetup->bmRequestType & VUSB_RECIP_MASK)
    {
        case VUSB_TO_DEVICE:
            Log(("vusb: SetFeature: dev(%u): selector=%u\n",
                pSetup->wIndex, pSetup->wValue));
            break;
        case VUSB_TO_INTERFACE:
            Log(("vusb: SetFeature: if(%u): selector=%u\n",
                pSetup->wIndex, pSetup->wValue));
            break;
        case VUSB_TO_ENDPOINT:
            Log(("vusb: SetFeature: ep(%u): selector=%u\n",
                pSetup->wIndex, pSetup->wValue));
            break;
        default:
            AssertMsgFailed(("VUSB_TO_OTHER!\n"));
            return false;
    }
    AssertMsgFailed(("This stuff is bogus\n"));
    return false;
}

static bool vusbDevStdReqGetStatus(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(EndPt);
    if (*pcbBuf != 2)
    {
        LogFlow(("vusbDevStdReqGetStatus: %s: buffer is too small! (%d)\n", pDev->pUsbIns->pszName, *pcbBuf));
        return false;
    }

    uint16_t u16Status;
    switch (pSetup->bmRequestType & VUSB_RECIP_MASK)
    {
        case VUSB_TO_DEVICE:
            u16Status = pDev->u16Status;
            LogFlow(("vusbDevStdReqGetStatus: %s: device status %#x (%d)\n", pDev->pUsbIns->pszName, u16Status, u16Status));
            break;
        case VUSB_TO_INTERFACE:
            u16Status = 0;
            LogFlow(("vusbDevStdReqGetStatus: %s: bogus interface status request!!\n", pDev->pUsbIns->pszName));
            break;
        case VUSB_TO_ENDPOINT:
            u16Status = 0;
            LogFlow(("vusbDevStdReqGetStatus: %s: bogus endpoint status request!!\n", pDev->pUsbIns->pszName));
            break;
        default:
            AssertMsgFailed(("VUSB_TO_OTHER!\n"));
            return false;
    }

    *(uint16_t *)pbBuf = u16Status;
    return true;
}


/**
 * Finds a cached string.
 *
 * @returns Pointer to the cached string if found.  NULL if not.
 * @param   paLanguages         The languages to search.
 * @param   cLanguages          The number of languages in the table.
 * @param   idLang              The language ID.
 * @param   iString             The string index.
 */
static PCPDMUSBDESCCACHESTRING FindCachedString(PCPDMUSBDESCCACHELANG paLanguages, unsigned cLanguages,
                                                uint16_t idLang, uint8_t iString)
{
    /** @todo binary lookups! */
    unsigned iCurLang = cLanguages;
    while (iCurLang-- > 0)
        if (paLanguages[iCurLang].idLang == idLang)
        {
            PCPDMUSBDESCCACHESTRING paStrings = paLanguages[iCurLang].paStrings;
            unsigned                iCurStr   = paLanguages[iCurLang].cStrings;
            while (iCurStr-- > 0)
                if (paStrings[iCurStr].idx == iString)
                    return &paStrings[iCurStr];
            break;
        }
    return NULL;
}


/** Macro for copying descriptor data. */
#define COPY_DATA(pbDst, cbLeft, pvSrc, cbSrc) \
    do { \
        uint32_t cbSrc_ = cbSrc; \
        uint32_t cbCopy = RT_MIN(cbLeft, cbSrc_); \
        if (cbCopy) \
            memcpy(pbBuf, pvSrc, cbCopy); \
        cbLeft -= cbCopy; \
        if (!cbLeft) \
            return; \
        pbBuf += cbCopy; \
    } while (0)

/**
 * Internal function for reading the language IDs.
 */
static void ReadCachedStringDesc(PCPDMUSBDESCCACHESTRING pString, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    uint32_t        cbLeft = *pcbBuf;

    RTUTF16         wsz[128];           /* 128-1 => bLength=0xff */
    PRTUTF16        pwsz = wsz;
    size_t          cwc;
    int rc = RTStrToUtf16Ex(pString->psz, RT_ELEMENTS(wsz) - 1, &pwsz, RT_ELEMENTS(wsz), &cwc);
    if (RT_FAILURE(rc))
    {
        AssertRC(rc);
        wsz[0] = 'e';
        wsz[1] = 'r';
        wsz[2] = 'r';
        cwc = 3;
    }

    VUSBDESCSTRING  StringDesc;
    StringDesc.bLength          = (uint8_t)(sizeof(StringDesc) + cwc * sizeof(RTUTF16));
    StringDesc.bDescriptorType  = VUSB_DT_STRING;
    COPY_DATA(pbBuf, cbLeft, &StringDesc, sizeof(StringDesc));
    COPY_DATA(pbBuf, cbLeft, wsz, (uint32_t)cwc * sizeof(RTUTF16));

    /* updated the size of the output buffer. */
    *pcbBuf -= cbLeft;
}


/**
 * Internal function for reading the language IDs.
 */
static void ReadCachedLangIdDesc(PCPDMUSBDESCCACHELANG paLanguages, unsigned cLanguages,
                                 uint8_t *pbBuf, uint32_t *pcbBuf)
{
    uint32_t        cbLeft      = *pcbBuf;

    VUSBDESCLANGID  LangIdDesc;
    size_t          cbDesc      = sizeof(LangIdDesc) + cLanguages * sizeof(paLanguages[0].idLang);
    LangIdDesc.bLength          = (uint8_t)RT_MIN(0xff, cbDesc);
    LangIdDesc.bDescriptorType  = VUSB_DT_STRING;
    COPY_DATA(pbBuf, cbLeft, &LangIdDesc, sizeof(LangIdDesc));

    unsigned iLanguage = cLanguages;
    while (iLanguage-- > 0)
        COPY_DATA(pbBuf, cbLeft, &paLanguages[iLanguage].idLang, sizeof(paLanguages[iLanguage].idLang));

    /* updated the size of the output buffer. */
    *pcbBuf -= cbLeft;
}


/**
 * Internal function which performs a descriptor read on the cached descriptors.
 */
static void ReadCachedConfigDesc(PCVUSBDESCCONFIGEX pCfgDesc, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    uint32_t cbLeft = *pcbBuf;

/** @todo See @bugref{2693} */
    /*
     * Make a copy of the config descriptor and calculate the wTotalLength field.
     */
    VUSBDESCCONFIG CfgDesc;
    memcpy(&CfgDesc, pCfgDesc, VUSB_DT_CONFIG_MIN_LEN);
    uint32_t cbTotal = pCfgDesc->Core.bLength;
    for (unsigned i = 0; i < pCfgDesc->Core.bNumInterfaces; i++)
    {
        PCVUSBINTERFACE pIf = &pCfgDesc->paIfs[i];
        for (uint32_t j = 0; j < pIf->cSettings; j++)
        {
            cbTotal += pIf->paSettings[j].cbIAD;
            cbTotal += pIf->paSettings[j].Core.bLength;
            cbTotal += pIf->paSettings[j].cbClass;
            for (unsigned k = 0; k < pIf->paSettings[j].Core.bNumEndpoints; k++)
            {
                cbTotal += pIf->paSettings[j].paEndpoints[k].Core.bLength;
                cbTotal += pIf->paSettings[j].paEndpoints[k].cbSsepc;
                cbTotal += pIf->paSettings[j].paEndpoints[k].cbClass;
            }
        }
    }
    CfgDesc.wTotalLength = RT_H2LE_U16(cbTotal);

    /*
     * Copy the config descriptor
     */
    COPY_DATA(pbBuf, cbLeft, &CfgDesc, VUSB_DT_CONFIG_MIN_LEN);
    COPY_DATA(pbBuf, cbLeft, pCfgDesc->pvMore, pCfgDesc->Core.bLength - VUSB_DT_CONFIG_MIN_LEN);

    /*
     * Copy out all the interfaces for this configuration
     */
    for (unsigned i = 0; i < pCfgDesc->Core.bNumInterfaces; i++)
    {
        PCVUSBINTERFACE pIf = &pCfgDesc->paIfs[i];
        for (uint32_t j = 0; j < pIf->cSettings; j++)
        {
            PCVUSBDESCINTERFACEEX pIfDesc = &pIf->paSettings[j];

            COPY_DATA(pbBuf, cbLeft, pIfDesc->pIAD, pIfDesc->cbIAD);
            COPY_DATA(pbBuf, cbLeft, pIfDesc, VUSB_DT_INTERFACE_MIN_LEN);
            COPY_DATA(pbBuf, cbLeft, pIfDesc->pvMore, pIfDesc->Core.bLength - VUSB_DT_INTERFACE_MIN_LEN);
            COPY_DATA(pbBuf, cbLeft, pIfDesc->pvClass, pIfDesc->cbClass);

            /*
             * Copy out all the endpoints for this interface
             */
            for (unsigned k = 0; k < pIfDesc->Core.bNumEndpoints; k++)
            {
                VUSBDESCENDPOINT EndPtDesc;
                memcpy(&EndPtDesc, &pIfDesc->paEndpoints[k], VUSB_DT_ENDPOINT_MIN_LEN);
                EndPtDesc.wMaxPacketSize = RT_H2LE_U16(EndPtDesc.wMaxPacketSize);

                COPY_DATA(pbBuf, cbLeft, &EndPtDesc, VUSB_DT_ENDPOINT_MIN_LEN);
                COPY_DATA(pbBuf, cbLeft, pIfDesc->paEndpoints[k].pvMore, EndPtDesc.bLength - VUSB_DT_ENDPOINT_MIN_LEN);
                COPY_DATA(pbBuf, cbLeft, pIfDesc->paEndpoints[k].pvSsepc, pIfDesc->paEndpoints[k].cbSsepc);
                COPY_DATA(pbBuf, cbLeft, pIfDesc->paEndpoints[k].pvClass, pIfDesc->paEndpoints[k].cbClass);
            }
        }
    }

    /* updated the size of the output buffer. */
    *pcbBuf -= cbLeft;
}

/**
 * Internal function which performs a descriptor read on the cached descriptors.
 */
static void ReadCachedDeviceDesc(PCVUSBDESCDEVICE pDevDesc, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    uint32_t cbLeft = *pcbBuf;

    /*
     * Duplicate the device description and update some fields we keep in cpu type.
     */
    Assert(sizeof(VUSBDESCDEVICE) == 18);
    VUSBDESCDEVICE DevDesc = *pDevDesc;
    DevDesc.bcdUSB    = RT_H2LE_U16(DevDesc.bcdUSB);
    DevDesc.idVendor  = RT_H2LE_U16(DevDesc.idVendor);
    DevDesc.idProduct = RT_H2LE_U16(DevDesc.idProduct);
    DevDesc.bcdDevice = RT_H2LE_U16(DevDesc.bcdDevice);

    COPY_DATA(pbBuf, cbLeft, &DevDesc, sizeof(DevDesc));
    COPY_DATA(pbBuf, cbLeft, pDevDesc + 1, pDevDesc->bLength - sizeof(DevDesc));

    /* updated the size of the output buffer. */
    *pcbBuf -= cbLeft;
}

#undef COPY_DATA

/**
 * Standard device request: GET_DESCRIPTOR
 * @returns success indicator.
 * @remark not really used yet as we consider GET_DESCRIPTOR 'safe'.
 */
static bool vusbDevStdReqGetDescriptor(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
    RT_NOREF(EndPt);
    if ((pSetup->bmRequestType & VUSB_RECIP_MASK) == VUSB_TO_DEVICE)
    {
        switch (pSetup->wValue >> 8)
        {
            case VUSB_DT_DEVICE:
                ReadCachedDeviceDesc(pDev->pDescCache->pDevice, pbBuf, pcbBuf);
                LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of device descriptors\n", pDev->pUsbIns->pszName, *pcbBuf));
                return true;

            case VUSB_DT_CONFIG:
            {
                unsigned int iIndex = (pSetup->wValue & 0xff);
                if (iIndex >= pDev->pDescCache->pDevice->bNumConfigurations)
                {
                    LogFlow(("vusbDevStdReqGetDescriptor: %s: iIndex=%p >= bNumConfigurations=%d !!!\n",
                             pDev->pUsbIns->pszName, iIndex, pDev->pDescCache->pDevice->bNumConfigurations));
                    return false;
                }
                ReadCachedConfigDesc(&pDev->pDescCache->paConfigs[iIndex], pbBuf, pcbBuf);
                LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of config descriptors\n", pDev->pUsbIns->pszName, *pcbBuf));
                return true;
            }

            case VUSB_DT_STRING:
            {
                if (pSetup->wIndex == 0)
                {
                    ReadCachedLangIdDesc(pDev->pDescCache->paLanguages, pDev->pDescCache->cLanguages, pbBuf, pcbBuf);
                    LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of language ID (string) descriptors\n", pDev->pUsbIns->pszName, *pcbBuf));
                    return true;
                }
                PCPDMUSBDESCCACHESTRING pString;
                pString = FindCachedString(pDev->pDescCache->paLanguages, pDev->pDescCache->cLanguages,
                                           pSetup->wIndex, pSetup->wValue & 0xff);
                if (pString)
                {
                    ReadCachedStringDesc(pString, pbBuf, pcbBuf);
                    LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of string descriptors \"%s\"\n",
                             pDev->pUsbIns->pszName, *pcbBuf, pString->psz));
                    return true;
                }
                break;
            }

            default:
                break;
        }
    }
    Log(("vusb: %s: warning: unknown descriptor: type=%u descidx=%u lang=%u len=%u!!!\n",
         pDev->pUsbIns->pszName, pSetup->wValue >> 8, pSetup->wValue & 0xff, pSetup->wIndex, pSetup->wLength));
    return false;
}


/**
 * Service the standard USB requests.
 *
 * Devices may call this from controlmsg() if you want vusb core to handle your standard
 * request, it's not necessary - you could handle them manually
 *
 * @param   pDev        The device.
 * @param   EndPoint    The endpoint.
 * @param   pSetup      Pointer to the setup request structure.
 * @param   pvBuf       Buffer?
 * @param   pcbBuf      ?
 */
bool vusbDevStandardRequest(PVUSBDEV pDev, int EndPoint, PVUSBSETUP pSetup, void *pvBuf, uint32_t *pcbBuf)
{
    static bool (* const s_apfnStdReq[VUSB_REQ_MAX])(PVUSBDEV, int, PVUSBSETUP, uint8_t *, uint32_t *) =
    {
        vusbDevStdReqGetStatus,
        vusbDevStdReqClearFeature,
        NULL,
        vusbDevStdReqSetFeature,
        NULL,
        vusbDevStdReqSetAddress,
        vusbDevStdReqGetDescriptor,
        NULL,
        vusbDevStdReqGetConfig,
        vusbDevStdReqSetConfig,
        vusbDevStdReqGetInterface,
        vusbDevStdReqSetInterface,
        NULL /* for iso */
    };

    /*
     * Check that the device is in a valid state.
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (enmState == VUSB_DEVICE_STATE_RESET)
    {
        LogRel(("VUSB: %s: standard control message ignored, the device is resetting\n", pDev->pUsbIns->pszName));
        return false;
    }

    /*
     * Do the request if it's one we want to deal with.
     */
    if (    pSetup->bRequest >= VUSB_REQ_MAX
        ||  !s_apfnStdReq[pSetup->bRequest])
    {
        Log(("vusb: warning: standard req not implemented: message %u: val=%u idx=%u len=%u !!!\n",
             pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
        return false;
    }

    return s_apfnStdReq[pSetup->bRequest](pDev, EndPoint, pSetup, (uint8_t *)pvBuf, pcbBuf);
}


/**
 * Add a device to the address hash
 */
static void vusbDevAddressHash(PVUSBDEV pDev)
{
    if (pDev->u8Address == VUSB_INVALID_ADDRESS)
        return;
    uint8_t u8Hash = vusbHashAddress(pDev->u8Address);
    pDev->pNextHash = pDev->pHub->pRootHub->apAddrHash[u8Hash];
    pDev->pHub->pRootHub->apAddrHash[u8Hash] = pDev;
}

/**
 * Remove a device from the address hash
 */
static void vusbDevAddressUnHash(PVUSBDEV pDev)
{
    if (pDev->u8Address == VUSB_INVALID_ADDRESS)
        return;

    uint8_t u8Hash = vusbHashAddress(pDev->u8Address);
    pDev->u8Address = VUSB_INVALID_ADDRESS;
    pDev->u8NewAddress = VUSB_INVALID_ADDRESS;

    RTCritSectEnter(&pDev->pHub->pRootHub->CritSectDevices);
    PVUSBDEV pCur = pDev->pHub->pRootHub->apAddrHash[u8Hash];
    if (pCur == pDev)
    {
        /* special case, we're at the head */
        pDev->pHub->pRootHub->apAddrHash[u8Hash] = pDev->pNextHash;
        pDev->pNextHash = NULL;
    }
    else
    {
        /* search the list */
        PVUSBDEV pPrev;
        for (pPrev = pCur, pCur = pCur->pNextHash;
             pCur;
             pPrev = pCur, pCur = pCur->pNextHash)
        {
            if (pCur == pDev)
            {
                pPrev->pNextHash = pCur->pNextHash;
                pDev->pNextHash = NULL;
                break;
            }
        }
    }
    RTCritSectLeave(&pDev->pHub->pRootHub->CritSectDevices);
}

/**
 * Sets the address of a device.
 *
 * Called by status_completion() and vusbDevResetWorker().
 */
void vusbDevSetAddress(PVUSBDEV pDev, uint8_t u8Address)
{
    LogFlow(("vusbDevSetAddress: pDev=%p[%s]/%i u8Address=%#x\n",
             pDev, pDev->pUsbIns->pszName, pDev->i16Port, u8Address));

    /*
     * Check that the device is in a valid state.
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    VUSBDEV_ASSERT_VALID_STATE(enmState);
    if (    enmState == VUSB_DEVICE_STATE_ATTACHED
        ||  enmState == VUSB_DEVICE_STATE_DETACHED)
    {
        LogFlow(("vusbDevSetAddress: %s: fails because %d < POWERED\n", pDev->pUsbIns->pszName, pDev->enmState));
        return;
    }
    if (enmState == VUSB_DEVICE_STATE_RESET)
    {
        LogRel(("VUSB: %s: set address ignored, the device is resetting\n", pDev->pUsbIns->pszName));
        return;
    }

    /*
     * Ok, get on with it.
     */
    if (pDev->u8Address == u8Address)
        return;

    PVUSBROOTHUB pRh = vusbDevGetRh(pDev);
    AssertPtrReturnVoid(pRh);
    if (pDev->u8Address == VUSB_DEFAULT_ADDRESS)
        pRh->pDefaultAddress = NULL;

    vusbDevAddressUnHash(pDev);

    if (u8Address == VUSB_DEFAULT_ADDRESS)
    {
        if (pRh->pDefaultAddress != NULL)
        {
            vusbDevAddressUnHash(pRh->pDefaultAddress);
            vusbDevSetStateCmp(pRh->pDefaultAddress, VUSB_DEVICE_STATE_POWERED, VUSB_DEVICE_STATE_DEFAULT);
            Log(("2 DEFAULT ADDRS\n"));
        }

        pRh->pDefaultAddress = pDev;
        vusbDevSetState(pDev, VUSB_DEVICE_STATE_DEFAULT);
    }
    else
        vusbDevSetState(pDev, VUSB_DEVICE_STATE_ADDRESS);

    pDev->u8Address = u8Address;
    vusbDevAddressHash(pDev);

    Log(("vusb: %p[%s]/%i: Assigned address %u\n",
         pDev, pDev->pUsbIns->pszName, pDev->i16Port, u8Address));
}


static DECLCALLBACK(int) vusbDevCancelAllUrbsWorker(PVUSBDEV pDev, bool fDetaching)
{
    /*
     * Iterate the URBs and cancel them.
     */
    PVUSBURBVUSB pVUsbUrb, pVUsbUrbNext;
    RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
    {
        PVUSBURB pUrb = pVUsbUrb->pUrb;

        Assert(pUrb->pVUsb->pDev == pDev);

        LogFlow(("%s: vusbDevCancelAllUrbs: CANCELING URB\n", pUrb->pszDesc));
        int rc = vusbUrbCancelWorker(pUrb, CANCELMODE_FAIL);
        AssertRC(rc);
    }

    /*
     * Reap any URBs which became ripe during cancel now.
     */
    RTCritSectEnter(&pDev->CritSectAsyncUrbs);
    unsigned cReaped;
    do
    {
        cReaped = 0;
        pVUsbUrb = RTListGetFirst(&pDev->LstAsyncUrbs, VUSBURBVUSBINT, NdLst);
        while (pVUsbUrb)
        {
            PVUSBURBVUSB pNext = RTListGetNext(&pDev->LstAsyncUrbs, pVUsbUrb, VUSBURBVUSBINT, NdLst);
            PVUSBURB pUrb = pVUsbUrb->pUrb;
            Assert(pUrb->pVUsb->pDev == pDev);

            PVUSBURB pRipe = NULL;
            if (pUrb->enmState == VUSBURBSTATE_REAPED)
                pRipe = pUrb;
            else if (pUrb->enmState == VUSBURBSTATE_CANCELLED)
#ifdef RT_OS_WINDOWS   /** @todo Windows doesn't do cancelling, thus this kludge to prevent really bad
                    * things from happening if we leave a pending URB behinds. */
                pRipe = pDev->pUsbIns->pReg->pfnUrbReap(pDev->pUsbIns, fDetaching ? 1500 : 0 /*ms*/);
#else
                pRipe = pDev->pUsbIns->pReg->pfnUrbReap(pDev->pUsbIns, fDetaching ? 10 : 0 /*ms*/);
#endif
            else
                AssertMsgFailed(("pUrb=%p enmState=%d\n", pUrb, pUrb->enmState));
            if (pRipe)
            {
                if (   pNext
                    && pRipe == pNext->pUrb)
                    pNext = RTListGetNext(&pDev->LstAsyncUrbs, pNext, VUSBURBVUSBINT, NdLst);
                vusbUrbRipe(pRipe);
                cReaped++;
            }

            pVUsbUrb = pNext;
        }
    } while (cReaped > 0);

    /*
     * If we're detaching, we'll have to orphan any leftover URBs.
     */
    if (fDetaching)
    {
        RTListForEachSafe(&pDev->LstAsyncUrbs, pVUsbUrb, pVUsbUrbNext, VUSBURBVUSBINT, NdLst)
        {
            PVUSBURB pUrb = pVUsbUrb->pUrb;
            Assert(pUrb->pVUsb->pDev == pDev);

            AssertMsgFailed(("%s: Leaking left over URB! state=%d pDev=%p[%s]\n",
                             pUrb->pszDesc, pUrb->enmState, pDev, pDev->pUsbIns->pszName));
            vusbUrbUnlink(pUrb);
            /* Unlink isn't enough, because boundary timer and detaching will try to reap it.
             * It was tested with MSD & iphone attachment to vSMP guest, if
             * it breaks anything, please add comment here, why we should unlink only.
             */
            pUrb->pVUsb->pfnFree(pUrb);
        }
    }
    RTCritSectLeave(&pDev->CritSectAsyncUrbs);
    return VINF_SUCCESS;
}

/**
 * Cancels and completes (with CRC failure) all async URBs pending
 * on a device. This is typically done as part of a reset and
 * before detaching a device.
 *
 * @param   fDetaching  If set, we will unconditionally unlink (and leak)
 *                      any URBs which isn't reaped.
 */
DECLHIDDEN(void) vusbDevCancelAllUrbs(PVUSBDEV pDev, bool fDetaching)
{
    int rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbDevCancelAllUrbsWorker, 2, pDev, fDetaching);
    AssertRC(rc);
}


static DECLCALLBACK(int) vusbDevUrbIoThread(RTTHREAD hThread, void *pvUser)
{
    PVUSBDEV pDev = (PVUSBDEV)pvUser;

    /* Notify the starter that we are up and running. */
    RTThreadUserSignal(hThread);

    LogFlowFunc(("Entering work loop\n"));

    while (!ASMAtomicReadBool(&pDev->fTerminate))
    {
        if (vusbDevGetState(pDev) != VUSB_DEVICE_STATE_RESET)
            vusbUrbDoReapAsyncDev(pDev, RT_INDEFINITE_WAIT);

        /* Process any URBs waiting to be cancelled first. */
        int rc = RTReqQueueProcess(pDev->hReqQueueSync, 0); /* Don't wait if there is nothing to do. */
        Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT); NOREF(rc);
    }

    return VINF_SUCCESS;
}

int vusbDevUrbIoThreadWakeup(PVUSBDEV pDev)
{
    ASMAtomicXchgBool(&pDev->fWokenUp, true);
    return pDev->pUsbIns->pReg->pfnWakeup(pDev->pUsbIns);
}

/**
 * Create the URB I/O thread.
 *
 * @returns VBox status code.
 * @param   pDev    The VUSB device.
 */
int vusbDevUrbIoThreadCreate(PVUSBDEV pDev)
{
    int rc = VINF_SUCCESS;

    ASMAtomicXchgBool(&pDev->fTerminate, false);
    rc = RTThreadCreateF(&pDev->hUrbIoThread, vusbDevUrbIoThread, pDev, 0, RTTHREADTYPE_IO,
                         RTTHREADFLAGS_WAITABLE, "USBDevIo-%d", pDev->i16Port);
    if (RT_SUCCESS(rc))
    {
        /* Wait for it to become active. */
        rc = RTThreadUserWait(pDev->hUrbIoThread, RT_INDEFINITE_WAIT);
    }

    return rc;
}

/**
 * Destro the URB I/O thread.
 *
 * @returns VBox status code.
 * @param   pDev    The VUSB device.
 */
int vusbDevUrbIoThreadDestroy(PVUSBDEV pDev)
{
    int rc = VINF_SUCCESS;
    int rcThread = VINF_SUCCESS;

    ASMAtomicXchgBool(&pDev->fTerminate, true);
    vusbDevUrbIoThreadWakeup(pDev);

    rc = RTThreadWait(pDev->hUrbIoThread, RT_INDEFINITE_WAIT, &rcThread);
    if (RT_SUCCESS(rc))
        rc = rcThread;

    pDev->hUrbIoThread = NIL_RTTHREAD;

    return rc;
}


/**
 * Detaches a device from the hub it's attached to.
 *
 * @returns VBox status code.
 * @param   pDev        The device to detach.
 *
 * @remark  This can be called in any state but reset.
 */
int vusbDevDetach(PVUSBDEV pDev)
{
    LogFlow(("vusbDevDetach: pDev=%p[%s] enmState=%#x\n", pDev, pDev->pUsbIns->pszName, pDev->enmState));
    VUSBDEV_ASSERT_VALID_STATE(pDev->enmState);
    Assert(pDev->enmState != VUSB_DEVICE_STATE_RESET);

    vusbDevCancelAllUrbs(pDev, true);
    vusbDevAddressUnHash(pDev);

    PVUSBROOTHUB pRh = vusbDevGetRh(pDev);
    if (!pRh)
        AssertMsgFailedReturn(("Not attached!\n"), VERR_VUSB_DEVICE_NOT_ATTACHED);
    if (pRh->pDefaultAddress == pDev)
        pRh->pDefaultAddress = NULL;

    pDev->pHub->pOps->pfnDetach(pDev->pHub, pDev);
    pDev->i16Port = -1;
    vusbDevSetState(pDev, VUSB_DEVICE_STATE_DETACHED);
    pDev->pHub = NULL;

    /* Remove the configuration */
    pDev->pCurCfgDesc = NULL;
    for (unsigned i = 0; i < RT_ELEMENTS(pDev->aPipes); i++)
        vusbDevResetPipeData(&pDev->aPipes[i]);
    return VINF_SUCCESS;
}


/**
 * Destroys a device, detaching it from the hub if necessary.
 *
 * @param   pDev    The device.
 * @thread any.
 */
void vusbDevDestroy(PVUSBDEV pDev)
{
    LogFlow(("vusbDevDestroy: pDev=%p[%s] enmState=%d\n", pDev, pDev->pUsbIns->pszName, pDev->enmState));

    RTMemFree(pDev->paIfStates);
    TMR3TimerDestroy(pDev->pResetTimer);
    pDev->pResetTimer = NULL;
    for (unsigned i = 0; i < RT_ELEMENTS(pDev->aPipes); i++)
    {
        Assert(pDev->aPipes[i].pCtrl == NULL);
        RTCritSectDelete(&pDev->aPipes[i].CritSectCtrl);
    }

    /*
     * Destroy I/O thread and request queue last because they might still be used
     * when cancelling URBs.
     */
    vusbDevUrbIoThreadDestroy(pDev);

    int rc = RTReqQueueDestroy(pDev->hReqQueueSync);
    AssertRC(rc);

    if (pDev->hSniffer != VUSBSNIFFER_NIL)
        VUSBSnifferDestroy(pDev->hSniffer);

    vusbUrbPoolDestroy(&pDev->UrbPool);

    RTCritSectDelete(&pDev->CritSectAsyncUrbs);
    /* Not using vusbDevSetState() deliberately here because it would assert on the state. */
    pDev->enmState = VUSB_DEVICE_STATE_DESTROYED;
    pDev->pUsbIns->pvVUsbDev2 = NULL;
    RTMemFree(pDev);
}


/* -=-=-=-=-=- VUSBIDEVICE methods -=-=-=-=-=- */


/**
 * The actual reset has been done, do completion on EMT.
 *
 * There are several things we have to do now, like set default
 * config and address, and cleanup the state of control pipes.
 *
 * It's possible that the device has a delayed destroy request
 * pending when we get here. This can happen for async resetting.
 * We deal with it here, since we're now executing on the EMT
 * thread and the destruction will be properly serialized now.
 *
 * @param   pDev    The device that is being reset.
 * @param   rc      The vusbDevResetWorker return code.
 * @param   pfnDone The done callback specified by the caller of vusbDevReset().
 * @param   pvUser  The user argument for the callback.
 */
static void vusbDevResetDone(PVUSBDEV pDev, int rc, PFNVUSBRESETDONE pfnDone, void *pvUser)
{
    VUSBDEV_ASSERT_VALID_STATE(pDev->enmState);
    Assert(pDev->enmState == VUSB_DEVICE_STATE_RESET);

    /*
     * Do control pipe cleanup regardless of state and result.
     */
    for (unsigned i = 0; i < VUSB_PIPE_MAX; i++)
        if (pDev->aPipes[i].pCtrl)
            vusbMsgResetExtraData(pDev->aPipes[i].pCtrl);

    /*
     * Switch to the default state.
     */
    vusbDevSetState(pDev, VUSB_DEVICE_STATE_DEFAULT);
    pDev->u16Status = 0;
    vusbDevDoSelectConfig(pDev, &g_Config0);
    if (!vusbDevIsRh(pDev))
        vusbDevSetAddress(pDev, VUSB_DEFAULT_ADDRESS);
    if (pfnDone)
        pfnDone(&pDev->IDevice, rc, pvUser);
}


/**
 * Timer callback for doing reset completion.
 *
 * @param   pUsbIns     The USB device instance.
 * @param   pTimer      The timer instance.
 * @param   pvUser      The VUSB device data.
 * @thread EMT
 */
static DECLCALLBACK(void) vusbDevResetDoneTimer(PPDMUSBINS pUsbIns, PTMTIMER pTimer, void *pvUser)
{
    RT_NOREF(pUsbIns, pTimer);
    PVUSBDEV        pDev  = (PVUSBDEV)pvUser;
    PVUSBRESETARGS  pArgs = (PVUSBRESETARGS)pDev->pvArgs;
    Assert(pDev->pUsbIns == pUsbIns);

    AssertPtr(pArgs);

    /*
     * Reset-done processing and cleanup.
     */
    pDev->pvArgs = NULL;
    vusbDevResetDone(pDev, pArgs->rc, pArgs->pfnDone, pArgs->pvUser);
    RTMemFree(pArgs);
}


/**
 * Perform the actual reset.
 *
 * @thread EMT or a VUSB reset thread.
 */
static int vusbDevResetWorker(PVUSBDEV pDev, bool fResetOnLinux, bool fUseTimer, PVUSBRESETARGS pArgs)
{
    int rc = VINF_SUCCESS;
    uint64_t u64EndTS = TMTimerGet(pDev->pResetTimer) + TMTimerFromMilli(pDev->pResetTimer, 10);

    if (pDev->pUsbIns->pReg->pfnUsbReset)
        rc = pDev->pUsbIns->pReg->pfnUsbReset(pDev->pUsbIns, fResetOnLinux);

    if (pArgs)
    {
        pArgs->rc = rc;
        rc = VINF_SUCCESS;
    }

    if (fUseTimer)
    {
        /*
         * We use a timer to communicate the result back to EMT.
         * This avoids suspend + poweroff issues, and it should give
         * us more accurate scheduling than making this thread sleep.
         */
        int rc2 = TMTimerSet(pDev->pResetTimer, u64EndTS);
        AssertReleaseRC(rc2);
    }

    LogFlow(("vusbDevResetWorker: %s: returns %Rrc\n", pDev->pUsbIns->pszName, rc));
    return rc;
}


/**
 * Resets a device.
 *
 * Since a device reset shall take at least 10ms from the guest point of view,
 * it must be performed asynchronously.  We create a thread which performs this
 * operation and ensures it will take at least 10ms.
 *
 * At times - like init - a synchronous reset is required, this can be done
 * by passing NULL for pfnDone.
 *
 * While the device is being reset it is in the VUSB_DEVICE_STATE_RESET state.
 * On completion it will be in the VUSB_DEVICE_STATE_DEFAULT state if successful,
 * or in the VUSB_DEVICE_STATE_DETACHED state if the rest failed.
 *
 * @returns VBox status code.
 *
 * @param   pDev            Pointer to the VUSB device interface.
 * @param   fResetOnLinux   Whether it's safe to reset the device(s) on a linux
 *                          host system. See discussion of logical reconnects elsewhere.
 * @param   pfnDone         Pointer to the completion routine. If NULL a synchronous
 *                          reset is preformed not respecting the 10ms.
 * @param   pVM             Pointer to the VM handle for performing the done function
 *                          on the EMT thread.
 * @thread  EMT
 */
static DECLCALLBACK(int) vusbIDeviceReset(PVUSBIDEVICE pDevice, bool fResetOnLinux,
                                          PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
{
    RT_NOREF(pVM);
    PVUSBDEV pDev = (PVUSBDEV)pDevice;
    Assert(!pfnDone || pVM);
    LogFlow(("vusb: reset: [%s]/%i\n", pDev->pUsbIns->pszName, pDev->i16Port));

    /*
     * Only one reset operation at a time.
     */
    const VUSBDEVICESTATE enmStateOld = vusbDevSetState(pDev, VUSB_DEVICE_STATE_RESET);
    if (enmStateOld == VUSB_DEVICE_STATE_RESET)
    {
        LogRel(("VUSB: %s: reset request is ignored, the device is already resetting!\n", pDev->pUsbIns->pszName));
        return VERR_VUSB_DEVICE_IS_RESETTING;
    }

    /*
     * First, cancel all async URBs.
     */
    vusbDevCancelAllUrbs(pDev, false);

    /* Async or sync? */
    if (pfnDone)
    {
        /*
         * Async fashion.
         */
        PVUSBRESETARGS pArgs = (PVUSBRESETARGS)RTMemTmpAlloc(sizeof(*pArgs));
        if (pArgs)
        {
            pArgs->pDev    = pDev;
            pArgs->pfnDone = pfnDone;
            pArgs->pvUser  = pvUser;
            pArgs->rc      = VINF_SUCCESS;
            AssertPtrNull(pDev->pvArgs);
            pDev->pvArgs   = pArgs;
            int rc = vusbDevIoThreadExec(pDev, 0 /* fFlags */, (PFNRT)vusbDevResetWorker, 4, pDev, fResetOnLinux, true, pArgs);
            if (RT_SUCCESS(rc))
                return rc;

            RTMemTmpFree(pArgs);
        }
        /* fall back to sync on failure */
    }

    /*
     * Sync fashion.
     */
    int rc = vusbDevResetWorker(pDev, fResetOnLinux, false, NULL);
    vusbDevResetDone(pDev, rc, pfnDone, pvUser);
    return rc;
}


/**
 * Powers on the device.
 *
 * @returns VBox status code.
 * @param   pInterface      Pointer to the device interface structure.
 */
static DECLCALLBACK(int) vusbIDevicePowerOn(PVUSBIDEVICE pInterface)
{
    PVUSBDEV pDev = (PVUSBDEV)pInterface;
    LogFlow(("vusbDevPowerOn: pDev=%p[%s]\n", pDev, pDev->pUsbIns->pszName));

    /*
     * Check that the device is in a valid state.
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (enmState == VUSB_DEVICE_STATE_DETACHED)
    {
        Log(("vusb: warning: attempt to power on detached device %p[%s]\n", pDev, pDev->pUsbIns->pszName));
        return VERR_VUSB_DEVICE_NOT_ATTACHED;
    }
    if (enmState == VUSB_DEVICE_STATE_RESET)
    {
        LogRel(("VUSB: %s: power on ignored, the device is resetting!\n", pDev->pUsbIns->pszName));
        return VERR_VUSB_DEVICE_IS_RESETTING;
    }

    /*
     * Do the job.
     */
    if (enmState == VUSB_DEVICE_STATE_ATTACHED)
        vusbDevSetState(pDev, VUSB_DEVICE_STATE_POWERED);

    return VINF_SUCCESS;
}


/**
 * Powers off the device.
 *
 * @returns VBox status code.
 * @param   pInterface      Pointer to the device interface structure.
 */
static DECLCALLBACK(int) vusbIDevicePowerOff(PVUSBIDEVICE pInterface)
{
    PVUSBDEV pDev = (PVUSBDEV)pInterface;
    LogFlow(("vusbDevPowerOff: pDev=%p[%s]\n", pDev, pDev->pUsbIns->pszName));

    /*
     * Check that the device is in a valid state.
     */
    const VUSBDEVICESTATE enmState = vusbDevGetState(pDev);
    if (enmState == VUSB_DEVICE_STATE_DETACHED)
    {
        Log(("vusb: warning: attempt to power off detached device %p[%s]\n", pDev, pDev->pUsbIns->pszName));
        return VERR_VUSB_DEVICE_NOT_ATTACHED;
    }
    if (enmState == VUSB_DEVICE_STATE_RESET)
    {
        LogRel(("VUSB: %s: power off ignored, the device is resetting!\n", pDev->pUsbIns->pszName));
        return VERR_VUSB_DEVICE_IS_RESETTING;
    }

    /*
     * If it's a root hub, we will have to cancel all URBs and reap them.
     */
    if (vusbDevIsRh(pDev))
    {
        PVUSBROOTHUB pRh = (PVUSBROOTHUB)pDev;
        VUSBIRhCancelAllUrbs(&pRh->IRhConnector);
        VUSBIRhReapAsyncUrbs(&pRh->IRhConnector, pInterface, 0);
    }

    vusbDevSetState(pDev, VUSB_DEVICE_STATE_ATTACHED);
    return VINF_SUCCESS;
}


/**
 * Get the state of the device.
 *
 * @returns Device state.
 * @param   pInterface      Pointer to the device interface structure.
 */
static DECLCALLBACK(VUSBDEVICESTATE) vusbIDeviceGetState(PVUSBIDEVICE pInterface)
{
    return vusbDevGetState((PVUSBDEV)pInterface);
}


/**
 * @interface_method_impl{VUSBIDEVICE,pfnIsSavedStateSupported}
 */
static DECLCALLBACK(bool) vusbIDeviceIsSavedStateSupported(PVUSBIDEVICE pInterface)
{
    PVUSBDEV pDev = (PVUSBDEV)pInterface;
    bool fSavedStateSupported = RT_BOOL(pDev->pUsbIns->pReg->fFlags & PDM_USBREG_SAVED_STATE_SUPPORTED);

    LogFlowFunc(("pInterface=%p\n", pInterface));

    LogFlowFunc(("returns %RTbool\n", fSavedStateSupported));
    return fSavedStateSupported;
}


/**
 * @interface_method_impl{VUSBIDEVICE,pfnGetState}
 */
static DECLCALLBACK(VUSBSPEED) vusbIDeviceGetSpeed(PVUSBIDEVICE pInterface)
{
    PVUSBDEV pDev = (PVUSBDEV)pInterface;
    VUSBSPEED enmSpeed = pDev->pUsbIns->enmSpeed;

    LogFlowFunc(("pInterface=%p, returns %u\n", pInterface, enmSpeed));
    return enmSpeed;
}


/**
 * The maximum number of interfaces the device can have in all of it's configuration.
 *
 * @returns Number of interfaces.
 * @param   pDev        The device.
 */
size_t vusbDevMaxInterfaces(PVUSBDEV pDev)
{
    uint8_t cMax = 0;
    unsigned i = pDev->pDescCache->pDevice->bNumConfigurations;
    while (i-- > 0)
    {
        if (pDev->pDescCache->paConfigs[i].Core.bNumInterfaces > cMax)
            cMax = pDev->pDescCache->paConfigs[i].Core.bNumInterfaces;
    }

    return cMax;
}


/**
 * Executes a given function on the I/O thread.
 *
 * @returns IPRT status code.
 * @param   pDev           The USB device instance data.
 * @param   fFlags         Combination of VUSB_DEV_IO_THREAD_EXEC_FLAGS_*
 * @param   pfnFunction    The function to execute.
 * @param   cArgs          Number of arguments to the function.
 * @param   Args           The parameter list.
 *
 * @remarks See remarks on RTReqQueueCallV
 */
DECLHIDDEN(int) vusbDevIoThreadExecV(PVUSBDEV pDev, uint32_t fFlags, PFNRT pfnFunction, unsigned cArgs, va_list Args)
{
    int rc = VINF_SUCCESS;
    PRTREQ hReq = NULL;

    Assert(pDev->hUrbIoThread != NIL_RTTHREAD);
    if (RT_LIKELY(pDev->hUrbIoThread != NIL_RTTHREAD))
    {
        uint32_t fReqFlags = RTREQFLAGS_IPRT_STATUS;

        if (!(fFlags & VUSB_DEV_IO_THREAD_EXEC_FLAGS_SYNC))
            fReqFlags |= RTREQFLAGS_NO_WAIT;

        rc = RTReqQueueCallV(pDev->hReqQueueSync, &hReq, 0 /* cMillies */, fReqFlags, pfnFunction, cArgs, Args);
        Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);

        /* In case we are called on the I/O thread just process the request. */
        if (   pDev->hUrbIoThread == RTThreadSelf()
            && (fFlags & VUSB_DEV_IO_THREAD_EXEC_FLAGS_SYNC))
        {
            int rc2 = RTReqQueueProcess(pDev->hReqQueueSync, 0);
            Assert(RT_SUCCESS(rc2) || rc2 == VERR_TIMEOUT); NOREF(rc2);
        }
        else
            vusbDevUrbIoThreadWakeup(pDev);

        if (   rc == VERR_TIMEOUT
            && (fFlags & VUSB_DEV_IO_THREAD_EXEC_FLAGS_SYNC))
        {
            rc = RTReqWait(hReq, RT_INDEFINITE_WAIT);
            AssertRC(rc);
        }
        RTReqRelease(hReq);
    }
    else
        rc = VERR_INVALID_STATE;

    return rc;
}


/**
 * Executes a given function on the I/O thread.
 *
 * @returns IPRT status code.
 * @param   pDev           The USB device instance data.
 * @param   fFlags         Combination of VUSB_DEV_IO_THREAD_EXEC_FLAGS_*
 * @param   pfnFunction    The function to execute.
 * @param   cArgs          Number of arguments to the function.
 * @param   ...            The parameter list.
 *
 * @remarks See remarks on RTReqQueueCallV
 */
DECLHIDDEN(int) vusbDevIoThreadExec(PVUSBDEV pDev, uint32_t fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
{
    int rc = VINF_SUCCESS;
    va_list va;

    va_start(va, cArgs);
    rc = vusbDevIoThreadExecV(pDev, fFlags, pfnFunction, cArgs, va);
    va_end(va);
    return rc;
}


/**
 * Executes a given function synchronously on the I/O thread waiting for it to complete.
 *
 * @returns IPRT status code.
 * @param   pDev           The USB device instance data
 * @param   pfnFunction    The function to execute.
 * @param   cArgs          Number of arguments to the function.
 * @param   ...            The parameter list.
 *
 * @remarks See remarks on RTReqQueueCallV
 */
DECLHIDDEN(int) vusbDevIoThreadExecSync(PVUSBDEV pDev, PFNRT pfnFunction, unsigned cArgs, ...)
{
    int rc = VINF_SUCCESS;
    va_list va;

    va_start(va, cArgs);
    rc = vusbDevIoThreadExecV(pDev, VUSB_DEV_IO_THREAD_EXEC_FLAGS_SYNC, pfnFunction, cArgs, va);
    va_end(va);
    return rc;
}


static DECLCALLBACK(int) vusbDevGetDescriptorCacheWorker(PPDMUSBINS pUsbIns, PCPDMUSBDESCCACHE *ppDescCache)
{
    *ppDescCache = pUsbIns->pReg->pfnUsbGetDescriptorCache(pUsbIns);
    return VINF_SUCCESS;
}

/**
 * Initialize a new VUSB device.
 *
 * @returns VBox status code.
 * @param   pDev    The VUSB device to initialize.
 * @param   pUsbIns Pointer to the PDM USB Device instance.
 */
int vusbDevInit(PVUSBDEV pDev, PPDMUSBINS pUsbIns, const char *pszCaptureFilename)
{
    /*
     * Initialize the device data members.
     * (All that are Non-Zero at least.)
     */
    Assert(!pDev->IDevice.pfnReset);
    Assert(!pDev->IDevice.pfnPowerOn);
    Assert(!pDev->IDevice.pfnPowerOff);
    Assert(!pDev->IDevice.pfnGetState);
    Assert(!pDev->IDevice.pfnIsSavedStateSupported);

    pDev->IDevice.pfnReset = vusbIDeviceReset;
    pDev->IDevice.pfnPowerOn = vusbIDevicePowerOn;
    pDev->IDevice.pfnPowerOff = vusbIDevicePowerOff;
    pDev->IDevice.pfnGetState = vusbIDeviceGetState;
    pDev->IDevice.pfnIsSavedStateSupported = vusbIDeviceIsSavedStateSupported;
    pDev->IDevice.pfnGetSpeed = vusbIDeviceGetSpeed;
    pDev->pUsbIns = pUsbIns;
    pDev->pNext = NULL;
    pDev->pNextHash = NULL;
    pDev->pHub = NULL;
    pDev->enmState = VUSB_DEVICE_STATE_DETACHED;
    pDev->cRefs = 1;
    pDev->u8Address = VUSB_INVALID_ADDRESS;
    pDev->u8NewAddress = VUSB_INVALID_ADDRESS;
    pDev->i16Port = -1;
    pDev->u16Status = 0;
    pDev->pDescCache = NULL;
    pDev->pCurCfgDesc = NULL;
    pDev->paIfStates = NULL;
    RTListInit(&pDev->LstAsyncUrbs);
    memset(&pDev->aPipes[0], 0, sizeof(pDev->aPipes));
    for (unsigned i = 0; i < RT_ELEMENTS(pDev->aPipes); i++)
    {
        int rc = RTCritSectInit(&pDev->aPipes[i].CritSectCtrl);
        AssertRCReturn(rc, rc);
    }
    pDev->pResetTimer = NULL;
    pDev->hSniffer = VUSBSNIFFER_NIL;

    int rc = RTCritSectInit(&pDev->CritSectAsyncUrbs);
    AssertRCReturn(rc, rc);

    /* Create the URB pool. */
    rc = vusbUrbPoolInit(&pDev->UrbPool);
    AssertRCReturn(rc, rc);

    /* Setup request queue executing synchronous tasks on the I/O thread. */
    rc = RTReqQueueCreate(&pDev->hReqQueueSync);
    AssertRCReturn(rc, rc);

    /* Create I/O thread. */
    rc = vusbDevUrbIoThreadCreate(pDev);
    AssertRCReturn(rc, rc);

    /*
     * Create the reset timer.
     */
    rc = PDMUsbHlpTMTimerCreate(pDev->pUsbIns, TMCLOCK_VIRTUAL, vusbDevResetDoneTimer, pDev, 0 /*fFlags*/,
                                "USB Device Reset Timer",  &pDev->pResetTimer);
    AssertRCReturn(rc, rc);

    if (pszCaptureFilename)
    {
        rc = VUSBSnifferCreate(&pDev->hSniffer, 0, pszCaptureFilename, NULL, NULL);
        AssertRCReturn(rc, rc);
    }

    /*
     * Get the descriptor cache from the device. (shall cannot fail)
     */
    rc = vusbDevIoThreadExecSync(pDev, (PFNRT)vusbDevGetDescriptorCacheWorker, 2, pUsbIns, &pDev->pDescCache);
    AssertRC(rc);
    AssertPtr(pDev->pDescCache);
#ifdef VBOX_STRICT
    if (pDev->pDescCache->fUseCachedStringsDescriptors)
    {
        int32_t iPrevId = -1;
        for (unsigned iLang = 0; iLang < pDev->pDescCache->cLanguages; iLang++)
        {
            Assert((int32_t)pDev->pDescCache->paLanguages[iLang].idLang > iPrevId);
            iPrevId = pDev->pDescCache->paLanguages[iLang].idLang;

            int32_t                 idxPrevStr = -1;
            PCPDMUSBDESCCACHESTRING paStrings  = pDev->pDescCache->paLanguages[iLang].paStrings;
            unsigned                cStrings   = pDev->pDescCache->paLanguages[iLang].cStrings;
            for (unsigned iStr = 0; iStr < cStrings; iStr++)
            {
                Assert((int32_t)paStrings[iStr].idx > idxPrevStr);
                idxPrevStr = paStrings[iStr].idx;
                size_t cch = strlen(paStrings[iStr].psz);
                Assert(cch <= 127);
            }
        }
    }
#endif

    /*
     * Allocate memory for the interface states.
     */
    size_t cbIface = vusbDevMaxInterfaces(pDev) * sizeof(*pDev->paIfStates);
    pDev->paIfStates = (PVUSBINTERFACESTATE)RTMemAllocZ(cbIface);
    AssertMsgReturn(pDev->paIfStates, ("RTMemAllocZ(%d) failed\n", cbIface), VERR_NO_MEMORY);

    return VINF_SUCCESS;
}

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

