/* $Id: USBProxyDevice-win.cpp 57358 2015-08-14 15:16:38Z vboxsync $ */ /** @file * USBPROXY - USB proxy, Win32 backend */ /* * Copyright (C) 2006-2015 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * 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_USBPROXY #include #include #include #include #include #include #include #include #include #include #include #include "../USBProxyDevice.h" #include /********************************************************************************************************************************* * Structures and Typedefs * *********************************************************************************************************************************/ typedef struct _QUEUED_URB { PVUSBURB urb; USBSUP_URB urbwin; OVERLAPPED overlapped; DWORD cbReturned; bool fCancelled; } QUEUED_URB, *PQUEUED_URB; typedef struct { /* Critical section to protect this structure. */ RTCRITSECT CritSect; HANDLE hDev; uint8_t bInterfaceNumber; bool fClaimed; /** The allocated size of paHandles and paQueuedUrbs. */ unsigned cAllocatedUrbs; /** The number of URBs in the array. */ unsigned cQueuedUrbs; /** Array of pointers to the in-flight URB structures. */ PQUEUED_URB *paQueuedUrbs; /** Array of handles, this is parallel to paQueuedUrbs. */ PHANDLE paHandles; /* Event sempahore to wakeup the reaper thead. */ HANDLE hEventWakeup; /** Number of queued URBs waiting to get into the handle list. */ unsigned cPendingUrbs; /** Array of pending URBs. */ PQUEUED_URB aPendingUrbs[64]; } PRIV_USBW32, *PPRIV_USBW32; /* All functions are returning 1 on success, 0 on error */ /********************************************************************************************************************************* * Internal Functions * *********************************************************************************************************************************/ static int usbProxyWinSetInterface(PUSBPROXYDEV p, int ifnum, int setting); /** * Converts the given Windows error code to VBox handling unplugged devices. * * @returns VBox status code. * @param pProxDev The USB proxy device instance. * @param dwErr Windows error code. */ static int usbProxyWinHandleUnpluggedDevice(PUSBPROXYDEV pProxyDev, DWORD dwErr) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); if ( dwErr == ERROR_INVALID_HANDLE_STATE || dwErr == ERROR_BAD_COMMAND) { Log(("usbproxy: device %x unplugged!!\n", pPriv->hDev)); pProxyDev->fDetached = true; } else AssertMsgFailed(("lasterr=%d\n", dwErr)); return RTErrConvertFromWin32(dwErr); } /** * Open a USB device and create a backend instance for it. * * @returns VBox status code. */ static DECLCALLBACK(int) usbProxyWinOpen(PUSBPROXYDEV pProxyDev, const char *pszAddress, void *pvBackend) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); int rc = VINF_SUCCESS; pPriv->cAllocatedUrbs = 32; pPriv->paHandles = (PHANDLE)RTMemAllocZ(sizeof(pPriv->paHandles[0]) * pPriv->cAllocatedUrbs); pPriv->paQueuedUrbs = (PQUEUED_URB *)RTMemAllocZ(sizeof(pPriv->paQueuedUrbs[0]) * pPriv->cAllocatedUrbs); if ( pPriv->paQueuedUrbs && pPriv->paHandles) { /* * Open the device. */ pPriv->hDev = CreateFile(pszAddress, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, // no SECURITY_ATTRIBUTES structure OPEN_EXISTING, // No special create flags FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_OVERLAPPED, // overlapped IO NULL); // No template file if (pPriv->hDev != INVALID_HANDLE_VALUE) { Log(("usbProxyWinOpen: hDev=%p\n", pPriv->hDev)); /* * Check the version */ USBSUP_VERSION version = {0}; DWORD cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_GET_VERSION, NULL, 0, &version, sizeof(version), &cbReturned, NULL)) { if (!( version.u32Major != USBDRV_MAJOR_VERSION || version.u32Minor < USBDRV_MINOR_VERSION)) { USBSUP_CLAIMDEV in; in.bInterfaceNumber = 0; cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_CLAIM_DEVICE, &in, sizeof(in), &in, sizeof(in), &cbReturned, NULL)) { if (in.fClaimed) { pPriv->fClaimed = true; #if 0 /** @todo this needs to be enabled if windows chooses a default config. Test with the TrekStor GO Stick. */ pProxyDev->iActiveCfg = 1; pProxyDev->cIgnoreSetConfigs = 1; #endif rc = RTCritSectInit(&pPriv->CritSect); AssertRC(rc); pPriv->hEventWakeup = CreateEvent(NULL, FALSE, FALSE, NULL); Assert(pPriv->hEventWakeup); pPriv->paHandles[0] = pPriv->hEventWakeup; return VINF_SUCCESS; } rc = VERR_GENERAL_FAILURE; Log(("usbproxy: unable to claim device %x (%s)!!\n", pPriv->hDev, pszAddress)); } } else { rc = VERR_VERSION_MISMATCH; Log(("usbproxy: Version mismatch: %d.%d != %d.%d (cur)\n", version.u32Major, version.u32Minor, USBDRV_MAJOR_VERSION, USBDRV_MINOR_VERSION)); } } /* Convert last error if necessary */ if (RT_SUCCESS(rc)) { DWORD dwErr = GetLastError(); Log(("usbproxy: last error %d\n", dwErr)); rc = RTErrConvertFromWin32(dwErr); } CloseHandle(pPriv->hDev); pPriv->hDev = INVALID_HANDLE_VALUE; } else { Log(("usbproxy: FAILED to open '%s'! last error %d\n", pszAddress, GetLastError())); rc = VERR_FILE_NOT_FOUND; } } else rc = VERR_NO_MEMORY; RTMemFree(pPriv->paQueuedUrbs); RTMemFree(pPriv->paHandles); return rc; } /** * Copy the device and free resources associated with the backend. */ static DECLCALLBACK(void) usbProxyWinClose(PUSBPROXYDEV pProxyDev) { /* Here we just close the device and free up p->priv * there is no need to do anything like cancel outstanding requests * that will have been done already */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); Assert(pPriv); if (!pPriv) return; Log(("usbProxyWinClose: %p\n", pPriv->hDev)); if (pPriv->hDev != INVALID_HANDLE_VALUE) { Assert(pPriv->fClaimed); USBSUP_RELEASEDEV in; DWORD cbReturned = 0; in.bInterfaceNumber = pPriv->bInterfaceNumber; if (!DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_RELEASE_DEVICE, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) { Log(("usbproxy: usbProxyWinClose: DeviceIoControl %#x failed with %#x!!\n", pPriv->hDev, GetLastError())); } if (!CloseHandle(pPriv->hDev)) AssertLogRelMsgFailed(("usbproxy: usbProxyWinClose: CloseHandle %#x failed with %#x!!\n", pPriv->hDev, GetLastError())); pPriv->hDev = INVALID_HANDLE_VALUE; } CloseHandle(pPriv->hEventWakeup); RTCritSectDelete(&pPriv->CritSect); RTMemFree(pPriv->paQueuedUrbs); RTMemFree(pPriv->paHandles); } static DECLCALLBACK(int) usbProxyWinReset(PUSBPROXYDEV pProxyDev, bool fResetOnLinux) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); DWORD cbReturned; int rc; Assert(pPriv); Log(("usbproxy: Reset %x\n", pPriv->hDev)); /* Here we just need to assert reset signalling on the USB device */ cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_RESET, NULL, 0, NULL, 0, &cbReturned, NULL)) { #if 0 /** @todo this needs to be enabled if windows chooses a default config. Test with the TrekStor GO Stick. */ pProxyDev->iActiveCfg = 1; pProxyDev->cIgnoreSetConfigs = 2; #else pProxyDev->iActiveCfg = -1; pProxyDev->cIgnoreSetConfigs = 0; #endif return VINF_SUCCESS; } rc = GetLastError(); if (rc == ERROR_DEVICE_REMOVED) { Log(("usbproxy: device %p unplugged!!\n", pPriv->hDev)); pProxyDev->fDetached = true; } return RTErrConvertFromWin32(rc); } static DECLCALLBACK(int) usbProxyWinSetConfig(PUSBPROXYDEV pProxyDev, int cfg) { /* Send a SET_CONFIGURATION command to the device. We don't do this * as a normal control message, because the OS might not want to * be left out of the loop on such a thing. * * It would be OK to send a SET_CONFIGURATION control URB at this * point but it has to be synchronous. */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); USBSUP_SET_CONFIG in; DWORD cbReturned; Assert(pPriv); Log(("usbproxy: Set config of %p to %d\n", pPriv->hDev, cfg)); in.bConfigurationValue = cfg; /* Here we just need to assert reset signalling on the USB device */ cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_SET_CONFIG, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError()); } static DECLCALLBACK(int) usbProxyWinClaimInterface(PUSBPROXYDEV pProxyDev, int ifnum) { /* Called just before we use an interface. Needed on Linux to claim * the interface from the OS, since even when proxying the host OS * might want to allow other programs to use the unused interfaces. * Not relevant for Windows. */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); pPriv->bInterfaceNumber = ifnum; Assert(pPriv); return VINF_SUCCESS; } static DECLCALLBACK(int) usbProxyWinReleaseInterface(PUSBPROXYDEV pProxyDev, int ifnum) { /* The opposite of claim_interface. */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); Assert(pPriv); return VINF_SUCCESS; } static DECLCALLBACK(int) usbProxyWinSetInterface(PUSBPROXYDEV pProxyDev, int ifnum, int setting) { /* Select an alternate setting for an interface, the same applies * here as for set_config, you may convert this in to a control * message if you want but it must be synchronous */ PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); USBSUP_SELECT_INTERFACE in; DWORD cbReturned; Assert(pPriv); Log(("usbproxy: Select interface of %x to %d/%d\n", pPriv->hDev, ifnum, setting)); in.bInterfaceNumber = ifnum; in.bAlternateSetting = setting; /* Here we just need to assert reset signalling on the USB device */ cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_SELECT_INTERFACE, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError()); } /** * Clears the halted endpoint 'ep'. */ static DECLCALLBACK(int) usbProxyWinClearHaltedEndPt(PUSBPROXYDEV pProxyDev, unsigned int ep) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); USBSUP_CLEAR_ENDPOINT in; DWORD cbReturned; Assert(pPriv); Log(("usbproxy: Clear endpoint %d of %x\n", ep, pPriv->hDev)); in.bEndpoint = ep; cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_CLEAR_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError()); } /** * Aborts a pipe/endpoint (cancels all outstanding URBs on the endpoint). */ static int usbProxyWinAbortEndPt(PUSBPROXYDEV pProxyDev, unsigned int ep) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); USBSUP_CLEAR_ENDPOINT in; DWORD cbReturned; int rc; Assert(pPriv); Log(("usbproxy: Abort endpoint %d of %x\n", ep, pPriv->hDev)); in.bEndpoint = ep; cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; return usbProxyWinHandleUnpluggedDevice(pProxyDev, GetLastError()); } /** * @copydoc USBPROXYBACK::pfnUrbQueue */ static DECLCALLBACK(int) usbProxyWinUrbQueue(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); Assert(pPriv); /* * Allocate and initialize a URB queue structure. */ /** @todo pool these */ PQUEUED_URB pQUrbWin = (PQUEUED_URB)RTMemAllocZ(sizeof(QUEUED_URB)); if (!pQUrbWin) return VERR_NO_MEMORY; switch (pUrb->enmType) { case VUSBXFERTYPE_CTRL: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_CTRL; break; /* you won't ever see these */ case VUSBXFERTYPE_ISOC: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_ISOC; pQUrbWin->urbwin.numIsoPkts = pUrb->cIsocPkts; for (unsigned i = 0; i < pUrb->cIsocPkts; ++i) { pQUrbWin->urbwin.aIsoPkts[i].cb = pUrb->aIsocPkts[i].cb; pQUrbWin->urbwin.aIsoPkts[i].off = pUrb->aIsocPkts[i].off; pQUrbWin->urbwin.aIsoPkts[i].stat = USBSUP_XFER_OK; } break; case VUSBXFERTYPE_BULK: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_BULK; break; case VUSBXFERTYPE_INTR: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_INTR; break; case VUSBXFERTYPE_MSG: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_MSG; break; default: AssertMsgFailed(("Invalid type %d\n", pUrb->enmType)); return VERR_INVALID_PARAMETER; } switch (pUrb->enmDir) { case VUSBDIRECTION_SETUP: AssertFailed(); pQUrbWin->urbwin.dir = USBSUP_DIRECTION_SETUP; break; case VUSBDIRECTION_IN: pQUrbWin->urbwin.dir = USBSUP_DIRECTION_IN; break; case VUSBDIRECTION_OUT: pQUrbWin->urbwin.dir = USBSUP_DIRECTION_OUT; break; default: AssertMsgFailed(("Invalid direction %d\n", pUrb->enmDir)); return VERR_INVALID_PARAMETER; } Log(("usbproxy: Queue URB %p ep=%d cbData=%d abData=%p cIsocPkts=%d\n", pUrb, pUrb->EndPt, pUrb->cbData, pUrb->abData, pUrb->cIsocPkts)); pQUrbWin->urb = pUrb; pQUrbWin->urbwin.ep = pUrb->EndPt; pQUrbWin->urbwin.len = pUrb->cbData; pQUrbWin->urbwin.buf = pUrb->abData; pQUrbWin->urbwin.error = USBSUP_XFER_OK; pQUrbWin->urbwin.flags = USBSUP_FLAG_NONE; if (pUrb->enmDir == VUSBDIRECTION_IN && !pUrb->fShortNotOk) pQUrbWin->urbwin.flags = USBSUP_FLAG_SHORT_OK; int rc = VINF_SUCCESS; pQUrbWin->overlapped.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (pQUrbWin->overlapped.hEvent != INVALID_HANDLE_VALUE) { pUrb->Dev.pvPrivate = pQUrbWin; if ( DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_SEND_URB, &pQUrbWin->urbwin, sizeof(pQUrbWin->urbwin), &pQUrbWin->urbwin, sizeof(pQUrbWin->urbwin), &pQUrbWin->cbReturned, &pQUrbWin->overlapped) || GetLastError() == ERROR_IO_PENDING) { /* insert into the queue */ RTCritSectEnter(&pPriv->CritSect); unsigned j = pPriv->cPendingUrbs; pPriv->aPendingUrbs[j] = pQUrbWin; pPriv->cPendingUrbs++; RTCritSectLeave(&pPriv->CritSect); SetEvent(pPriv->hEventWakeup); return VINF_SUCCESS; } else { DWORD dwErr = GetLastError(); if ( dwErr == ERROR_INVALID_HANDLE_STATE || dwErr == ERROR_BAD_COMMAND) { Log(("usbproxy: device %p unplugged!!\n", pPriv->hDev)); pProxyDev->fDetached = true; } else AssertMsgFailed(("dwErr=%X urbwin.error=%d (submit urb)\n", dwErr, pQUrbWin->urbwin.error)); rc = RTErrConvertFromWin32(dwErr); CloseHandle(pQUrbWin->overlapped.hEvent); pQUrbWin->overlapped.hEvent = INVALID_HANDLE_VALUE; } } #ifdef DEBUG_misha else { AssertMsgFailed(("FAILED!!, hEvent(0x%p)\n", pQUrbWin->overlapped.hEvent)); rc = VERR_NO_MEMORY; } #endif Assert(pQUrbWin->overlapped.hEvent == INVALID_HANDLE_VALUE); RTMemFree(pQUrbWin); return rc; } /** * Convert Windows proxy URB status to VUSB status. * * @returns VUSB status constant. * @param win_status Windows USB proxy status constant. */ static VUSBSTATUS usbProxyWinStatusToVUsbStatus(USBSUP_ERROR win_status) { VUSBSTATUS vusb_status; switch (win_status) { case USBSUP_XFER_OK: vusb_status = VUSBSTATUS_OK; break; case USBSUP_XFER_STALL: vusb_status = VUSBSTATUS_STALL; break; case USBSUP_XFER_DNR: vusb_status = VUSBSTATUS_DNR; break; case USBSUP_XFER_CRC: vusb_status = VUSBSTATUS_CRC; break; case USBSUP_XFER_NAC: vusb_status = VUSBSTATUS_NOT_ACCESSED; break; case USBSUP_XFER_UNDERRUN: vusb_status = VUSBSTATUS_DATA_UNDERRUN; break; case USBSUP_XFER_OVERRUN: vusb_status = VUSBSTATUS_DATA_OVERRUN; break; default: AssertMsgFailed(("USB: Invalid error %d\n", win_status)); vusb_status = VUSBSTATUS_DNR; break; } return vusb_status; } /** * 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) usbProxyWinUrbReap(PUSBPROXYDEV pProxyDev, RTMSINTERVAL cMillies) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); AssertReturn(pPriv, NULL); /* * There are some unnecessary calls, just return immediately or * WaitForMultipleObjects will fail. */ if ( pPriv->cQueuedUrbs <= 0 && pPriv->cPendingUrbs == 0) { if ( cMillies != 0 && pPriv->cPendingUrbs == 0) { /* Wait for the wakeup call. */ DWORD cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies; DWORD rc = WaitForMultipleObjects(1, &pPriv->hEventWakeup, FALSE, cMilliesWait); } return NULL; } again: /* Check for pending URBs. */ if (pPriv->cPendingUrbs) { RTCritSectEnter(&pPriv->CritSect); /* Ensure we've got sufficient space in the arrays. */ if (pPriv->cQueuedUrbs + pPriv->cPendingUrbs + 1 > pPriv->cAllocatedUrbs) { unsigned cNewMax = pPriv->cAllocatedUrbs + pPriv->cPendingUrbs + 1; void *pv = RTMemRealloc(pPriv->paHandles, sizeof(pPriv->paHandles[0]) * (cNewMax + 1)); /* One extra for the wakeup event. */ if (!pv) { AssertMsgFailed(("RTMemRealloc failed for paHandles[%d]", cNewMax)); //break; } pPriv->paHandles = (PHANDLE)pv; pv = RTMemRealloc(pPriv->paQueuedUrbs, sizeof(pPriv->paQueuedUrbs[0]) * cNewMax); if (!pv) { AssertMsgFailed(("RTMemRealloc failed for paQueuedUrbs[%d]", cNewMax)); //break; } pPriv->paQueuedUrbs = (PQUEUED_URB *)pv; pPriv->cAllocatedUrbs = cNewMax; } /* Copy the pending URBs over. */ for (unsigned i = 0; i < pPriv->cPendingUrbs; i++) { pPriv->paHandles[pPriv->cQueuedUrbs + i] = pPriv->aPendingUrbs[i]->overlapped.hEvent; pPriv->paQueuedUrbs[pPriv->cQueuedUrbs + i] = pPriv->aPendingUrbs[i]; } pPriv->cQueuedUrbs += pPriv->cPendingUrbs; pPriv->cPendingUrbs = 0; pPriv->paHandles[pPriv->cQueuedUrbs] = pPriv->hEventWakeup; pPriv->paHandles[pPriv->cQueuedUrbs + 1] = INVALID_HANDLE_VALUE; RTCritSectLeave(&pPriv->CritSect); } /* * Wait/poll. * * ASSUMPTIONS: * 1. The usbProxyWinUrbReap can not be run concurrently with each other * so racing the cQueuedUrbs access/modification can not occur. * 2. The usbProxyWinUrbReap can not be run concurrently with * usbProxyWinUrbQueue so they can not race the pPriv->paHandles * access/realloc. */ unsigned cQueuedUrbs = ASMAtomicReadU32((volatile uint32_t *)&pPriv->cQueuedUrbs); DWORD cMilliesWait = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies; PVUSBURB pUrb = NULL; DWORD rc = WaitForMultipleObjects(cQueuedUrbs + 1, pPriv->paHandles, FALSE, cMilliesWait); /* If the wakeup event fired return immediately. */ if (rc == WAIT_OBJECT_0 + cQueuedUrbs) { if (pPriv->cPendingUrbs) goto again; return NULL; } if (rc >= WAIT_OBJECT_0 && rc < WAIT_OBJECT_0 + cQueuedUrbs) { RTCritSectEnter(&pPriv->CritSect); unsigned iUrb = rc - WAIT_OBJECT_0; PQUEUED_URB pQUrbWin = pPriv->paQueuedUrbs[iUrb]; pUrb = pQUrbWin->urb; /* * Remove it from the arrays. */ cQueuedUrbs = --pPriv->cQueuedUrbs; if (cQueuedUrbs != iUrb) { /* Move the array forward */ for (unsigned i=iUrb;ipaHandles[i] = pPriv->paHandles[i+1]; pPriv->paQueuedUrbs[i] = pPriv->paQueuedUrbs[i+1]; } } pPriv->paHandles[cQueuedUrbs] = pPriv->hEventWakeup; pPriv->paHandles[cQueuedUrbs + 1] = INVALID_HANDLE_VALUE; pPriv->paQueuedUrbs[cQueuedUrbs] = NULL; RTCritSectLeave(&pPriv->CritSect); Assert(cQueuedUrbs == pPriv->cQueuedUrbs); /* * Update the urb. */ pUrb->enmStatus = usbProxyWinStatusToVUsbStatus(pQUrbWin->urbwin.error); pUrb->cbData = (uint32_t)pQUrbWin->urbwin.len; if (pUrb->enmType == VUSBXFERTYPE_ISOC) { for (unsigned i = 0; i < pUrb->cIsocPkts; ++i) { /* NB: Windows won't change the packet offsets, but the packets may * be only partially filled or completely empty. */ pUrb->aIsocPkts[i].enmStatus = usbProxyWinStatusToVUsbStatus(pQUrbWin->urbwin.aIsoPkts[i].stat); pUrb->aIsocPkts[i].cb = pQUrbWin->urbwin.aIsoPkts[i].cb; } } Log(("usbproxy: pUrb=%p (#%d) ep=%d cbData=%d status=%d cIsocPkts=%d ready\n", pUrb, rc - WAIT_OBJECT_0, pQUrbWin->urb->EndPt, pQUrbWin->urb->cbData, pUrb->enmStatus, pUrb->cIsocPkts)); /* free the urb queuing structure */ if (pQUrbWin->overlapped.hEvent != INVALID_HANDLE_VALUE) { CloseHandle(pQUrbWin->overlapped.hEvent); pQUrbWin->overlapped.hEvent = INVALID_HANDLE_VALUE; } RTMemFree(pQUrbWin); } else if ( rc == WAIT_FAILED || (rc >= WAIT_ABANDONED_0 && rc < WAIT_ABANDONED_0 + cQueuedUrbs)) AssertMsgFailed(("USB: WaitForMultipleObjects %d objects failed with rc=%d and last error %d\n", cQueuedUrbs, rc, GetLastError())); return pUrb; } /** * Cancels an in-flight URB. * * The URB requires reaping, so we don't change its state. * * @remark There isn't a way to cancel a specific URB on Windows. * on darwin. The interface only supports the aborting of * all URBs pending on an endpoint. Luckily that is usually * exactly what the guest wants to do. */ static DECLCALLBACK(int) usbProxyWinUrbCancel(PUSBPROXYDEV pProxyDev, PVUSBURB pUrb) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); PQUEUED_URB pQUrbWin = (PQUEUED_URB)pUrb->Dev.pvPrivate; USBSUP_CLEAR_ENDPOINT in; DWORD cbReturned; AssertPtrReturn(pQUrbWin, VERR_INVALID_PARAMETER); in.bEndpoint = pUrb->EndPt | (pUrb->enmDir == VUSBDIRECTION_IN ? 0x80 : 0); Log(("Cancel urb %p, endpoint %x\n", pUrb, in.bEndpoint)); cbReturned = 0; if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL)) return VINF_SUCCESS; DWORD dwErr = GetLastError(); if ( dwErr == ERROR_INVALID_HANDLE_STATE || dwErr == ERROR_BAD_COMMAND) { Log(("usbproxy: device %x unplugged!!\n", pPriv->hDev)); pProxyDev->fDetached = true; return VINF_SUCCESS; /* Fake success and deal with the unplugged device elsewhere. */ } AssertMsgFailed(("lastErr=%ld\n", dwErr)); return RTErrConvertFromWin32(dwErr); } static DECLCALLBACK(int) usbProxyWinWakeup(PUSBPROXYDEV pProxyDev) { PPRIV_USBW32 pPriv = USBPROXYDEV_2_DATA(pProxyDev, PPRIV_USBW32); SetEvent(pPriv->hEventWakeup); return VINF_SUCCESS; } /** * The Win32 USB Proxy Backend. */ extern const USBPROXYBACK g_USBProxyDeviceHost = { /* pszName */ "host", /* cbBackend */ sizeof(PRIV_USBW32), usbProxyWinOpen, NULL, usbProxyWinClose, usbProxyWinReset, usbProxyWinSetConfig, usbProxyWinClaimInterface, usbProxyWinReleaseInterface, usbProxyWinSetInterface, usbProxyWinClearHaltedEndPt, usbProxyWinUrbQueue, usbProxyWinUrbCancel, usbProxyWinUrbReap, usbProxyWinWakeup, 0 };