USBProxyDevice-linux.c revision 4e527ebc87939c4bc53391b38d5800bd6a41ab00
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * USB device proxy - the Linux backend.
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * Copyright (C) 2006-2010 Oracle Corporation
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * This file is part of VirtualBox Open Source Edition (OSE), as
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * available from http://www.virtualbox.org. This file is free software;
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * you can redistribute it and/or modify it under the terms of the GNU
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * General Public License (GPL) as published by the Free Software
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * Foundation, in version 2 as it comes in the "COPYING" file of the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
a16eb14ad7a4b5ef91ddc22d3e8e92d930f736fcvboxsync * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync/*******************************************************************************
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync* Defined Constants And Macros *
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync*******************************************************************************/
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync/** Define NO_PORT_RESET to skip the slow and broken linux port reset.
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * Resetting will break PalmOne. */
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync/** Define NO_LOGICAL_RECONNECT to skip the broken logical reconnect handling. */
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync/*******************************************************************************
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync* Header Files *
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync*******************************************************************************/
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * Backlevel 2.4 headers doesn't have these two defines.
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync * They were added some time between 2.4.21 and 2.4.26, probably in 2.4.23.
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync# define USBDEVFS_URB_SHORT_NOT_OK 0 /* rhel3 doesn't have this. darn! */
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync/* FedoraCore 4 does not have the bit defined by default. */
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync# include "../rdesktop.h"
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync/*******************************************************************************
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync* Structures and Typedefs *
929fb9ebb388276d4f0e544ab804c076669f5bddvboxsync*******************************************************************************/
typedef struct USBPROXYURBLNX
bool fTimedOut;
bool fCanceledByTimedOut;
bool fCanceledBySubmit;
bool fSplitElementReaped;
typedef struct USBPROXYDEVLNX
bool fUsingSysfs;
static int usbProxyLinuxDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries);
static int usbProxyLinuxFindActiveConfig(PUSBPROXYDEV pProxyDev, const char *pszPath, int *piFirstCfg);
static int usbProxyLinuxDoIoCtl(PUSBPROXYDEV pProxyDev, unsigned long iCmd, void *pvArg, bool fHandleNoDev, uint32_t cTries)
int rc;
if (rc >= 0)
return rc;
} while (cTries-- > 0);
return rc;
while (pUrbLnx)
if (pUrbTaxing)
if (pUrbTaxing)
if (!fQuiet)
&& !fQuiet)
if (pUrbLnx)
if (!pUrbLnx)
return NULL;
if (!pSplitHead)
return pUrbLnx;
while (pUrbLnx)
static int usbProxyLinuxFindActiveConfigUsbfs(PUSBPROXYDEV pProxyDev, const char *pszDevNode, int *piFirstCfg)
if (piFirstCfg)
psz--;
psz--;
char *pszNext;
uint32_t u;
if (u != uBus)
if (!psz)
if (u != uDev)
if (psz)
if (piFirstCfg)
*piFirstCfg = u;
if (fActive)
iActiveCfg = u;
if (fActive)
return iActiveCfg;
static int usbProxyLinuxFindActiveConfigSysfs(PUSBPROXYDEV pProxyDev, const char *pszPath, int *piFirstCfg)
#ifdef VBOX_USB_WITH_SYSFS
static int usbProxyLinuxFindActiveConfig(PUSBPROXYDEV pProxyDev, const char *pszPath, int *piFirstCfg)
const char *pszDevNode;
const char *pszPath;
bool fUsingSysfs;
#ifdef VBOX_USB_WITH_SYSFS
if (fUsingSysfs)
if (!pszDevNode)
return VERR_INVALID_PARAMETER;
#ifndef VBOX_USB_WITH_SYSFS
fUsingSysfs = false;
if (pDevLnx)
return VINF_SUCCESS;
return rc;
unsigned iIf;
int iFirstCfg;
Log(("usbProxyLinuxInit: No active config! Tried to set %d: iActiveCfg=%d\n", iFirstCfg, pProxyDev->iActiveCfg));
return VINF_SUCCESS;
if (!pDevLnx)
unsigned iIf;
while (pCur)
Log2(("usb_reset_logical_reconnect: pDev=%p:{.bBus=%#x, .bDevNum=%#x, .idVendor=%#x, .idProduct=%#x, .bcdDevice=%#x, .u64SerialHash=%#llx .bDevNumParent=%#x .bPort=%#x .bLevel=%#x}\n",
pDev, pDev->Info.bBus, pDev->Info.bDevNum, pDev->Info.idVendor, pDev->Info.idProduct, pDev->Info.bcdDevice,
if (!RTThreadYield())
if (!pFile)
return VERR_FILE_NOT_FOUND;
int rc;
int got = 0;
switch ( buf[0] ) {
Log2(("usb_reset_logical_reconnect: {.bBus=%#x, .bDevNum=%#x, .idVendor=%#x, .idProduct=%#x, .bcdDevice=%#x, .u64SerialHash=%#llx, .bDevNumParent=%#x, .bPort=%#x, .bLevel=%#x}\n",
id.bBus, id.bDevNum, id.idVendor, id.idProduct, id.bcdDevice, id.u64SerialHash, id.bDevNumParent, id.bPort, id.bLevel));
goto l_found;
got = 0;
psz++;
got++;
got++;
return VINF_SUCCESS;
if (rc < 0) {
return VERR_GENERAL_FAILURE;
#ifdef NO_PORT_RESET
if ( !fResetOnLinux
# ifndef NO_LOGICAL_RECONNECT
# ifndef NO_LOGICAL_RECONNECT
return VINF_SUCCESS;
# ifndef NO_LOGICAL_RECONNECT
return VINF_SUCCESS;
LogFlow(("usbProxyLinuxClaimInterface: pProxyDev=%s ifnum=%#x\n", usbProxyGetName(pProxyDev), iIf));
LogFlow(("usbProxyLinuxReleaseInterface: pProxyDev=%s ifnum=%#x\n", usbProxyGetName(pProxyDev), iIf));
LogFlow(("usbProxyLinuxClearHaltedEp: pProxyDev=%s EndPt=%u\n", usbProxyGetName(pProxyDev), EndPt));
static void usbProxyLinuxCleanupFailedSubmit(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx, PUSBPROXYURBLNX pCur, PVUSBURB pUrb, bool *pfUnplugged)
*pfUnplugged = true;
LogRel(("USB: Failed to discard %p! errno=%d (pUrb=%p)\n", pUrbLnx->KUrb.usercontext, errno, pUrb)); /* serious! */
while (pCur)
if (*pfUnplugged)
static bool usbProxyLinuxSubmitURB(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pCur, PVUSBURB pUrb, bool *pfUnplugged)
unsigned cTries = 0;
*pfUnplugged = true;
static PUSBPROXYURBLNX usbProxyLinuxSplitURBFragment(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pHead, PUSBPROXYURBLNX pCur)
if (!pNew)
return NULL;
return pNew;
static int usbProxyLinuxUrbQueueSplit(PUSBPROXYDEV pProxyDev, PUSBPROXYURBLNX pUrbLnx, PVUSBURB pUrb)
case VUSBXFERTYPE_ISOC:
bool fSucceeded = false;
bool fUnplugged = false;
fSucceeded = true;
if (!pCur)
if (!fSucceeded)
if (fSucceeded)
unsigned cTries;
#ifndef RDESKTOP
if (!pUrbLnx)
case VUSBXFERTYPE_MSG:
case VUSBXFERTYPE_BULK:
case VUSBXFERTYPE_ISOC:
case VUSBXFERTYPE_INTR:
goto l_err;
cTries = 0;
pCur;
unsigned cFailures = 0;
Log(("vusbProxyLinuxUrbDoTimeouts: pUrb=%p failed errno=%d (!!split!!)\n", pCur2->KUrb.usercontext, errno));
LogRel(("USB: Cancelled URB (%p) after %llums!!\n", pCur->KUrb.usercontext, (long long unsigned) u64MilliTS - pCur->u64SubmitTS));
/* Disabled for the time beeing as some USB devices have URBs pending for an unknown amount of time.
else if (u64MilliTS - pCur->u64SubmitTS >= 200*1000 /* 200 sec (180 sec has been observed with XP) */)
* sitd_complete+itd_complete in ehci-sched.c, and qtd_copy_status in
* ehci-q.c.
switch (iStatus)
return VUSBSTATUS_OK;
case -EILSEQ:
return VUSBSTATUS_CRC;
return VUSBSTATUS_DATA_UNDERRUN;
case -EOVERFLOW:
return VUSBSTATUS_DATA_OVERRUN;
case -ETIME:
case -ENODEV:
return VUSBSTATUS_DNR;
case -EPIPE:
return VUSBSTATUS_STALL;
case -ESHUTDOWN:
return VUSBSTATUS_STALL;
return VUSBSTATUS_STALL;
return VUSBSTATUS_CRC;
if (pUrbLnx)
if (!pUrbLnx)
return NULL;
if (cMillies)
int rc;
return NULL;
Log(("usb-linux: Reap URB - poll -> %d errno=%d pProxyDev=%s\n", rc, errno, usbProxyGetName(pProxyDev)));
return NULL;
return NULL;
/* for variable size URBs, we may need to queue more if the just-reaped URB was completely filled */
if (pUrbLnx->cbSplitRemaining && (pKUrb->actual_length == pKUrb->buffer_length) && !pUrbLnx->pSplitNext)
bool fUnplugged = false;
bool fSucceeded;
if (!pNew)
Log(("usb-linux: Allocating URB fragment failed. errno=%d pProxyDev=%s\n", errno, usbProxyGetName(pProxyDev)));
return NULL;
if (fUnplugged)
if (!fSucceeded)
return NULL;
if (!pUrbLnx)
if ( pUrb
unsigned i, off;
pUrb->aIsocPkts[i].enmStatus = vusbProxyLinuxStatusToVUsbStatus(pUrbLnx->KUrb.iso_frame_desc[i].status);
return pUrb;
#ifndef RDESKTOP