USBProxyDevice-win.cpp revision ab455939477d81b7bb500586f88029d3a3c0b34c
/* $Id$ */
/** @file
* USBPROXY - USB proxy, Win32 backend
*
* NOTE: This code assumes only one thread will use it at a time!!
* bird: usbProxyWinReset() will be called in a separate thread because it
* will usually take >=10ms. So, the assumption is broken.
*/
/*
* Copyright (C) 2006-2007 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_USBPROXY
#include <windows.h>
#include "../USBProxyDevice.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
typedef struct _QUEUED_URB
{
bool fCancelled;
} QUEUED_URB, *PQUEUED_URB;
typedef struct
{
/* Critical section to protect this structure. */
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. */
/** Array of handles, this is parallel to paQueuedUrbs. */
/** The number of pending URBs. */
unsigned cPendingUrbs;
/** Array of pointers to the pending URB structures. */
/* Thread handle for async io handling. */
/* Event semaphore for signalling the arrival of a new URB */
/* Event semaphore for signalling thread termination */
/* Set when the async io thread is started. */
bool fThreadAsyncIoActive;
} PRIV_USBW32, *PPRIV_USBW32;
/* All functions are returning 1 on success, 0 on error */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/**
* Open a USB device and create a backend instance for it.
*
* @returns VBox status code.
*/
{
/* Here you just need to use pProxyDev->priv to store whatever per-device
* data is needed
*/
/*
* Allocate private device instance data and use USBPROXYDEV::Backend::pv to point to it.
*/
if (!pPriv)
return VERR_NO_MEMORY;
int rc = VINF_SUCCESS;
pPriv->paQueuedUrbs = (PQUEUED_URB *)RTMemAllocZ(sizeof(pPriv->paQueuedUrbs[0]) * pPriv->cAllocatedUrbs);
if ( pPriv->paQueuedUrbs
{
/*
* Open the device.
*/
NULL, // no SECURITY_ATTRIBUTES structure
OPEN_EXISTING, // No special create flags
NULL); // No template file
{
/*
* 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))
{
{
in.bInterfaceNumber = 0;
cbReturned = 0;
if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_CLAIM_DEVICE, &in, sizeof(in), &in, sizeof(in), &cbReturned, NULL))
{
{
#if 0 /** @todo this needs to be enabled if windows chooses a default config. Test with the TrekStor GO Stick. */
#endif
rc = RTThreadCreate(&pPriv->hThreadAsyncIo, usbProxyWinAsyncIoThread, pPriv, 128 * _1K, RTTHREADTYPE_IO, 0, "USBAsyncIo");
return VINF_SUCCESS;
}
}
}
else
{
Log(("usbproxy: Version mismatch: %d.%d != %d.%d (cur)\n",
}
}
/* Convert last error if necessary */
if (RT_SUCCESS(rc))
{
}
}
else
{
}
}
else
rc = VERR_NO_MEMORY;
return rc;
}
/**
* Copy the device and free resources associated with the backend.
*/
{
/* 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
*/
if (!pPriv)
return;
{
DWORD cbReturned = 0;
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()));
}
AssertLogRelMsgFailed(("usbproxy: usbProxyWinClose: CloseHandle %#x failed with %#x!!\n", pPriv->hDev, GetLastError()));
}
/* Terminate async thread (which will clean up hEventAsyncTerm) */
}
{
int rc;
/* Here we just need to assert reset signalling on the USB device */
cbReturned = 0;
{
#if 0 /** @todo this needs to be enabled if windows chooses a default config. Test with the TrekStor GO Stick. */
#else
pProxyDev->cIgnoreSetConfigs = 0;
#endif
return VINF_SUCCESS;
}
rc = GetLastError();
if (rc == ERROR_DEVICE_REMOVED)
{
}
return RTErrConvertFromWin32(rc);
}
{
/* 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.
*/
/* 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 1;
if ( GetLastError() == ERROR_INVALID_HANDLE_STATE
|| GetLastError() == ERROR_BAD_COMMAND)
{
}
else
return 0;
}
{
/* 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.
*/
return true;
}
{
/* The opposite of claim_interface. */
return true;
}
{
/* 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
*/
/* 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 true;
if ( GetLastError() == ERROR_INVALID_HANDLE_STATE
|| GetLastError() == ERROR_BAD_COMMAND)
{
}
else
return 0;
}
/**
* Clears the halted endpoint 'ep'.
*/
{
cbReturned = 0;
if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_CLEAR_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
return true;
if ( GetLastError() == ERROR_INVALID_HANDLE_STATE
|| GetLastError() == ERROR_BAD_COMMAND)
{
}
else
return 0;
}
/**
*/
{
int rc;
cbReturned = 0;
if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
return VINF_SUCCESS;
rc = GetLastError();
if ( rc == ERROR_INVALID_HANDLE_STATE
|| rc == ERROR_BAD_COMMAND)
{
}
else
return RTErrConvertFromWin32(rc);
}
/**
* @copydoc USBPROXYBACK::pfnUrbQueue
*/
{
/*
* Allocate and initialize a URB queue structure.
*/
/** @todo pool these */
if (!pQUrbWin)
return false;
{
case VUSBXFERTYPE_CTRL: pQUrbWin->urbwin.type = USBSUP_TRANSFER_TYPE_CTRL; break; /* you won't ever see these */
{
}
break;
default:
return false;
}
{
case VUSBDIRECTION_SETUP:
AssertFailed();
break;
case VUSBDIRECTION_IN:
break;
case VUSBDIRECTION_OUT:
break;
default:
return false;
}
Log(("usbproxy: Queue URB %p ep=%d cbData=%d abData=%p cIsocPkts=%d\n", pUrb, pUrb->EndPt, pUrb->cbData, pUrb->abData, pUrb->cIsocPkts));
{
while (!pPriv->fThreadAsyncIoActive)
RTThreadSleep(1);
do
{
/* Ensure we've got sufficient space in the arrays.
* Do it inside the lock to ensure we do not concur
* with the usbProxyWinAsyncIoThread */
{
if (!pv)
{
break;
}
if (!pv)
{
break;
}
}
pPriv->cPendingUrbs++;
return true;
} while (0);
}
#ifdef DEBUG_misha
else
{
}
#endif
return false;
}
/**
* Convert Windows proxy URB status to VUSB status.
*
* @returns VUSB status constant.
* @param win_status Windows USB proxy status constant.
*/
{
switch (win_status)
{
default:
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.
*/
{
/*
* There are some unnecessary calls, just return immediately or
* WaitForMultipleObjects will fail.
*/
if (pPriv->cQueuedUrbs <= 0)
{
if (cMillies != 0)
{
/* Wait for the URBs to be queued if there are some pending */
while (pPriv->cPendingUrbs)
RTThreadSleep(1);
if (pPriv->cQueuedUrbs <= 0)
return NULL;
}
else
return NULL;
}
/*
*
* 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
*/
{
/*
* Remove it from the arrays.
*/
if (cQueuedUrbs != iUrb)
{
/* Move the array forward */
for (unsigned i=iUrb;i<cQueuedUrbs;i++)
{
}
}
/*
* Update the urb.
*/
{
{
/* NB: Windows won't change the packet offsets, but the packets may
* be only partially filled or completely empty.
*/
}
}
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 */
{
}
}
else if ( rc == WAIT_FAILED
AssertMsgFailed(("USB: WaitForMultipleObjects %d objects failed with rc=%d and last error %d\n", cQueuedUrbs, rc, GetLastError()));
return pUrb;
}
/* Thread to handle async URB queueing. */
{
Log(("usbProxyWinAsyncIoThread: start\n"));
pPriv->fThreadAsyncIoActive = true;
while (true)
{
if (ret == WAIT_OBJECT_0)
{
/*
* Submit pending URBs.
*/
for (unsigned i = 0; i < pPriv->cPendingUrbs; i++)
{
if (pQUrbWin)
{
|| GetLastError() == ERROR_IO_PENDING)
{
/* insert into the queue */
unsigned j = pPriv->cQueuedUrbs;
/* do an atomic increment to allow usbProxyWinUrbReap thread get it outside a lock,
* being sure that pPriv->paHandles contains cQueuedUrbs valid handles */
}
else
{
if ( dwErr == ERROR_INVALID_HANDLE_STATE
|| dwErr == ERROR_BAD_COMMAND)
{
}
else
}
}
pPriv->aPendingUrbs[i] = 0;
}
pPriv->cPendingUrbs = 0;
}
else
{
Log(("usbProxyWinAsyncIoThread: terminating\n"));
break;
}
else
{
break;
}
}
return 0;
}
/**
* 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.
*/
{
int rc;
cbReturned = 0;
if (DeviceIoControl(pPriv->hDev, SUPUSB_IOCTL_USB_ABORT_ENDPOINT, &in, sizeof(in), NULL, 0, &cbReturned, NULL))
return;
rc = GetLastError();
if ( rc == ERROR_INVALID_HANDLE_STATE
|| rc == ERROR_BAD_COMMAND)
{
}
else
}
/**
* The Win32 USB Proxy Backend.
*/
extern const USBPROXYBACK g_USBProxyDeviceHost =
{
"host",
NULL,
0
};