VUSBDevice.cpp revision 82bcaaf8077ba892f39afb721dca149353c63d2c
/* $Id$ */
/** @file
* Virtual USB - Device.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_VUSB
#include <iprt/semaphore.h>
#include "VUSBInternal.h"
/** Asserts that the give device state is valid. */
#define VUSBDEV_ASSERT_VALID_STATE(enmState) \
AssertMsg((enmState) > VUSB_DEVICE_STATE_INVALID && (enmState) < VUSB_DEVICE_STATE_DESTROYED, ("enmState=%#x\n", enmState));
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Argument package of vusbDevResetThread().
*/
typedef struct vusb_reset_args
{
/** Pointer to the device which is being reset. */
/** Can reset on linux. */
bool fResetOnLinux;
/** The reset return code. */
int rc;
/** Pointer to the completion callback. */
/** User argument to pfnDone. */
void *pvUser;
/** The timer used to notify EMT. */
/*******************************************************************************
* 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
},
};
/** 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,
};
{
if (iCfg == 0)
return &g_Config0;
return NULL;
}
{
return &pDev->paIfStates[i];
return NULL;
}
static PCVUSBDESCINTERFACEEX vusbDevFindAltIfDesc(PVUSBDEV pDev, PCVUSBINTERFACESTATE pIfState, int iAlt)
{
return NULL;
}
{
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,
{
}
{
#if defined(RT_OS_LINUX) || defined(RT_OS_SOLARIS)
/*
* For high-speed isochronous input endpoints, spin off a read-ahead buffering thread.
*/
#endif
}
else
{
}
{
}
}
{
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,
{
}
{
/* If there was a read-ahead thread associated with this endpoint, tell it to go away. */
if (pPipe->pvReadAheadArgs)
{
Log(("vusb: and tell read-ahead thread for the endpoint to terminate\n"));
}
}
else
{
}
{
}
}
{
LogFlow(("map_interface: pDev=%p[%s] pIfDesc=%p:{.iInterface=%d, .bAlternateSetting=%d}\n",
{
Log(("vusb: Endpoint 0x%x on interface %u.%u tried to overrride the default message pipe!!!\n",
pIfDesc->paEndpoints[i].Core.bEndpointAddress, pIfDesc->Core.bInterfaceNumber, pIfDesc->Core.bAlternateSetting));
else
}
}
{
LogFlow(("vusbDevDoSelectConfig: pDev=%p[%s] pCfgDesc=%p:{.iConfiguration=%d}\n",
/*
* Clean up all pipes and interfaces.
*/
unsigned i;
for (i = 0; i < VUSB_PIPE_MAX; i++)
{
if (i != VUSB_PIPE_DEFAULT)
{
}
}
/*
* Map in the default setting for every interface.
*/
{
struct vusb_interface_state *pIfState;
/*
* Find the 0 setting, if it is not present we just use
* the lowest numbered one.
*/
{
if ( !pIfState->pCurIfDesc
break;
}
if (pIfState->pCurIfDesc)
}
else
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)
{
{
Log(("vusb: error: %s: SET_CONFIGURATION - invalid request (dir) !!!\n", pDev->pUsbIns->pszName, iCfg));
return false;
}
/*
* Check that the device is in a valid state.
* (The caller has already checked that it's not being reset.)
*/
if (enmState == VUSB_DEVICE_STATE_DEFAULT)
{
return false;
}
if (!pNewCfgDesc)
{
return false;
}
if (iCfg == 0)
else
{
int rc = pDev->pUsbIns->pReg->pfnUsbSetConfiguration(pDev->pUsbIns, pNewCfgDesc->Core.bConfigurationValue,
if (RT_FAILURE(rc))
{
return false;
}
}
}
/**
* Standard device request: GET_CONFIGURATION
* @returns success indicator.
*/
static bool vusbDevStdReqGetConfig(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
{
return false;
}
/*
* Check that the device is in a valid state.
* (The caller has already checked that it's not being reset.)
*/
if ( enmState != VUSB_DEVICE_STATE_CONFIGURED
{
LogFlow(("vusbDevStdReqGetConfig: error: %s: invalid device state %d!!!\n", pDev->pUsbIns->pszName, enmState));
return false;
}
if (*pcbBuf < 1)
{
return true;
}
iCfg = 0;
else
*pcbBuf = 1;
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)
{
{
return false;
}
/*
* Check that the device is in a valid state.
* (The caller has already checked that it's not being reset.)
*/
if (enmState != VUSB_DEVICE_STATE_CONFIGURED)
{
LogFlow(("vusbDevStdReqGetInterface: error: %s: invalid device state %d!!!\n", pDev->pUsbIns->pszName, enmState));
return false;
}
if (*pcbBuf < 1)
{
return true;
}
{
if ( pIfDesc
{
*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)
{
{
return false;
}
/*
* Check that the device is in a valid state.
* (The caller has already checked that it's not being reset.)
*/
if (enmState != VUSB_DEVICE_STATE_CONFIGURED)
{
LogFlow(("vusbDevStdReqSetInterface: error: %s: invalid device state %d !!!\n", pDev->pUsbIns->pszName, enmState));
return false;
}
/*
* Find the interface.
*/
if (!pIfState)
{
LogFlow(("vusbDevStdReqSetInterface: error: %s: couldn't find interface %u !!!\n", pDev->pUsbIns->pszName, iIf));
return false;
}
if (!pIfDesc)
{
LogFlow(("vusbDevStdReqSetInterface: error: %s: couldn't find alt interface %u.%u !!!\n", pDev->pUsbIns->pszName, iIf, iAlt));
return false;
}
{
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;
}
}
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)
{
{
return false;
}
/*
* Check that the device is in a valid state.
* (The caller has already checked that it's not being reset.)
*/
if ( enmState != VUSB_DEVICE_STATE_DEFAULT
{
LogFlow(("vusbDevStdReqSetAddress: error: %s: invalid device state %d !!!\n", pDev->pUsbIns->pszName, enmState));
return false;
}
return true;
}
/**
* Standard device request: CLEAR_FEATURE
* @returns success indicator.
*
* @remark This is only called for VUSB_TO_ENDPOINT && ep == 0 && wValue == ENDPOINT_HALT.
*/
static bool vusbDevStdReqClearFeature(PVUSBDEV pDev, int EndPt, PVUSBSETUP pSetup, uint8_t *pbBuf, uint32_t *pcbBuf)
{
{
case VUSB_TO_DEVICE:
break;
case VUSB_TO_INTERFACE:
break;
case VUSB_TO_ENDPOINT:
if ( !EndPt /* Default control pipe only */
{
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)
{
{
case VUSB_TO_DEVICE:
Log(("vusb: SetFeature: dev(%u): selector=%u\n",
break;
case VUSB_TO_INTERFACE:
Log(("vusb: SetFeature: if(%u): selector=%u\n",
break;
case VUSB_TO_ENDPOINT:
Log(("vusb: SetFeature: ep(%u): selector=%u\n",
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)
{
if (*pcbBuf != 2)
{
LogFlow(("vusbDevStdReqGetStatus: %s: buffer is too small! (%d)\n", pDev->pUsbIns->pszName, *pcbBuf));
return false;
}
{
case VUSB_TO_DEVICE:
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;
break;
default:
AssertMsgFailed(("VUSB_TO_OTHER!\n"));
return false;
}
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,
{
/** @todo binary lookups! */
unsigned iCurLang = cLanguages;
while (iCurLang-- > 0)
{
while (iCurStr-- > 0)
break;
}
return NULL;
}
/** Macro for copying descriptor data. */
do { \
if (!cbLeft) \
return; \
} while (0)
/**
* Internal function for reading the language IDs.
*/
{
if (RT_FAILURE(rc))
{
wsz[0] = 'e';
cwc = 3;
}
/* updated the size of the output buffer. */
}
/**
* Internal function for reading the language IDs.
*/
{
unsigned iLanguage = cLanguages;
while (iLanguage-- > 0)
/* updated the size of the output buffer. */
}
/**
* Internal function which performs a descriptor read on the cached descriptors.
*/
{
/** @todo See @bugref{2693} */
/*
* Make a copy of the config descriptor and calculate the wTotalLength field.
*/
{
{
{
}
}
}
/*
* Copy the config descriptor
*/
/*
* Copy out all the interfaces for this configuration
*/
{
{
/*
* Copy out all the endpoints for this interface
*/
{
COPY_DATA(pbBuf, cbLeft, pIfDesc->paEndpoints[k].pvMore, EndPtDesc.bLength - VUSB_DT_ENDPOINT_MIN_LEN);
}
}
}
/* updated the size of the output buffer. */
}
/**
* Internal function which performs a descriptor read on the cached descriptors.
*/
{
/*
* Duplicate the device description and update some fields we keep in cpu type.
*/
/* updated the size of the output buffer. */
}
/**
* 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)
{
{
{
case VUSB_DT_DEVICE:
LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of device descriptors\n", pDev->pUsbIns->pszName, *pcbBuf));
return true;
case VUSB_DT_CONFIG:
{
{
LogFlow(("vusbDevStdReqGetDescriptor: %s: iIndex=%p >= bNumConfigurations=%d !!!\n",
return false;
}
LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of config descriptors\n", pDev->pUsbIns->pszName, *pcbBuf));
return true;
}
case VUSB_DT_STRING:
{
{
LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of language ID (string) descriptors\n", pDev->pUsbIns->pszName, *pcbBuf));
return true;
}
if (pString)
{
LogFlow(("vusbDevStdReqGetDescriptor: %s: %u bytes of string descriptors \"%s\"\n",
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 *) =
{
NULL,
NULL,
NULL,
NULL /* for iso */
};
/*
* Check that the device is in a valid state.
*/
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.
*/
{
Log(("vusb: warning: standard req not implemented: message %u: val=%u idx=%u len=%u !!!\n",
return false;
}
}
/**
* Add a device to the address hash
*/
{
return;
}
/**
* Remove a device from the address hash
*/
{
return;
{
/* special case, we're at the head */
}
else
{
/* search the list */
pCur;
{
{
break;
}
}
}
}
/**
* Sets the address of a device.
*
* Called by status_completion() and vusbDevResetWorker().
*/
{
LogFlow(("vusbDevSetAddress: pDev=%p[%s]/%i u8Address=%#x\n",
/*
* Check that the device is in a valid state.
*/
if ( enmState == VUSB_DEVICE_STATE_ATTACHED
{
LogFlow(("vusbDevSetAddress: %s: fails because %d < POWERED\n", pDev->pUsbIns->pszName, pDev->enmState));
return;
}
if (enmState == VUSB_DEVICE_STATE_RESET)
{
return;
}
/*
* Ok, get on with it.
*/
return;
if (u8Address == VUSB_DEFAULT_ADDRESS)
{
{
Log(("2 DEFAULT ADDRS\n"));
}
}
else
Log(("vusb: %p[%s]/%i: Assigned address %u\n",
}
/**
* 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.
*/
{
/*
* Iterate the URBs and cancel them.
*/
while (pUrb)
{
{
}
}
/*
* Reap any URBs which became ripe during cancel now.
*/
unsigned cReaped;
do
{
cReaped = 0;
while (pUrb)
{
{
#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. */
#else
#endif
else
if (pRipe)
{
cReaped++;
}
}
}
} while (cReaped > 0);
/*
* If we're detaching, we'll have to orphan any leftover URBs.
*/
if (fDetaching)
{
while (pUrb)
{
{
AssertMsgFailed(("%s: Leaking left over URB! state=%d pDev=%p[%s]\n",
}
}
}
}
/**
* 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.
*/
{
LogFlow(("vusbDevDetach: pDev=%p[%s] enmState=%#x\n", pDev, pDev->pUsbIns->pszName, pDev->enmState));
vusbDevCancelAllUrbs(pDev, true);
if (!pRh)
/* Remove the configuration */
for (unsigned i = 0; i < VUSB_PIPE_MAX; i++)
return VINF_SUCCESS;
}
/**
* Destroys a device, detaching it from the hub if necessary.
*
* @param pDev The device.
* @thread EMT
*/
{
LogFlow(("vusbDevDestroy: pDev=%p[%s] enmState=%d\n", pDev, pDev->pUsbIns->pszName, pDev->enmState));
/*
* Deal with pending async reset.
*/
{
if (RT_SUCCESS(rc))
{
}
}
/*
* Detach and free resources.
*/
}
/* -=-=-=-=-=- VUSBIDEVICE methods -=-=-=-=-=- */
/**
* Perform the actual reset.
*
* @thread EMT or a VUSB reset thread.
*/
{
int rc = VINF_SUCCESS;
return rc;
}
/**
* 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.
*/
{
/*
* Do control pipe cleanup regardless of state and result.
*/
for (unsigned i = 0; i < VUSB_PIPE_MAX; i++)
/*
* Switch to the default state.
*/
if (!vusbDevIsRh(pDev))
if (pfnDone)
}
/**
* Timer callback for doing reset completion.
*
* @thread EMT
*/
{
/*
* Release the thread and update the device structure.
*/
/*
* Reset-done processing and cleanup.
*/
}
/**
* Thread function for performing an async reset.
*
* This will pass the argument packet back to EMT upon completion
* by means of a one shot timer.
*
* @returns whatever vusbDevResetWorker() returns.
* @param Thread This thread.
* @param pvUser Pointer to a VUSBRESETARGS structure.
*/
{
LogFlow(("vusb: reset thread started\n"));
/*
* Tell EMT that we're in flow and then perform the reset.
*/
/*
* We use a timer to commuicate the result back to EMT.
* This avoids suspend + poweroff issues, and it should give
* us more accurate scheduling than making this thread sleep.
*/
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
*/
DECLCALLBACK(int) vusbDevReset(PVUSBIDEVICE pDevice, bool fResetOnLinux, PFNVUSBRESETDONE pfnDone, void *pvUser, PVM pVM)
{
/*
* Only one reset operation at a time.
*/
if (enmState == 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.
*/
if (pArgs)
{
pArgs->pTimer = TMR3TimerCreateExternal(pVM, TMCLOCK_VIRTUAL, vusbDevResetDoneTimer, pArgs, "USB Device Reset Timer.");
{
int rc = RTThreadCreate(&pDev->hResetThread, vusbDevResetThread, pArgs, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "USBRESET");
if (RT_SUCCESS(rc))
{
/* give the thread a chance to get started. */
return rc;
}
}
}
/* fall back to sync on failure */
}
/*
* Sync fashion.
*/
return rc;
}
/**
* Powers on the device.
*
* @returns VBox status code.
* @param pInterface Pointer to the device interface structure.
*/
{
/*
* Check that the device is in a valid state.
*/
if (enmState == VUSB_DEVICE_STATE_DETACHED)
{
return VERR_VUSB_DEVICE_NOT_ATTACHED;
}
if (enmState == VUSB_DEVICE_STATE_RESET)
{
return VERR_VUSB_DEVICE_IS_RESETTING;
}
/*
* Do the job.
*/
if (enmState == VUSB_DEVICE_STATE_ATTACHED)
return VINF_SUCCESS;
}
/**
* Powers off the device.
*
* @returns VBox status code.
* @param pInterface Pointer to the device interface structure.
*/
{
/*
* Check that the device is in a valid state.
*/
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)
{
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))
{
}
return VINF_SUCCESS;
}
/**
* Get the state of the device.
*
* @returns Device state.
* @param pInterface Pointer to the device interface structure.
*/
{
}
/**
* The maximum number of interfaces the device can have in all of it's configuration.
*
* @returns Number of interfaces.
* @param pDev The device.
*/
{
while (i-- > 0)
{
}
return cMax;
}
/**
* 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.
*/
{
/*
* Initialize the device data members.
* (All that are Non-Zero at least.)
*/
/*
* Get the descriptor cache from the device. (shall cannot fail)
*/
#ifdef VBOX_STRICT
{
{
{
}
}
}
#endif
/*
* Allocate memory for the interface states.
*/
return VINF_SUCCESS;
}
/*
* Local Variables:
* mode: c
* c-file-style: "bsd"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: s
* End:
*/