VUSBUrb.cpp revision 899acd106e06a30ecb233d09c74f9aa71867627f
/* $Id$ */
/** @file
* Virtual USB - URBs.
*/
/*
* Copyright (C) 2006-2010 Oracle Corporation
*
* 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.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DRV_VUSB
#include <iprt/semaphore.h>
#include "VUSBInternal.h"
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** Strings for the CTLSTAGE enum values. */
const char * const g_apszCtlStates[4] =
{
"SETUP",
"DATA",
"STATUS",
"N/A"
};
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
#ifdef LOG_ENABLED
{
/** Strings for the URB statuses. */
static const char * const s_apszNames[] =
{
"OK",
"STALL",
"ERR_DNR",
"ERR_CRC",
"DATA_UNDERRUN",
"DATA_OVERRUN",
"NOT_ACCESSED",
"7", "8", "9", "10", "11", "12", "13", "14", "15"
};
? "INVALID"
: "??";
}
{
/** Strings for the URB directions. */
static const char * const s_apszNames[] =
{
"setup",
"in",
"out"
};
: "??";
}
{
/** Strings for the URB types. */
static const char * const s_apszName[] =
{
"control-part",
"isochronous",
"bulk",
"interrupt",
"control"
};
: "??";
}
{
switch (ScsiErr)
{
case 0: return "?";
}
return "?";
}
{
switch (Key)
{
case 0:
{
}
break;
case 1:
return "Soft Error";
case 2:
return "Not Ready";
case 3:
return "Medium Error";
case 4:
return "Hard Error";
case 5:
return "Illegal Request";
case 6:
return "Unit Attention";
case 7:
return "Write Protected";
case 0xb:
return "Aborted Command";
}
return "?";
}
/**
* Logs an URB.
*
* Note that pUrb->pUsbIns, pUrb->VUsb.pDev and pUrb->VUsb.pDev->pUsbIns can all be NULL.
*/
{
bool fDescriptors = false;
if (cchMsg > s_cchMaxMsg)
Log(("%s: %*s: pDev=%p[%s] rc=%s a=%i e=%u d=%s t=%s cb=%#x(%d) Ed=%08x cTds=%d Td0=%08x ts=%RU64 (%RU64 ns ago) %s\n",
pDev,
#ifndef DEBUG_bird
return;
#endif
&& cbData))
{
static const char * const s_apszRequests[] =
{
"GET_STATUS", "CLEAR_FEATURE", "2?", "SET_FEATURE",
"4?", "SET_ADDRESS", "GET_DESCRIPTOR", "SET_DESCRIPTOR",
"GET_CONFIGURATION", "SET_CONFIGURATION", "GET_INTERFACE", "SET_INTERFACE",
"SYNCH_FRAME"
};
Log(("%s: %*s: CTRL: bmRequestType=0x%.2x (%s %s %s) bRequest=0x%.2x (%s) wValue=0x%.4x wIndex=0x%.4x wLength=0x%.4x\n",
pSetup->bmRequestType, s_apszReqDirs[pSetup->bmRequestType >> 7], s_apszReqTypes[(pSetup->bmRequestType >> 5) & 0x3],
(unsigned)(pSetup->bmRequestType & 0xf) < RT_ELEMENTS(s_apszReqRecipients) ? s_apszReqRecipients[pSetup->bmRequestType & 0xf] : "??",
pSetup->bRequest, pSetup->bRequest < RT_ELEMENTS(s_apszRequests) ? s_apszRequests[pSetup->bRequest] : "??",
&& fComplete
fDescriptors = true;
}
else if ( fComplete
&& cbData > 0)
{
fDescriptors = true;
}
/*
* Dump descriptors.
*/
if (fDescriptors)
{
{
/* length out of bounds? */
{
Log(("URB: %*s: DESC: warning descriptor length goes beyond the end of the URB! cbLength=%d cbLeft=%d\n",
}
if (cb >= 2)
{
Log(("URB: %*s: DESC: %04x: %25s = %#04x (%d)\n"
"URB: %*s: %04x: %25s = %#04x (",
#pragma pack(1)
{ \
Log(("\n")); \
} } while (0)
#define SIZE_CHECK(strct) \
/* on type */
switch (bDescriptorType)
{
case VUSB_DT_DEVICE:
{
struct dev_desc
{
Log(("DEV)\n"));
SIZE_CHECK(struct dev_desc);
break;
}
case VUSB_DT_CONFIG:
{
struct cfg_desc
{
Log(("CFG)\n"));
static const char * const s_apszSyncType[4] = { "NoSync", "Asynchronous", "Adaptive", "Synchronous" };
static const char * const s_apszUsageType[4] = { "Data ep", "Feedback ep.", "Implicit feedback Data ep.", "Reserved" };
s_apszSyncType[((pDesc->bmAttributes >> 2) & 0x3)], s_apszUsageType[((pDesc->bmAttributes >> 4) & 0x3)]));
SIZE_CHECK(struct cfg_desc);
break;
}
case VUSB_DT_STRING:
{
/* langid array */
Log(("LANGIDs)\n"));
{
Log(("URB: %*s: %04x: wLANGID[%#x] = %#06x\n",
pu16++;
}
if (cb & 1)
Log(("URB: %*s: %04x: WARNING descriptor size is odd! extra byte: %02\n",
}
else
{
/** a string. */
Log(("STRING)\n"));
if (cb > 2)
Log(("URB: %*s: %04x: Length=%d String=%.*ls\n",
else
}
break;
case VUSB_DT_INTERFACE:
{
struct if_desc
{
Log(("IF)\n"));
SIZE_CHECK(struct if_desc);
break;
}
case VUSB_DT_ENDPOINT:
{
struct ep_desc
{
Log(("EP)\n"));
SIZE_CHECK(struct ep_desc);
break;
}
case VUSB_DT_DEVICE_QUALIFIER:
{
struct dq_desc
{
Log(("DEVQ)\n"));
SIZE_CHECK(struct dq_desc);
break;
}
case VUSB_DT_OTHER_SPEED_CFG:
{
struct oth_cfg_desc
{
Log(("OCFG)\n"));
static const char * const s_apszSyncType[4] = { "NoSync", "Asynchronous", "Adaptive", "Synchronous" };
static const char * const s_apszUsageType[4] = { "Data ep", "Feedback ep.", "Implicit feedback Data ep.", "Reserved" };
s_apszSyncType[((pDesc->bmAttributes >> 2) & 0x3)], s_apszUsageType[((pDesc->bmAttributes >> 4) & 0x3)]));
SIZE_CHECK(struct oth_cfg_desc);
break;
}
case 0x21:
{
struct hid_desc
{
Log(("EP)\n"));
SIZE_CHECK(struct hid_desc);
break;
}
case 0xff:
Log(("UNKNOWN-ignore)\n"));
break;
default:
Log(("UNKNOWN)!!!\n"));
break;
}
#pragma pack()
}
else
{
Log(("URB: %*s: DESC: %04x: bLength=%d bDescriptorType=%d - invalid length\n",
break;
}
/* next */
}
}
/*
* SCSI
*/
{
const struct usbc
{
Log(("URB: %*s: SCSI: Tag=%#x DataTransferLength=%#x Flags=%#x Lun=%#x Length=%#x CDB=%.*Rhxs\n",
switch (pb[0])
{
case 0x00: /* test unit read */
Log(("URB: %*s: SCSI: TEST_UNIT_READY LUN=%d Ctrl=%#RX8\n",
break;
case 0x03: /* Request Sense command */
Log(("URB: %*s: SCSI: REQUEST_SENSE LUN=%d AlcLen=%#RX16 Ctrl=%#RX8\n",
break;
case 0x12: /* Inquiry command. */
Log(("URB: %*s: SCSI: INQUIRY EVPD=%d LUN=%d PgCd=%#RX8 AlcLen=%#RX8 Ctrl=%#RX8\n",
break;
case 0x1a: /* Mode Sense(6) command */
Log(("URB: %*s: SCSI: MODE_SENSE6 LUN=%d DBD=%d PC=%d PgCd=%#RX8 AlcLen=%#RX8 Ctrl=%#RX8\n",
break;
case 0x5a:
Log(("URB: %*s: SCSI: MODE_SENSE10 LUN=%d DBD=%d PC=%d PgCd=%#RX8 AlcLen=%#RX16 Ctrl=%#RX8\n",
break;
case 0x25: /* Read Capacity(6) command. */
Log(("URB: %*s: SCSI: READ_CAPACITY\n",
s_cchMaxMsg, pszMsg));
break;
case 0x28: /* Read(10) command. */
Log(("URB: %*s: SCSI: READ10 RelAdr=%d FUA=%d DPO=%d LUN=%d LBA=%#RX32 Len=%#RX16 Ctrl=%#RX8\n",
break;
case 0xa8: /* Read(12) command. */
Log(("URB: %*s: SCSI: READ12 RelAdr=%d FUA=%d DPO=%d LUN=%d LBA=%#RX32 Len=%#RX32 Ctrl=%#RX8\n",
pb[11]));
break;
case 0x3e: /* Read Long command. */
Log(("URB: %*s: SCSI: READ LONG RelAdr=%d Correct=%d LUN=%d LBA=%#RX16 ByteLen=%#RX16 Ctrl=%#RX8\n",
pb[11]));
break;
case 0x2a: /* Write(10) command. */
Log(("URB: %*s: SCSI: WRITE10 RelAdr=%d EBP=%d FUA=%d DPO=%d LUN=%d LBA=%#RX32 Len=%#RX16 Ctrl=%#RX8\n",
break;
case 0xaa: /* Write(12) command. */
Log(("URB: %*s: SCSI: WRITE12 RelAdr=%d EBP=%d FUA=%d DPO=%d LUN=%d LBA=%#RX32 Len=%#RX32 Ctrl=%#RX8\n",
pb[11]));
break;
case 0x3f: /* Write Long command. */
Log(("URB: %*s: SCSI: WRITE LONG RelAdr=%d LUN=%d LBA=%#RX16 ByteLen=%#RX16 Ctrl=%#RX8\n",
pb[11]));
break;
case 0x35: /* Synchronize Cache(10) command. */
Log(("URB: %*s: SCSI: SYNCHRONIZE_CACHE10\n",
s_cchMaxMsg, pszMsg));
break;
case 0xa0: /* Report LUNs command. */
Log(("URB: %*s: SCSI: REPORT_LUNS\n",
s_cchMaxMsg, pszMsg));
break;
default:
Log(("URB: %*s: SCSI: cmd=%#x\n",
break;
}
if (pDev)
}
else if ( fComplete
{
const struct usbs
{
Log(("URB: %*s: SCSI: Tag=%#x DataResidue=%#RX32 Status=%#RX8 %s\n",
s_apszStatuses[pUsbS->Status < RT_ELEMENTS(s_apszStatuses) ? pUsbS->Status : RT_ELEMENTS(s_apszStatuses) - 1]));
if (pDev)
}
else if ( fComplete
&& pDev
{
{
case 0x03: /* REQUEST_SENSE */
Log(("URB: %*s: SCSI: RESPONSE: REQUEST_SENSE (%s)\n",
Log(("URB: %*s: SCSI: ErrCd=%#RX8 (%s) Seg=%#RX8 Filemark=%d EOM=%d ILI=%d\n",
Log(("URB: %*s: SCSI: SenseKey=%#x ASC=%#RX8 ASCQ=%#RX8 : %s\n",
/** @todo more later */
break;
case 0x12: /* INQUIRY. */
{
Log(("URB: %*s: SCSI: RESPONSE: INQUIRY\n"
"URB: %*s: SCSI: PeripheralQualifier=%d PeripheralType=%#RX8 RMB=%d DevTypeMod=%#RX8\n",
Log(("URB: %*s: SCSI: ISOVer=%d ECMAVer=%d ANSIVer=%d\n",
Log(("URB: %*s: SCSI: AENC=%d TrmlOP=%d RespDataFmt=%d (%s) AddLen=%d\n",
if (cb < 8)
break;
Log(("URB: %*s: SCSI: RelAdr=%d WBus32=%d WBus16=%d Sync=%d Linked=%d CmdQue=%d SftRe=%d\n",
if (cb < 16)
break;
if (cb < 32)
break;
if (cb < 36)
break;
if (cb > 36)
Log(("URB: %*s: SCSI: VendorSpecific=%.*s\n",
if (cb > 96)
Log(("URB: %*s: SCSI: VendorParam=%.*Rhxs\n",
break;
}
case 0x25: /* Read Capacity(6) command. */
Log(("URB: %*s: SCSI: RESPONSE: READ_CAPACITY\n"
"URB: %*s: SCSI: LBA=%#RX32 BlockLen=%#RX32\n",
break;
}
}
/*
* The Quickcam control pipe.
*/
if ( pSetup
&& pDev
&& pDev->pDescCache
)
{
{
/* the value */
if (cbData > 1)
{
case 0: pszReg = "i2c init"; break;
default: pszReg = "[no clue]"; break;
}
if (pszReg)
Log(("URB: %*s: QUICKCAM: %s %#x (%d) %s '%s' (%#x)\n",
(pSetup->bmRequestType >> 7) ? "read" : "write", uVal, uVal, (pSetup->bmRequestType >> 7) ? "from" : "to",
}
else if (cbData)
Log(("URB: %*s: QUICKCAM: Unknown request: bRequest=%#x bmRequestType=%#x wValue=%#x wIndex=%#x: %.*Rhxs\n", s_cchMaxMsg, pszMsg,
else
Log(("URB: %*s: QUICKCAM: Unknown request: bRequest=%#x bmRequestType=%#x wValue=%#x wIndex=%#x: (no data)\n", s_cchMaxMsg, pszMsg,
}
#if 1
if ( cbData /** @todo Fix RTStrFormatV to communicate .* so formatter doesn't apply defaults when cbData=0. */
&& (fComplete
#endif
}
#endif /* LOG_ENABLED */
/**
* Complete a SETUP stage URB.
*
* This is used both for dev2host and host2dev kind of transfers.
* It is used by both the sync and async control paths.
*/
{
LogFlow(("%s: vusbMsgSetupCompletion: cbData=%d wLength=%#x cbLeft=%d pPipe=%p stage %s->DATA\n",
pUrb->pszDesc, pUrb->cbData, pSetup->wLength, pExtra->cbLeft, pPipe, g_apszCtlStates[pExtra->enmStage])); NOREF(pSetup);
}
/**
* Complete a DATA stage URB.
*
* This is used both for dev2host and host2dev kind of transfers.
* It is used by both the sync and async control paths.
*/
{
LogFlow(("%s: vusbMsgDataCompletion: cbData=%d wLength=%#x cbLeft=%d pPipe=%p stage DATA\n",
}
/**
* Complete a STATUS stage URB.
*
* This is used both for dev2host and host2dev kind of transfers.
* It is used by both the sync and async control paths.
*/
{
{
/*
* vusbDevStdReqSetAddress requests are deferred.
*/
{
}
LogFlow(("%s: vusbMsgStatusCompletion: pDev=%p[%s] pPipe=%p err=OK stage %s->SETUP\n",
}
else
{
LogFlow(("%s: vusbMsgStatusCompletion: pDev=%p[%s] pPipe=%p err=STALL stage %s->SETUP\n",
}
/*
* Done with this message sequence.
*/
}
/**
* This is a worker function for vusbMsgCompletion and
* vusbMsgSubmitSynchronously used to complete the original URB.
*
* @param pUrb The URB originating from the HCI.
*/
{
{
case CTLSTAGE_SETUP:
break;
case CTLSTAGE_DATA:
break;
case CTLSTAGE_STATUS:
break;
}
}
/**
* Called from vusbUrbCompletionRh when it encounters a
* message type URB.
*
* @param pUrb The URB within the control pipe extra state data.
*/
{
#ifdef LOG_ENABLED
#endif
else
/*
* Complete the original URB.
*/
/*
* 'Free' the message URB, i.e. put it back to the allocated state.
*/
/* Complete the original control URB on the root hub now. */
}
/**
* Deal with URB errors, talking thru the RH to the HCI.
*
* @returns true if it could be retried.
* @returns false if it should be completed with failure.
* @param pUrb The URB in question.
*/
{
LogFlow(("%s: vusbUrbErrorRh: pDev=%p[%s] rh=%p\n", pUrb->pszDesc, pDev, pDev->pUsbIns ? pDev->pUsbIns->pszName : "", pRh));
}
/**
* Does URB completion on roothub level.
*
* @param pUrb The URB to complete.
*/
{
LogFlow(("%s: vusbUrbCompletionRh: type=%s status=%s\n",
{
if (RT_FAILURE(rc))
}
/* If there is a sniffer on the roothub record the completed URB there too. */
{
if (RT_FAILURE(rc))
}
#ifdef VBOX_WITH_STATISTICS
/*
* Total and per-type submit statistics.
*/
{
{
{
{
if (cb)
{
{
}
else
{
}
}
{
case VUSBSTATUS_OK:
case VUSBSTATUS_DATA_UNDERRUN:
}
}
}
else
{
{
}
else
{
}
}
}
else
{
/* (Note. this also counts the cancelled packets) */
}
}
#endif /* VBOX_WITH_STATISTICS */
/*
* Msg transfers are special virtual transfers associated with
* vusb, not the roothub
*/
{
case VUSBXFERTYPE_MSG:
return;
case VUSBXFERTYPE_ISOC:
/* Don't bother with error callback for isochronous URBs. */
break;
#if 1 /** @todo r=bird: OHCI say "If the Transfer Descriptor is being
* retired because of an error, the Host Controller must update
* the Halt bit of the Endpoint Descriptor."
*
* So, I'll subject all transfertypes to the same halt stuff now. It could
* just happen to fix the logitech disconnect trap in win2k.
*/
default:
#endif
case VUSBXFERTYPE_BULK:
break;
}
#ifdef LOG_ENABLED
#endif
{
}
}
/**
* Certain control requests must not ever be forwarded to the device because
* they are required by the vusb core in order to maintain the vusb internal
* data structures.
*/
{
return true;
{
case VUSB_REQ_CLEAR_FEATURE:
case VUSB_REQ_SET_ADDRESS:
case VUSB_REQ_SET_INTERFACE:
case VUSB_REQ_GET_INTERFACE:
return false;
/*
* If the device wishes it, we'll use the cached device and
* configuration descriptors. (We return false when we want to use the
* cache. Yeah, it's a bit weird to read.)
*/
case VUSB_REQ_GET_DESCRIPTOR:
return true;
{
case VUSB_DT_DEVICE:
case VUSB_DT_CONFIG:
return false;
case VUSB_DT_STRING:
default:
return true;
}
default:
return true;
}
}
/**
* Queues an URB for asynchronous transfer.
* A list of asynchronous URBs is kept by the roothub.
*
* @returns VBox status code (from pfnUrbQueue).
* @param pUrb The URB.
*/
{
#ifdef LOG_ENABLED
#endif
/* Immediately return in case of error.
* XXX There is still a race: The Rh might vanish after this point! */
if (!pRh)
{
Log(("vusbUrbQueueAsyncRh returning VERR_OBJECT_DESTROYED\n"));
return VERR_OBJECT_DESTROYED;
}
if (RT_FAILURE(rc))
{
return rc;
}
/* Queue the pUrb on the roothub */
if (pDev->pAsyncUrbHead)
return VINF_SUCCESS;
}
/**
* Send a control message *synchronously*.
* @return
*/
{
LogFlow(("%s: vusbMsgSubmitSynchronously: pDev=%p[%s]\n", pUrb->pszDesc, pDev, pDev->pUsbIns ? pDev->pUsbIns->pszName : ""));
bool fOk = false;
if (!fSafeRequest)
else
AssertMsgFailed(("oops\n"));
if (fOk)
{
}
else
{
}
/*
* 'Free' the message URB, i.e. put it back to the allocated state.
*/
}
/**
* Callback for dealing with device reset.
*/
{
if (!pExtra)
return;
}
/**
* Callback to free a cancelled message URB.
*
* This is yet another place we're we have to performance acrobatics to
* deal with cancelled URBs. sigh.
*
* The deal here is that we never free message URBs since they are integrated
* into the message pipe state. But since cancel can leave URBs unreaped and in
* a state which require them not to be freed, we'll have to do two things.
* First, if a new message URB is processed we'll have to get a new message
* pipe state. Second, we cannot just free the damn state structure because
* that might lead to heap corruption since it might still be in-flight.
*
* The URB embedded into the message pipe control structure will start in an
* ALLOCATED state. When submitted it will be go to the IN-FLIGHT state. When
* reaped it will go from REAPED to ALLOCATED. When completed in the CANCELLED
* state it will remain in that state (as does normal URBs).
*
* If a new message urb comes up while it's in the CANCELLED state, we will
* orphan it and it will be freed here in vusbMsgFreeUrb. We indicate this
* by setting VUsb.pvFreeCtx to NULL.
*
* If we have to free the message state structure because of device destruction,
* configuration changes, or similar, we will orphan the message pipe state in
* the same way by setting VUsb.pvFreeCtx to NULL and let this function free it.
*
* @param pUrb
*/
{
{
}
else
{
}
}
/**
* Frees the extra state data associated with a message pipe.
*
* @param pExtra The data.
*/
{
if (!pExtra)
return;
{
}
else
}
/**
* Allocates the extra state data required for a control pipe.
*
* @returns Pointer to the allocated and initialized state data.
* @returns NULL on out of memory condition.
* @param pUrb A URB we can copy default data from.
*/
{
/** @todo reuse these? */
if (pExtra)
{
//pExtra->fOk = false;
//pExtra->cbLeft = 0;
//pExtra->Urb.Dev.pvProxyUrb = NULL;
#ifdef LOG_ENABLED
#endif
//pExtra->Urb.VUsb.pCtrlUrb = NULL;
//pExtra->Urb.VUsb.pNext = NULL;
//pExtra->Urb.VUsb.ppPrev = NULL;
//pExtra->Urb.Hci = {0};
//pExtra->Urb.Dev.pvProxyUrb = NULL;
//pExtra->Urb.fShortNotOk = false;
//pExtra->Urb.cbData = 0;
}
return pExtra;
}
/**
* Sets up the message.
*
* The message is associated with the pipe, in what's currently called
* control pipe extra state data (pointed to by pPipe->pCtrl). If this
* is a OUT message, we will no go on collecting data URB. If it's a
* IN message, we'll send it and then queue any incoming data for the
* URBs collecting it.
*
* @returns Success indicator.
*/
{
/*
* Validate length.
*/
{
LogFlow(("vusbMsgSetup: pPipe=%p cbBuf=%u < %u (failure) !!!\n",
return false;
}
/*
* Check if we've got an cancelled message URB. Allocate a new one in that case.
*/
{
if (!pvNew)
{
Log(("vusbMsgSetup: out of memory!!! cbReq=%u\n", RT_OFFSETOF(VUSBCTRLEXTRA, Urb.abData[pExtra->cbMax])));
return false;
}
}
/*
* Check that we've got sufficient space in the message URB.
*/
{
PVUSBCTRLEXTRA pNew = (PVUSBCTRLEXTRA)RTMemRealloc(pExtra, RT_OFFSETOF(VUSBCTRLEXTRA, Urb.abData[cbReq]));
if (!pNew)
{
Log(("vusbMsgSetup: out of memory!!! cbReq=%u %u\n",
return false;
}
{
}
}
/*
* Copy the setup data and prepare for data.
*/
pExtra->fSubmitted = false;
LogFlow(("vusbMsgSetup(%p,,%d): bmRequestType=%#04x bRequest=%#04x wValue=%#06x wIndex=%#06x wLength=0x%.4x\n",
pPipe, cbBuf, pSetup->bmRequestType, pSetup->bRequest, pSetup->wValue, pSetup->wIndex, pSetup->wLength));
return true;
}
/**
* Build the message URB from the given control URB and accompanying message
* pipe state which we grab from the device for the URB.
*
* @param pUrb The URB to submit.
*/
static void vusbMsgDoTransfer(PVUSBURB pUrb, PVUSBSETUP pSetup, PVUSBCTRLEXTRA pExtra, PVUSBPIPE pPipe, PVUSBDEV pDev)
{
/*
* Mark this transfer as sent (cleared at setup time).
*/
pExtra->fSubmitted = true;
/*
* Do we have to do this synchronously?
*/
if (!fSafeRequest)
{
return;
}
/*
* Do it asynchronously.
*/
LogFlow(("%s: vusbMsgDoTransfer: ep=%d pMsgUrb=%p pPipe=%p stage=%s\n",
pExtra->Urb.enmDir = (pSetup->bmRequestType & VUSB_DIR_TO_HOST) ? VUSBDIRECTION_IN : VUSBDIRECTION_OUT;
if (RT_FAILURE(rc))
{
/*
* If we fail submitting it, will not retry but fail immediately.
*
* This keeps things simple. The host OS will have retried if
* it's a proxied device, and if it's a virtual one it really means
* it if it's failing a control message.
*/
LogFlow(("%s: vusbMsgDoTransfer: failed submitting urb! failing it with %s (rc=%Rrc)!!!\n",
}
}
/**
* Fails a URB request with a pipe STALL error.
*
* @returns VINF_SUCCESS indicating that we've completed the URB.
* @param pUrb The URB in question.
*/
{
LogFlow(("%s: vusbMsgStall: pPipe=%p err=STALL stage %s->SETUP\n",
return VINF_SUCCESS;
}
/**
* Submit a control message.
*
* Here we implement the USB defined traffic that occurs in message pipes
* (aka control endpoints). We want to provide a single function for device
* drivers so that they don't all have to reimplement the usb logic for
* themselves. This means we need to keep a little bit of state information
* because control transfers occur over multiple bus transactions. We may
* also need to buffer data over multiple data stages.
*
* @returns VBox status code.
* @param pUrb The URB to submit.
*/
{
#ifdef LOG_ENABLED
#endif
{
return VERR_VUSB_NO_URB_MEMORY;
}
/*
* A setup packet always resets the transaction and the
* end of data transmission is signified by change in
* data direction.
*/
{
LogFlow(("%s: vusbUrbSubmitCtrl: pPipe=%p state %s->SETUP\n",
}
/* (the STATUS stage direction goes the other way) */
{
LogFlow(("%s: vusbUrbSubmitCtrl: pPipe=%p state %s->STATUS\n",
}
/*
* Act according to the current message stage.
*/
{
case CTLSTAGE_SETUP:
/*
* When stall handshake is returned, all subsequent packets
* must generate stall until a setup packet arrives.
*/
{
break;
}
/* Store setup details, return DNR if corrupt */
{
break;
}
{
}
/* pre-buffer our output if it's device-to-host */
{
}
/*
* If there is no DATA stage, we must send it now since there are
* no requirement of a STATUS stage.
*/
else
{
}
break;
case CTLSTAGE_DATA:
{
/*
* If a data stage exceeds the target buffer indicated in
* setup return stall, if data stage returns stall there
* will be no status stage.
*/
{
{
}
/* Variable length data transfers */
{
LogFlow(("%s: vusbUrbSubmitCtrl: Var DATA, pUrb->cbData %d -> %d\n", pUrb->pszDesc, pUrb->cbData, cbLeft));
}
else
{
break;
}
}
{
/* put data received from the device. */
/* advance */
else
{
/* adjust the pUrb->cbData to reflect the number of bytes containing actual data. */
LogFlow(("%s: vusbUrbSubmitCtrl: adjusting last DATA pUrb->cbData, %d -> %d\n",
}
}
else
{
/* get data for sending when completed. */
/* advance */
/*
* If we've got the necessary data, we'll send it now since there are
* no requirement of a STATUS stage.
*/
if ( !pExtra->fSubmitted
{
break;
}
}
break;
}
case CTLSTAGE_STATUS:
|| pExtra->fSubmitted)
{
}
else
{
}
break;
}
return VINF_SUCCESS;
}
/**
* Submit a interrupt URB.
*
* @returns VBox status code.
* @param pUrb The URB to submit.
*/
{
return vusbUrbQueueAsyncRh(pUrb);
}
/**
* Submit a bulk URB.
*
* @returns VBox status code.
* @param pUrb The URB to submit.
*/
{
return vusbUrbQueueAsyncRh(pUrb);
}
/**
* Submit an isochronous URB.
*
* @returns VBox status code.
* @param pUrb The URB to submit.
*/
{
return vusbUrbQueueAsyncRh(pUrb);
}
/**
* Fail a URB with a 'hard-error' sort of error.
*
* @return VINF_SUCCESS (the Urb status indicates the error).
* @param pUrb The URB.
*/
{
/* FIXME: Find out the correct return code from the spec */
return VINF_SUCCESS;
}
/**
* Submit a URB.
*/
{
/*
* Check that the device is in a valid state.
*/
if (enmState == VUSB_DEVICE_STATE_RESET)
{
/* This will postpone the TDs until we're done with the resetting. */
return VERR_VUSB_DEVICE_IS_RESETTING;
}
#ifdef LOG_ENABLED
/* stamp it */
#endif
/** @todo Check max packet size here too? */
/*
* Validate the pipe.
*/
{
Log(("%s: pDev=%p[%s]: SUBMIT: ep %i >= %i!!!\n", pUrb->pszDesc, pDev, pDev->pUsbIns->pszName, pUrb->EndPt, VUSB_PIPE_MAX));
return vusbUrbSubmitHardError(pUrb);
}
{
case VUSBDIRECTION_IN:
break;
case VUSBDIRECTION_SETUP:
case VUSBDIRECTION_OUT:
default:
break;
}
if (!pEndPtDesc)
{
Log(("%s: pDev=%p[%s]: SUBMIT: no endpoint!!! dir=%s e=%i\n",
return vusbUrbSubmitHardError(pUrb);
}
/*
* Check for correct transfer types.
* Our type codes are the same - what a coincidence.
*/
{
Log(("%s: pDev=%p[%s]: SUBMIT: %s transfer requested for %#x endpoint on DstAddress=%i ep=%i dir=%s\n",
pUrb->pszDesc, pDev, pDev->pUsbIns->pszName, vusbUrbTypeName(pUrb->enmType), pEndPtDesc->Core.bmAttributes,
return vusbUrbSubmitHardError(pUrb);
}
/*
* If there's a URB in the read-ahead buffer, use it.
*/
int rc;
{
if (RT_FAILURE(rc))
}
#ifdef VBOX_WITH_USB
{
return rc;
}
#endif
/*
* Take action based on type.
*/
{
case VUSBXFERTYPE_CTRL:
break;
case VUSBXFERTYPE_BULK:
break;
case VUSBXFERTYPE_INTR:
break;
case VUSBXFERTYPE_ISOC:
break;
default:
return vusbUrbSubmitHardError(pUrb);
}
/*
* The device was detached, so we fail everything.
* (We should really detach and destroy the device, but we'll have to wait till Main reacts.)
*/
if (rc == VERR_VUSB_DEVICE_NOT_ATTACHED)
/*
* We don't increment error count if async URBs are in flight, in
* this case we just assume we need to throttle back, this also
* makes sure we don't halt bulk endpoints at the wrong time.
*/
else if ( RT_FAILURE(rc)
/* && pUrb->enmType == VUSBXFERTYPE_BULK ?? */
&& !vusbUrbErrorRh(pUrb))
{
/* don't retry it anymore. */
return VINF_SUCCESS;
}
return rc;
}
/**
* Reap in-flight URBs.
*
* @param pHead Pointer to the head of the URB list.
* @param cMillies Number of milliseconds to block in each reap operation.
* Use 0 to not block at all.
*/
{
while (pUrb)
{
/* Don't touch resetting devices - paranoid safety precaution. */
{
/*
* Reap most URBs pending on a single device.
*/
/**
* This is workaround for race(should be fixed) detach on one EMT thread and frame boundary timer on other
* and leaked URBs (shouldn't be affected by leaked URBs).
*/
{
}
}
/* next */
}
}
/**
* Reap URBs on a per device level.
*
* @returns nothing.
* @param pDev The device instance to reap URBs for.
* @param cMillies Number of milliseconds to block in each reap operation.
* Use 0 to not block at all.
*/
{
/*
* Reap most URBs pending on a single device.
*/
/**
* This is workaround for race(should be fixed) detach on one EMT thread and frame boundary timer on other
* and leaked URBs (shouldn't be affected by leaked URBs).
*/
return;
{
break;
}
}
/**
* Completes the URB.
*/
{
#ifdef VBOX_WITH_USB
// Read-ahead URBs are handled differently
else
#endif
}
/**
* The worker for vusbUrbCancel() which is executed on the I/O thread.
*
* @returns IPRT status code.
* @param pUrb The URB to cancel.
* @param enmMode The way the URB should be canceled.
*/
{
#ifdef VBOX_WITH_STATISTICS
#endif
{
{
}
}
{
{
}
}
else
{
AssertMsg(pUrb->enmState == VUSBURBSTATE_CANCELLED, ("Invalid state %d, pUrb=%p\n", pUrb->enmState, pUrb));
switch (enmMode)
{
default:
AssertMsgFailed(("Invalid cancel mode\n"));
case CANCELMODE_FAIL:
break;
case CANCELMODE_UNDO:
break;
}
}
return VINF_SUCCESS;
}
/**
* Cancels an URB with CRC failure.
*
* Cancelling an URB is a tricky thing. The USBProxy backend can not
* all cancel it and we must keep the URB around until it's ripe and
* can be reaped the normal way. However, we must complete the URB
* now, before leaving this function. This is not nice. sigh.
*
* This function will cancel the URB if it's in-flight and complete
* it. The device will in its pfnCancel method be given the chance to
* say that the URB doesn't need reaping and should be unlinked.
*
* An URB which is in the cancel state after pfnCancel will remain in that
* state and in the async list until its reaped. When it's finally reaped
* it will be unlinked and freed without doing any completion.
*
* There are different modes of canceling an URB. When devices are being
* disconnected etc., they will be completed with an error (CRC). However,
* when the HC needs to temporarily halt communication with a device, the
*
* @param pUrb The URB to cancel.
* @param mode The way the URB should be canceled.
*/
{
}
/**
* Async version of vusbUrbCancel() - doesn't wait for the cancelling to be complete.
*/
{
int rc = vusbDevIoThreadExec(pUrb->VUsb.pDev, 0 /* fFlags */, (PFNRT)vusbUrbCancelWorker, 2, pUrb, mode);
}
/**
* Deals with a ripe URB (i.e. after reaping it).
*
* If an URB is in the reaped or in-flight state, we'll
* complete it. If it's cancelled, we'll simply free it.
* Any other states should never get here.
*
* @param pUrb The URB.
*/
{
{
}
{
}
else
}
/*
* Local Variables:
* mode: c
* c-file-style: "bsd"
* c-basic-offset: 4
* tab-width: 4
* indent-tabs-mode: s
* End:
*/