/**
 * SET_INTERFACE.
 *
 * @returns success indicator.
 */
static DECLCALLBACK(int) usbProxyFreeBSDSetInterface(PUSBPROXYDEV pProxyDev, int iIf, int iAlt)
{
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    struct usb_alt_interface UsbIntAlt;
    int rc;

    LogFlow(("usbProxyFreeBSDSetInterface: pProxyDev=%p iIf=%x iAlt=%x\n",
             pProxyDev, iIf, iAlt));

    /* We need to release kernel ressources first. */
    rc = usbProxyFreeBSDFsUnInit(pProxyDev);
    if (RT_FAILURE(rc))
    {
        LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
                 "failed failed rc=%d\n", rc));
        return rc;
    }
    memset(&UsbIntAlt, 0, sizeof(UsbIntAlt));
    UsbIntAlt.uai_interface_index = iIf;
    UsbIntAlt.uai_alt_index = iAlt;

    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_ALTINTERFACE, &UsbIntAlt, true);
    if (RT_FAILURE(rc))
    {
        LogFlow(("usbProxyFreeBSDSetInterface: Setting interface %d %d "
                 "failed rc=%d\n", iIf, iAlt, rc));
        return rc;
    }

    return usbProxyFreeBSDFsInit(pProxyDev);
}
/**
 * Claims an interface.
 * @returns success indicator.
 */
static DECLCALLBACK(int) usbProxyFreeBSDClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
{
    int rc;

    LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s "
             "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));

    /*
     * Try to detach kernel driver on this interface, ignore any
     * failures
     */
    usbProxyFreeBSDDoIoCtl(pProxyDev, USB_IFACE_DRIVER_DETACH, &iIf, true);

    /* Try to claim interface */
    return usbProxyFreeBSDDoIoCtl(pProxyDev, USB_CLAIM_INTERFACE, &iIf, true);
}
/**
 * Claims all the interfaces and figures out the
 * current configuration.
 *
 * @returns VINF_SUCCESS.
 * @param   pProxyDev       The proxy device.
 */
static DECLCALLBACK(int) usbProxyFreeBSDInit(PUSBPROXYDEV pProxyDev)
{
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    int rc;

    LogFlow(("usbProxyFreeBSDInit: pProxyDev=%s\n",
             pProxyDev->pUsbIns->pszName));

    /* Retrieve current active configuration. */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_GET_CONFIG,
                                &pProxyDev->iActiveCfg, true);
    if (RT_FAILURE(rc) || pProxyDev->iActiveCfg == 255)
    {
        pProxyDev->cIgnoreSetConfigs = 0;
        pProxyDev->iActiveCfg = -1;
    }
    else
    {
        pProxyDev->cIgnoreSetConfigs = 1;
        pProxyDev->iActiveCfg++;
    }

    Log(("usbProxyFreeBSDInit: iActiveCfg=%d\n", pProxyDev->iActiveCfg));

    return rc;
}
/**
 * Init USB subsystem.
 */
static int usbProxyFreeBSDFsInit(PUSBPROXYDEV pProxyDev)
{
    struct usb_fs_init UsbFsInit;
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    int rc;

    LogFlow(("usbProxyFreeBSDFsInit: pProxyDev=%p\n", (void *)pProxyDev));

    /* Sanity check */
    AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER);

    if (pDevFBSD->fInit == true)
        return VINF_SUCCESS;

    /* Zero default */
    memset(&UsbFsInit, 0, sizeof(UsbFsInit));

    UsbFsInit.pEndpoints = pDevFBSD->aHwEndpoint;
    UsbFsInit.ep_index_max = USBFBSD_MAXENDPOINTS;

    /* Init USB subsystem */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_INIT, &UsbFsInit, false);
    if (RT_SUCCESS(rc))
        pDevFBSD->fInit = true;

    return rc;
}
/**
 * Close an endpoint.
 *
 * @returns VBox status code.
 */
static int usbProxyFreeBSDEndpointClose(PUSBPROXYDEV pProxyDev, int Endpoint)
{
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    PUSBENDPOINTFBSD pEndpointFBSD = &pDevFBSD->aSwEndpoint[Endpoint];
    struct usb_fs_close UsbFsClose;
    int rc = VINF_SUCCESS;

    LogFlow(("usbProxyFreeBSDEndpointClose: pProxyDev=%p Endpoint=%d\n",
             (void *)pProxyDev, Endpoint));

    /* check for cancelling */
    if (pEndpointFBSD->pUrb != NULL)
    {
        pEndpointFBSD->fCancelling = true;
        pDevFBSD->fCancelling = true;
    }

    /* check for opened */
    if (pEndpointFBSD->fOpen)
    {
        pEndpointFBSD->fOpen = false;

        /* Zero default */
        memset(&UsbFsClose, 0, sizeof(UsbFsClose));

        /* Set endpoint index */
        UsbFsClose.ep_index = Endpoint;

        /* Close endpoint */
        rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_CLOSE, &UsbFsClose, true);
    }
    return rc;
}
/**
 * Uninit USB subsystem.
 */
static int usbProxyFreeBSDFsUnInit(PUSBPROXYDEV pProxyDev)
{
    struct usb_fs_uninit UsbFsUninit;
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    int rc;

    LogFlow(("usbProxyFreeBSDFsUnInit: ProxyDev=%p\n", (void *)pProxyDev));

    /* Sanity check */
    AssertPtrReturn(pDevFBSD, VERR_INVALID_PARAMETER);

    if (pDevFBSD->fInit != true)
        return VINF_SUCCESS;

    /* Close any open endpoints. */
    for (unsigned n = 0; n != USBFBSD_MAXENDPOINTS; n++)
        usbProxyFreeBSDEndpointClose(pProxyDev, n);

    /* Zero default */
    memset(&UsbFsUninit, 0, sizeof(UsbFsUninit));

    /* Uninit USB subsystem */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_UNINIT, &UsbFsUninit, false);
    if (RT_SUCCESS(rc))
        pDevFBSD->fInit = false;

    return rc;
}
/**
 * Clears the halted endpoint 'ep_num'.
 */
static bool usbProxyFreeBSDClearHaltedEp(PUSBPROXYDEV pProxyDev, unsigned int ep_num)
{
    struct usb_ctl_request Req;
    int rc;

    LogFlow(("usbProxyFreeBSDClearHaltedEp: pProxyDev=%s ep_num=%u\n",
             pProxyDev->pUsbIns->pszName, ep_num));

    /*
     * Clearing the zero control pipe doesn't make sense.
     * Just ignore it.
     */
    if ((ep_num & 0xF) == 0)
        return true;

    memset(&Req, 0, sizeof(Req));

    usbProxyFreeBSDSetupReq(&Req.ucr_request,
                            VUSB_DIR_TO_DEV | VUSB_TO_ENDPOINT,
                            VUSB_REQ_CLEAR_FEATURE, 0, ep_num, 0);

    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DO_REQUEST, &Req, true);

    LogFlow(("usbProxyFreeBSDClearHaltedEp: rc=%Rrc\n", rc));

    if (RT_FAILURE(rc))
        return false;

    return true;
}
/**
 * Reset a device.
 *
 * @returns VBox status code.
 * @param   pDev    The device to reset.
 */
static DECLCALLBACK(int) usbProxyFreeBSDReset(PUSBPROXYDEV pProxyDev, bool fResetOnFreeBSD)
{
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    int iParm;
    int rc = VINF_SUCCESS;

    LogFlow(("usbProxyFreeBSDReset: pProxyDev=%s\n",
             pProxyDev->pUsbIns->pszName));

    if (!fResetOnFreeBSD)
        goto done;

    /* We need to release kernel ressources first. */
    rc = usbProxyFreeBSDFsUnInit(pProxyDev);
    if (RT_FAILURE(rc))
        goto done;

    /* Resetting is only possible as super-user, ignore any failures: */
    iParm = 0;
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_DEVICEENUMERATE, &iParm, true);
    if (RT_FAILURE(rc))
    {
        /* Set the config instead of bus reset */
        iParm = 255;
        rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true);
        if (RT_SUCCESS(rc))
        {
            iParm = 0;
            rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iParm, true);
        }
    }
    usleep(10000); /* nice it! */

    /* Allocate kernel ressources again. */
    rc = usbProxyFreeBSDFsInit(pProxyDev);
    if (RT_FAILURE(rc))
        goto done;

    /* Retrieve current active configuration. */
    rc = usbProxyFreeBSDInit(pProxyDev);

done:
    pProxyDev->cIgnoreSetConfigs = 2;

    return rc;
}
/**
 * Releases an interface.
 * @returns success indicator.
 */
static DECLCALLBACK(int) usbProxyFreeBSDReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
{
    int rc;

    LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s "
        "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));

    return usbProxyFreeBSDDoIoCtl(pProxyDev, USB_RELEASE_INTERFACE, &iIf, true);
}
/**
 * Claims an interface.
 * @returns success indicator.
 */
static int usbProxyFreeBSDClaimInterface(PUSBPROXYDEV pProxyDev, int iIf)
{
    int rc;

    LogFlow(("usbProxyFreeBSDClaimInterface: pProxyDev=%s "
             "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));

    /*
     * Try to detach kernel driver on this interface, ignore any
     * failures
     */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_IFACE_DRIVER_DETACH, &iIf, true);

    /* Try to claim interface */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_CLAIM_INTERFACE, &iIf, true);
    if (RT_FAILURE(rc))
        return false;

    return true;
}
/**
 * SET_CONFIGURATION.
 *
 * The caller makes sure that it's not called first time after open or reset
 * with the active interface.
 *
 * @returns success indicator.
 * @param   pProxyDev       The device instance data.
 * @param   iCfg            The configuration to set.
 */
static int usbProxyFreeBSDSetConfig(PUSBPROXYDEV pProxyDev, int iCfg)
{
    PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
    int iCfgIndex;
    int rc;

    LogFlow(("usbProxyFreeBSDSetConfig: pProxyDev=%s cfg=%x\n",
             pProxyDev->pUsbIns->pszName, iCfg));

    /* We need to release kernel ressources first. */
    rc = usbProxyFreeBSDFsUnInit(pProxyDev);
    if (RT_FAILURE(rc))
    {
        LogFlow(("usbProxyFreeBSDSetInterface: Freeing kernel resources "
                 "failed failed rc=%d\n", rc));
        return false;
    }

    if (iCfg == 0)
    {
        /* Unconfigure */
        iCfgIndex = 255;
    }
    else
    {
        /* Get the configuration index matching the value. */
        for (iCfgIndex = 0; iCfgIndex < pProxyDev->DevDesc.bNumConfigurations; iCfgIndex++)
        {
            if (pProxyDev->paCfgDescs[iCfgIndex].Core.bConfigurationValue == iCfg)
                break;
        }

        if (iCfgIndex == pProxyDev->DevDesc.bNumConfigurations)
        {
            LogFlow(("usbProxyFreeBSDSetConfig: configuration "
                     "%d not found\n", iCfg));
            return false;
        }
    }

    /* Set the config */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_SET_CONFIG, &iCfgIndex, true);
    if (RT_FAILURE(rc))
        return false;

    /* Allocate kernel ressources again. */
    rc = usbProxyFreeBSDFsInit(pProxyDev);
    if (RT_FAILURE(rc))
        return false;

    return true;
}
/**
 * Releases an interface.
 * @returns success indicator.
 */
static int usbProxyFreeBSDReleaseInterface(PUSBPROXYDEV pProxyDev, int iIf)
{
    int rc;

    LogFlow(("usbProxyFreeBSDReleaseInterface: pProxyDev=%s "
        "ifnum=%x\n", pProxyDev->pUsbIns->pszName, iIf));

    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_RELEASE_INTERFACE, &iIf, true);
    if (RT_FAILURE(rc))
        return false;

    return true;
}
/**
 * Reap URBs in-flight on a device.
 *
 * @returns Pointer to a completed URB.
 * @returns NULL if no URB was completed.
 * @param   pProxyDev   The device.
 * @param   cMillies    Number of milliseconds to wait. Use 0 to not wait at all.
 */
static DECLCALLBACK(PVUSBURB) usbProxyFreeBSDUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
{
    struct usb_fs_endpoint *pXferEndpoint;
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    PUSBENDPOINTFBSD pEndpointFBSD;
    PVUSBURB pUrb;
    struct usb_fs_complete UsbFsComplete;
    struct pollfd pfd[2];
    int rc;

    LogFlow(("usbProxyFreeBSDUrbReap: pProxyDev=%p, cMillies=%u\n",
             pProxyDev, cMillies));

repeat:

    pUrb = NULL;

    /* check for cancelled transfers */
    if (pDevFBSD->fCancelling)
    {
        for (unsigned n = 0; n < USBFBSD_MAXENDPOINTS; n++)
        {
            pEndpointFBSD = &pDevFBSD->aSwEndpoint[n];
            if (pEndpointFBSD->fCancelling)
            {
                pEndpointFBSD->fCancelling = false;
                pUrb = pEndpointFBSD->pUrb;
                pEndpointFBSD->pUrb = NULL;

                if (pUrb != NULL)
                    break;
            }
        }

        if (pUrb != NULL)
        {
            pUrb->enmStatus = VUSBSTATUS_INVALID;
            pUrb->Dev.pvPrivate = NULL;

            switch (pUrb->enmType)
            {
                case VUSBXFERTYPE_MSG:
                    pUrb->cbData = 0;
                    break;
                case VUSBXFERTYPE_ISOC:
                    pUrb->cbData = 0;
                    for (int n = 0; n < (int)pUrb->cIsocPkts; n++)
                        pUrb->aIsocPkts[n].cb = 0;
                    break;
                default:
                    pUrb->cbData = 0;
                    break;
            }
            return pUrb;
        }
        pDevFBSD->fCancelling = false;
    }
    /* Zero default */

    memset(&UsbFsComplete, 0, sizeof(UsbFsComplete));

    /* Check if any endpoints are complete */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true);
    if (RT_SUCCESS(rc))
    {
        pXferEndpoint = &pDevFBSD->aHwEndpoint[UsbFsComplete.ep_index];
        pEndpointFBSD = &pDevFBSD->aSwEndpoint[UsbFsComplete.ep_index];

        LogFlow(("usbProxyFreeBSDUrbReap: Reaped "
                 "URB %#p\n", pEndpointFBSD->pUrb));

        if (pXferEndpoint->status == USB_ERR_CANCELLED)
            goto repeat;

        pUrb = pEndpointFBSD->pUrb;
        pEndpointFBSD->pUrb = NULL;
        if (pUrb == NULL)
            goto repeat;

        switch (pXferEndpoint->status)
        {
            case USB_ERR_NORMAL_COMPLETION:
                pUrb->enmStatus = VUSBSTATUS_OK;
                break;
            case USB_ERR_STALLED:
                pUrb->enmStatus = VUSBSTATUS_STALL;
                break;
            default:
                pUrb->enmStatus = VUSBSTATUS_INVALID;
                break;
        }

        pUrb->Dev.pvPrivate = NULL;

        switch (pUrb->enmType)
        {
            case VUSBXFERTYPE_MSG:
                pUrb->cbData = pEndpointFBSD->acbData[0] + pEndpointFBSD->acbData[1];
                break;
            case VUSBXFERTYPE_ISOC:
            {
                int n;

                if (pUrb->enmDir == VUSBDIRECTION_OUT)
                    break;
                pUrb->cbData = 0;
                for (n = 0; n < (int)pUrb->cIsocPkts; n++)
                {
                    if (n >= (int)pEndpointFBSD->cMaxFrames)
                        break;
                    pUrb->cbData += pEndpointFBSD->acbData[n];
                    pUrb->aIsocPkts[n].cb = pEndpointFBSD->acbData[n];
                }
                for (; n < (int)pUrb->cIsocPkts; n++)
                    pUrb->aIsocPkts[n].cb = 0;

                break;
            }
            default:
                pUrb->cbData = pEndpointFBSD->acbData[0];
                break;
        }

        LogFlow(("usbProxyFreeBSDUrbReap: Status=%d epindex=%u "
                 "len[0]=%d len[1]=%d\n",
                 (int)pXferEndpoint->status,
                 (unsigned)UsbFsComplete.ep_index,
                 (unsigned)pEndpointFBSD->acbData[0],
                 (unsigned)pEndpointFBSD->acbData[1]));

    }
    else if (cMillies != 0 && rc == VERR_RESOURCE_BUSY)
    {
        for (;;)
        {
            pfd[0].fd = RTFileToNative(pDevFBSD->hFile);
            pfd[0].events = POLLIN | POLLRDNORM;
            pfd[0].revents = 0;

            pfd[1].fd = RTPipeToNative(pDevFBSD->hPipeWakeupR);
            pfd[1].events = POLLIN | POLLRDNORM;
            pfd[1].revents = 0;

            rc = poll(pfd, 2, (cMillies == RT_INDEFINITE_WAIT) ? INFTIM : cMillies);
            if (rc > 0)
            {
                if (pfd[1].revents & POLLIN)
                {
                    /* Got woken up, drain pipe. */
                    uint8_t bRead;
                    size_t cbIgnored = 0;
                    RTPipeRead(pDevFBSD->hPipeWakeupR, &bRead, 1, &cbIgnored);
                    /* Make sure we return from this function */
                    cMillies = 0;
                }
                break;
            }
            if (rc == 0)
                return NULL;
            if (errno != EAGAIN)
                return NULL;
        }
        goto repeat;
    }
    return pUrb;
}
/**
 * @interface_method_impl{USBPROXYBACK,pfnUrbQueue}
 */
static DECLCALLBACK(int) usbProxyFreeBSDUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb)
{
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    PUSBENDPOINTFBSD pEndpointFBSD;
    struct usb_fs_endpoint *pXferEndpoint;
    struct usb_fs_start UsbFsStart;
    unsigned cFrames;
    uint8_t *pbData;
    int index;
    int ep_num;
    int rc;

    LogFlow(("usbProxyFreeBSDUrbQueue: pUrb=%p EndPt=%u Dir=%u\n",
             pUrb, (unsigned)pUrb->EndPt, (unsigned)pUrb->enmDir));

    ep_num = pUrb->EndPt;
    if ((pUrb->enmType != VUSBXFERTYPE_MSG) && (pUrb->enmDir == VUSBDIRECTION_IN)) {
        /* set IN-direction bit */
        ep_num |= 0x80;
    }

    index = 0;

retry:

    index = usbProxyFreeBSDEndpointOpen(pProxyDev, ep_num,
                                        (pUrb->enmType == VUSBXFERTYPE_ISOC),
                                        index);

    if (index < 0)
        return VERR_INVALID_PARAMETER;

    pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
    pXferEndpoint = &pDevFBSD->aHwEndpoint[index];

    pbData = pUrb->abData;

    switch (pUrb->enmType)
    {
        case VUSBXFERTYPE_MSG:
        {
            pEndpointFBSD->apvData[0] = pbData;
            pEndpointFBSD->acbData[0] = 8;

            /* check wLength */
            if (pbData[6] || pbData[7])
            {
                pEndpointFBSD->apvData[1] = pbData + 8;
                pEndpointFBSD->acbData[1] = pbData[6] | (pbData[7] << 8);
                cFrames = 2;
            }
            else
            {
                pEndpointFBSD->apvData[1] = NULL;
                pEndpointFBSD->acbData[1] = 0;
                cFrames = 1;
            }

            LogFlow(("usbProxyFreeBSDUrbQueue: pUrb->cbData=%u, 0x%02x, "
                     "0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x, 0x%02x\n",
                     pUrb->cbData, pbData[0], pbData[1], pbData[2], pbData[3],
                     pbData[4], pbData[5], pbData[6], pbData[7]));

            pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
            pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
            break;
        }
        case VUSBXFERTYPE_ISOC:
        {
            unsigned i;

            for (i = 0; i < pUrb->cIsocPkts; i++)
            {
                if (i >= pEndpointFBSD->cMaxFrames)
                    break;
                pEndpointFBSD->apvData[i] = pbData + pUrb->aIsocPkts[i].off;
                pEndpointFBSD->acbData[i] = pUrb->aIsocPkts[i].cb;
            }
            /* Timeout handling will be done during reap. */
            pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
            pXferEndpoint->flags = USB_FS_FLAG_MULTI_SHORT_OK;
            cFrames = i;
            break;
        }
        default:
        {
            pEndpointFBSD->apvData[0] = pbData;
            pEndpointFBSD->cbData0 = pUrb->cbData;

            /* XXX maybe we have to loop */
            if (pUrb->cbData > pEndpointFBSD->cMaxIo)
                pEndpointFBSD->acbData[0] = pEndpointFBSD->cMaxIo;
            else
                pEndpointFBSD->acbData[0] = pUrb->cbData;

            /* Timeout handling will be done during reap. */
            pXferEndpoint->timeout = USB_FS_TIMEOUT_NONE;
            pXferEndpoint->flags = pUrb->fShortNotOk ? 0 : USB_FS_FLAG_MULTI_SHORT_OK;
            cFrames = 1;
            break;
        }
    }

    /* store number of frames */
    pXferEndpoint->nFrames = cFrames;

    /* zero-default */
    memset(&UsbFsStart, 0, sizeof(UsbFsStart));

    /* Start the transfer */
    UsbFsStart.ep_index = index;

    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_START, &UsbFsStart, true);

    LogFlow(("usbProxyFreeBSDUrbQueue: USB_FS_START returned rc=%d "
             "len[0]=%u len[1]=%u cbData=%u index=%u ep_num=%u\n", rc,
             (unsigned)pEndpointFBSD->acbData[0],
             (unsigned)pEndpointFBSD->acbData[1],
             (unsigned)pUrb->cbData,
             (unsigned)index, (unsigned)ep_num));

    if (RT_FAILURE(rc))
    {
        if (rc == VERR_RESOURCE_BUSY)
        {
            index++;
            goto retry;
        }
        return rc;
    }
    pUrb->Dev.pvPrivate = (void *)(long)(index + 1);
    pEndpointFBSD->pUrb = pUrb;

    return rc;
}
static int usbProxyFreeBSDEndpointOpen(PUSBPROXYDEV pProxyDev, int Endpoint, bool fIsoc, int index)
{
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    PUSBENDPOINTFBSD pEndpointFBSD = NULL; /* shut up gcc */
    struct usb_fs_endpoint *pXferEndpoint;
    struct usb_fs_open UsbFsOpen;
    int rc;

    LogFlow(("usbProxyFreeBSDEndpointOpen: pProxyDev=%p Endpoint=%d\n",
             (void *)pProxyDev, Endpoint));

    for (; index < USBFBSD_MAXENDPOINTS; index++)
    {
        pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
        if (pEndpointFBSD->fCancelling)
            continue;
        if (   pEndpointFBSD->fOpen
            && !pEndpointFBSD->pUrb
            && (int)pEndpointFBSD->iEpNum == Endpoint)
            return index;
    }

    if (index == USBFBSD_MAXENDPOINTS)
    {
        for (index = 0; index != USBFBSD_MAXENDPOINTS; index++)
        {
            pEndpointFBSD = &pDevFBSD->aSwEndpoint[index];
            if (pEndpointFBSD->fCancelling)
                continue;
            if (!pEndpointFBSD->fOpen)
                break;
        }
        if (index == USBFBSD_MAXENDPOINTS)
            return -1;
    }
    /* set ppBuffer and pLength */

    pXferEndpoint = &pDevFBSD->aHwEndpoint[index];
    pXferEndpoint->ppBuffer = &pEndpointFBSD->apvData[0];
    pXferEndpoint->pLength = &pEndpointFBSD->acbData[0];

    LogFlow(("usbProxyFreeBSDEndpointOpen: ep_index=%d ep_num=%d\n",
             index, Endpoint));

    memset(&UsbFsOpen, 0, sizeof(UsbFsOpen));

    UsbFsOpen.ep_index = index;
    UsbFsOpen.ep_no = Endpoint;
    UsbFsOpen.max_bufsize = 256 * 1024;
    /* Hardcoded assumption about the URBs we get. */

    UsbFsOpen.max_frames = fIsoc ? USBFBSD_MAXFRAMES : 2;

    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_OPEN, &UsbFsOpen, true);
    if (RT_FAILURE(rc))
    {
        if (rc == VERR_RESOURCE_BUSY)
            LogFlow(("usbProxyFreeBSDEndpointOpen: EBUSY\n"));

        return -1;
    }
    pEndpointFBSD->fOpen = true;
    pEndpointFBSD->pUrb = NULL;
    pEndpointFBSD->iEpNum = Endpoint;
    pEndpointFBSD->cMaxIo = UsbFsOpen.max_bufsize;
    pEndpointFBSD->cMaxFrames = UsbFsOpen.max_frames;

    return index;
}
/**
 * Reap URBs in-flight on a device.
 *
 * @returns Pointer to a completed URB.
 * @returns NULL if no URB was completed.
 * @param   pProxyDev   The device.
 * @param   cMillies    Number of milliseconds to wait. Use 0 to not wait at all.
 */
static PVUSBURB usbProxyFreeBSDUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies)
{
    struct usb_fs_endpoint *pXferEndpoint;
    PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD) pProxyDev->Backend.pv;
    PUSBENDPOINTFBSD pEndpointFBSD;
    PVUSBURB pUrb;
    struct usb_fs_complete UsbFsComplete;
    struct pollfd PollFd;
    int rc;

    LogFlow(("usbProxyFreeBSDUrbReap: pProxyDev=%p, cMillies=%u\n",
             pProxyDev, cMillies));

repeat:

    pUrb = NULL;

    /* check for cancelled transfers */
    if (pDevFBSD->fCancelling)
    {
        for (unsigned n = 0; n < USBFBSD_MAXENDPOINTS; n++)
        {
            pEndpointFBSD = &pDevFBSD->aSwEndpoint[n];
            if (pEndpointFBSD->fCancelling)
            {
                pEndpointFBSD->fCancelling = false;
                pUrb = pEndpointFBSD->pUrb;
                pEndpointFBSD->pUrb = NULL;

                if (pUrb != NULL)
                    break;
            }
        }

        if (pUrb != NULL)
        {
            pUrb->enmStatus = VUSBSTATUS_INVALID;
            pUrb->Dev.pvPrivate = NULL;

            switch (pUrb->enmType)
            {
                case VUSBXFERTYPE_MSG:
                    pUrb->cbData = 0;
                    break;
                case VUSBXFERTYPE_ISOC:
                    pUrb->cbData = 0;
                    for (int n = 0; n < (int)pUrb->cIsocPkts; n++)
                        pUrb->aIsocPkts[n].cb = 0;
                    break;
                default:
                    pUrb->cbData = 0;
                    break;
            }
            return pUrb;
        }
        pDevFBSD->fCancelling = false;
    }
    /* Zero default */

    memset(&UsbFsComplete, 0, sizeof(UsbFsComplete));

    /* Check if any endpoints are complete */
    rc = usbProxyFreeBSDDoIoCtl(pProxyDev, USB_FS_COMPLETE, &UsbFsComplete, true);
    if (RT_SUCCESS(rc))
    {
        pXferEndpoint = &pDevFBSD->aHwEndpoint[UsbFsComplete.ep_index];
        pEndpointFBSD = &pDevFBSD->aSwEndpoint[UsbFsComplete.ep_index];

        LogFlow(("usbProxyFreeBSDUrbReap: Reaped "
                 "URB %#p\n", pEndpointFBSD->pUrb));

        if (pXferEndpoint->status == USB_ERR_CANCELLED)
            goto repeat;

        pUrb = pEndpointFBSD->pUrb;
        pEndpointFBSD->pUrb = NULL;
        if (pUrb == NULL)
            goto repeat;

        switch (pXferEndpoint->status)
        {
            case USB_ERR_NORMAL_COMPLETION:
                pUrb->enmStatus = VUSBSTATUS_OK;
                break;
            case USB_ERR_STALLED:
                pUrb->enmStatus = VUSBSTATUS_STALL;
                break;
            default:
                pUrb->enmStatus = VUSBSTATUS_INVALID;
                break;
        }

        pUrb->Dev.pvPrivate = NULL;

        switch (pUrb->enmType)
        {
            case VUSBXFERTYPE_MSG:
                pUrb->cbData = pEndpointFBSD->acbData[0] + pEndpointFBSD->acbData[1];
                break;
            case VUSBXFERTYPE_ISOC:
            {
                int n;

                if (pUrb->enmDir == VUSBDIRECTION_OUT)
                    break;
                pUrb->cbData = 0;
                for (n = 0; n < (int)pUrb->cIsocPkts; n++)
                {
                    if (n >= (int)pEndpointFBSD->cMaxFrames)
                        break;
                    pUrb->cbData += pEndpointFBSD->acbData[n];
                    pUrb->aIsocPkts[n].cb = pEndpointFBSD->acbData[n];
                }
                for (; n < (int)pUrb->cIsocPkts; n++)
                    pUrb->aIsocPkts[n].cb = 0;

                break;
            }
            default:
                pUrb->cbData = pEndpointFBSD->acbData[0];
                break;
        }

        LogFlow(("usbProxyFreeBSDUrbReap: Status=%d epindex=%u "
                 "len[0]=%d len[1]=%d\n",
                 (int)pXferEndpoint->status,
                 (unsigned)UsbFsComplete.ep_index,
                 (unsigned)pEndpointFBSD->acbData[0],
                 (unsigned)pEndpointFBSD->acbData[1]));

    }
    else if (cMillies && rc == VERR_RESOURCE_BUSY)
    {
        /* Poll for finished transfers */
        PollFd.fd = RTFileToNative(pDevFBSD->hFile);
        PollFd.events = POLLIN | POLLRDNORM;
        PollFd.revents = 0;

        rc = poll(&PollFd, 1, (cMillies == RT_INDEFINITE_WAIT) ? INFTIM : cMillies);
        if (rc >= 1)
        {
            goto repeat;
        }
        else
        {
            LogFlow(("usbProxyFreeBSDUrbReap: "
                     "poll returned rc=%d\n", rc));
        }
    }
    return pUrb;
}