/**
 * 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);
}
/**
 * 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;
}
/**
 * Opens the device file.
 *
 * @returns VBox status code.
 * @param   pProxyDev       The device instance.
 * @param   pszAddress      If we are using usbfs, this is the path to the
 *                          device.  If we are using sysfs, this is a string of
 *                          the form "sysfs:<sysfs path>//device:<device node>".
 *                          In the second case, the two paths are guaranteed
 *                          not to contain the substring "//".
 * @param   pvBackend       Backend specific pointer, unused for the linux backend.
 */
static int usbProxyFreeBSDOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress,
                               void *pvBackend)
{
    int rc;

    LogFlow(("usbProxyFreeBSDOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));

    /*
     * Try open the device node.
     */
    RTFILE hFile;
    rc = RTFileOpen(&hFile, pszAddress, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    if (RT_SUCCESS(rc))
    {
        /*
         * Allocate and initialize the linux backend data.
         */
        PUSBPROXYDEVFBSD pDevFBSD = (PUSBPROXYDEVFBSD)RTMemAllocZ(sizeof(USBPROXYDEVFBSD));
        if (pDevFBSD)
        {
            pDevFBSD->hFile = hFile;
            pProxyDev->Backend.pv = pDevFBSD;

            rc = usbProxyFreeBSDFsInit(pProxyDev);
            if (RT_SUCCESS(rc))
            {
                LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns successfully hFile=%RTfile iActiveCfg=%d\n",
                         pProxyDev, pszAddress, pDevFBSD->hFile, pProxyDev->iActiveCfg));

                return VINF_SUCCESS;
            }

            RTMemFree(pDevFBSD);
        }
        else
            rc = VERR_NO_MEMORY;

        RTFileClose(hFile);
    }
    else if (rc == VERR_ACCESS_DENIED)
        rc = VERR_VUSB_USBFS_PERMISSION;

    Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%d!\n",
         pProxyDev, pszAddress, rc));

    pProxyDev->Backend.pv = NULL;

    NOREF(pvBackend);
    return rc;
}
/**
 * Opens the device file.
 *
 * @returns VBox status code.
 * @param   pProxyDev       The device instance.
 * @param   pszAddress      If we are using usbfs, this is the path to the
 *                          device.  If we are using sysfs, this is a string of
 *                          the form "sysfs:<sysfs path>//device:<device node>".
 *                          In the second case, the two paths are guaranteed
 *                          not to contain the substring "//".
 * @param   pvBackend       Backend specific pointer, unused for the linux backend.
 */
static DECLCALLBACK(int) usbProxyFreeBSDOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress,
                                             void *pvBackend)
{
    PUSBPROXYDEVFBSD pDevFBSD = USBPROXYDEV_2_DATA(pProxyDev, PUSBPROXYDEVFBSD);
    int rc;

    LogFlow(("usbProxyFreeBSDOpen: pProxyDev=%p pszAddress=%s\n", pProxyDev, pszAddress));

    NOREF(pvBackend);

    /*
     * Try open the device node.
     */
    RTFILE hFile;
    rc = RTFileOpen(&hFile, pszAddress, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    if (RT_SUCCESS(rc))
    {
        /*
         * Initialize the FreeBSD backend data.
         */
        pDevFBSD->hFile = hFile;
        rc = usbProxyFreeBSDFsInit(pProxyDev);
        if (RT_SUCCESS(rc))
        {
            /*
             * Create wakeup pipe.
             */
            rc = RTPipeCreate(&pDevFBSD->hPipeWakeupR, &pDevFBSD->hPipeWakeupW, 0);
            if (RT_SUCCESS(rc))
            {
                LogFlow(("usbProxyFreeBSDOpen(%p, %s): returns successfully hFile=%RTfile iActiveCfg=%d\n",
                         pProxyDev, pszAddress, pDevFBSD->hFile, pProxyDev->iActiveCfg));

                return VINF_SUCCESS;
            }
        }

        RTFileClose(hFile);
    }
    else if (rc == VERR_ACCESS_DENIED)
        rc = VERR_VUSB_USBFS_PERMISSION;

    Log(("usbProxyFreeBSDOpen(%p, %s) failed, rc=%d!\n",
         pProxyDev, pszAddress, rc));

    return rc;
}
/**
 * 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;
}