VBoxUsbLib-win.cpp revision 97b634ea021fd984782256de4ba4ff31cdb96c47
/* $Id$ */
/** @file
* VBox USB R3 Driver Interface library
*/
/*
* Copyright (C) 2011 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.
*/
#define LOG_GROUP LOG_GROUP_DRV_USBPROXY
#include <windows.h>
#include <VBox/usblib-win.h>
#include <VBox/VBoxDrvCfg-win.h>
#include <stdio.h>
#include <setupapi.h>
#include <usbdi.h>
#include <hidsdi.h>
#define VBOX_USB_USE_DEVICE_NOTIFICATION
# include <Dbt.h>
#endif
typedef struct _USB_INTERFACE_DESCRIPTOR2 {
typedef struct VBOXUSBGLOBALSTATE
{
#endif
static VBOXUSBGLOBALSTATE g_VBoxUsbGlobal;
typedef struct VBOXUSB_STRING_DR_ENTRY
{
struct VBOXUSB_STRING_DR_ENTRY *pNext;
/* this represents VBoxUsb device instance */
typedef struct VBOXUSB_DEV
{
struct VBOXUSB_DEV *pNext;
char szName[512];
char szDriverRegName[512];
} VBOXUSB_DEV, *PVBOXUSB_DEV;
{
hOut = CreateFile(pVuDev->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
if (hOut == INVALID_HANDLE_VALUE)
{
AssertMsgFailed((__FUNCTION__": CreateFile FAILED to open %s, winEr (%d)\n", pVuDev->szName, winEr));
return VERR_GENERAL_FAILURE;
}
USBSUP_VERSION version = {0};
DWORD cbReturned = 0;
int rc = VERR_VERSION_MISMATCH;
do
{
if (!DeviceIoControl(hOut, SUPUSB_IOCTL_GET_VERSION, NULL, 0,&version, sizeof(version), &cbReturned, NULL))
{
AssertMsgFailed((__FUNCTION__": DeviceIoControl SUPUSB_IOCTL_GET_VERSION failed with LastError=%Rwa\n", GetLastError()));
break;
}
{
AssertMsgFailed((__FUNCTION__": Invalid version %d:%d vs %d:%d\n", version.u32Major, version.u32Minor, USBDRV_MAJOR_VERSION, USBDRV_MINOR_VERSION));
break;
}
{
AssertMsgFailed((__FUNCTION__": DeviceIoControl SUPUSB_IOCTL_IS_OPERATIONAL failed with LastError=%Rwa\n", GetLastError()));
break;
}
rc = VINF_SUCCESS;
} while (0);
return rc;
}
static int usbLibVuDevicePopulate(PVBOXUSB_DEV pVuDev, HDEVINFO hDevInfo, PSP_DEVICE_INTERFACE_DATA pIfData)
{
int rc = VINF_SUCCESS;
NULL, /* OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData */
0, /* IN DWORD DeviceInterfaceDetailDataSize */
);
PSP_DEVICE_INTERFACE_DETAIL_DATA pIfDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)RTMemAllocZ(cbIfDetailData);
if (!pIfDetailData)
{
return VERR_OUT_OF_RESOURCES;
}
/* the cbSize should contain the sizeof a fixed-size part according to the docs */
do
{
&DevInfoData))
{
AssertMsgFailed((__FUNCTION__": SetupDiGetDeviceInterfaceDetail, cbRequired (%d), was (%d), winEr (%d)\n", cbDbgRequired, cbIfDetailData, winEr));
break;
}
NULL, /* OUT PDWORD PropertyRegDataType */
sizeof (pVuDev->szDriverRegName),
{
AssertMsgFailed((__FUNCTION__": SetupDiGetDeviceRegistryPropertyA, cbRequired (%d), was (%d), winEr (%d)\n", cbDbgRequired, sizeof (pVuDev->szDriverRegName), winEr));
break;
}
} while (0);
return rc;
}
{
while (pDevInfos)
{
}
}
{
*pcVuDevs = 0;
NULL, /* IN PCTSTR Enumerator */
NULL, /* IN HWND hwndParent */
);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
return VERR_GENERAL_FAILURE;
}
for (int i = 0; ; ++i)
{
NULL, /* IN PSP_DEVINFO_DATA DeviceInfoData */
&GUID_CLASS_VBOXUSB, /* IN LPGUID InterfaceClassGuid */
i,
&IfData))
{
if (winEr == ERROR_NO_MORE_ITEMS)
break;
continue;
}
/* we've now got the IfData */
if (!pVuDev)
{
continue;
}
if (!RT_SUCCESS(rc))
{
continue;
}
++*pcVuDevs;
}
return VINF_SUCCESS;
}
{
if (pDevice->pszManufacturer)
if (pDevice->pszProduct)
if (pDevice->pszSerialNumber)
}
{
while (pDevice)
{
}
}
static int usbLibDevPopulate(PUSBDEVICE pDev, PUSB_NODE_CONNECTION_INFORMATION_EX pConInfo, ULONG iPort, LPCSTR lpszDrvKeyName, LPCSTR lpszHubName, PVBOXUSB_STRING_DR_ENTRY pDrList)
{
/** @todo check which devices are used for primary input (keyboard & mouse) */
if (!lpszDrvKeyName || *lpszDrvKeyName == 0)
else
pDev->bNumConfigurations = 0;
pDev->u64SerialHash = 0;
{
if (pConInfo->DeviceDescriptor.iManufacturer && pDrList->iDr == pConInfo->DeviceDescriptor.iManufacturer)
{
}
else if (pConInfo->DeviceDescriptor.iProduct && pDrList->iDr == pConInfo->DeviceDescriptor.iProduct)
{
}
else if (pConInfo->DeviceDescriptor.iSerialNumber && pDrList->iDr == pConInfo->DeviceDescriptor.iSerialNumber)
{
}
if (lppszString)
{
char *pStringUTF8 = NULL;
{
}
}
}
return VINF_SUCCESS;
}
{
}
{
DWORD cbReturned = 0;
if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
{
return VERR_GENERAL_FAILURE;
}
{
AssertFailed();
return VERR_OUT_OF_RESOURCES;
}
PUSB_NODE_CONNECTION_DRIVERKEY_NAME pName = (PUSB_NODE_CONNECTION_DRIVERKEY_NAME)RTMemAllocZ(Name.ActualLength);
if (!pName)
{
AssertFailed();
return VERR_OUT_OF_RESOURCES;
}
int rc = VINF_SUCCESS;
if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
{
if (RT_SUCCESS(rc))
rc = VINF_SUCCESS;
}
else
{
}
return rc;
}
{
DWORD cbReturned = 0;
if (!DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, &Name, sizeof (Name), &Name, sizeof (Name), &cbReturned, NULL))
{
AssertFailed();
return VERR_GENERAL_FAILURE;
}
{
AssertFailed();
return VERR_OUT_OF_RESOURCES;
}
if (!pName)
return VERR_OUT_OF_RESOURCES;
int rc = VINF_SUCCESS;
if (DeviceIoControl(hHub, IOCTL_USB_GET_NODE_CONNECTION_NAME, pName, Name.ActualLength, pName, Name.ActualLength, &cbReturned, NULL))
{
if (RT_SUCCESS(rc))
rc = VINF_SUCCESS;
}
else
{
}
return rc;
}
{
DWORD cbReturned = 0;
if (!DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, &HubName, sizeof (HubName), &cbReturned, NULL))
{
return VERR_GENERAL_FAILURE;
}
if (!pHubName)
return VERR_OUT_OF_RESOURCES;
int rc = VINF_SUCCESS;
if (DeviceIoControl(hCtl, IOCTL_USB_GET_ROOT_HUB_NAME, NULL, 0, pHubName, HubName.ActualLength, &cbReturned, NULL))
{
if (RT_SUCCESS(rc))
rc = VINF_SUCCESS;
}
else
{
}
return rc;
}
static int usbLibDevCfgDrGet(HANDLE hHub, ULONG iPort, ULONG iDr, PUSB_CONFIGURATION_DESCRIPTOR *ppDr)
{
DWORD cbReturned = 0;
&cbReturned, NULL))
{
#ifdef DEBUG_misha
AssertFailed();
#endif
return VERR_GENERAL_FAILURE;
}
if (sizeof (Buf) != cbReturned)
{
AssertFailed();
return VERR_GENERAL_FAILURE;
}
{
AssertFailed();
return VERR_GENERAL_FAILURE;
}
if (!pRq)
return VERR_OUT_OF_RESOURCES;
int rc = VERR_GENERAL_FAILURE;
do
{
&cbReturned, NULL))
{
#ifdef DEBUG_misha
AssertFailed();
#endif
break;
}
if (cbRq != cbReturned)
{
AssertFailed();
break;
}
{
AssertFailed();
break;
}
return VINF_SUCCESS;
} while (0);
return rc;
}
{
}
static int usbLibDevStrDrEntryGet(HANDLE hHub, ULONG iPort, ULONG iDr, USHORT idLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
{
DWORD cbReturned = 0;
&cbReturned, NULL))
{
#ifdef DEBUG_misha
AssertFailed();
#endif
return VERR_GENERAL_FAILURE;
}
{
AssertFailed();
return VERR_GENERAL_FAILURE;
}
{
AssertFailed();
return VERR_GENERAL_FAILURE;
}
{
AssertFailed();
return VERR_GENERAL_FAILURE;
}
{
AssertFailed();
return VERR_GENERAL_FAILURE;
}
PVBOXUSB_STRING_DR_ENTRY pEntry = (PVBOXUSB_STRING_DR_ENTRY)RTMemAllocZ(sizeof (*pEntry) + pDr->bLength + 2);
if (!pEntry)
{
return VERR_OUT_OF_RESOURCES;
}
return VINF_SUCCESS;
}
{
}
{
while (pDr)
{
}
}
static int usbLibDevStrDrEntryGetForLangs(HANDLE hHub, ULONG iPort, ULONG iDr, ULONG cIdLang, const USHORT *pIdLang, PVBOXUSB_STRING_DR_ENTRY *ppList)
{
{
}
return VINF_SUCCESS;
}
static int usbLibDevStrDrEntryGetAll(HANDLE hHub, ULONG iPort, PUSB_DEVICE_DESCRIPTOR pDevDr, PUSB_CONFIGURATION_DESCRIPTOR pCfgDr, PVBOXUSB_STRING_DR_ENTRY *ppList)
{
if (RT_FAILURE(rc))
return rc;
ULONG cIdLang = (pLandStrDr->bLength - RT_OFFSETOF(USB_STRING_DESCRIPTOR, bString)) / sizeof (*pIdLang);
if (pDevDr->iManufacturer)
{
}
{
}
if (pDevDr->iSerialNumber)
{
}
{
{
AssertFailed();
break;
}
switch (pCmnDr->bDescriptorType)
{
{
{
AssertFailed();
break;
}
if (!pCurCfgDr->iConfiguration)
break;
rc = usbLibDevStrDrEntryGetForLangs(hHub, iPort, pCurCfgDr->iConfiguration, cIdLang, pIdLang, ppList);
break;
}
{
if (pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR) && pCmnDr->bLength != sizeof (USB_INTERFACE_DESCRIPTOR2))
{
AssertFailed();
break;
}
if (!pCurIfDr->iInterface)
break;
break;
}
default:
break;
}
}
return VINF_SUCCESS;
}
static int usbLibDevGetHubPortDevices(HANDLE hHub, LPCSTR lpcszHubName, ULONG iPort, PUSBDEVICE *ppDevs, uint32_t *pcDevs)
{
int rc = VINF_SUCCESS;
DWORD cbReturned = 0;
&cbReturned, NULL))
{
return VERR_GENERAL_FAILURE;
}
{
/* just ignore & return success */
return VWRN_INVALID_HANDLE;
}
if (pConInfo->DeviceIsHub)
{
if (RT_SUCCESS(rc))
{
return rc;
}
/* ignore this err */
return VINF_SUCCESS;
}
if (RT_FAILURE(rc))
{
return rc;
}
if (pCfgDr)
{
}
if (RT_SUCCESS(rc))
{
++*pcDevs;
}
if (pCfgDr)
if (lpszName)
if (pList)
return VINF_SUCCESS;
}
{
if (!lpszDevName)
{
AssertFailed();
return VERR_OUT_OF_RESOURCES;
}
int rc = VINF_SUCCESS;
do
{
DWORD cbReturned = 0;
HANDLE hDev = CreateFile(lpszDevName, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (hDev == INVALID_HANDLE_VALUE)
{
AssertFailed();
break;
}
&cbReturned, NULL))
{
AssertFailed();
break;
}
{
}
} while (0);
return rc;
}
{
char CtlName[16];
int rc = VINF_SUCCESS;
for (int i = 0; i < 10; ++i)
{
if (hCtl != INVALID_HANDLE_VALUE)
{
char* lpszName;
if (RT_SUCCESS(rc))
{
}
if (RT_FAILURE(rc))
break;
}
}
return VINF_SUCCESS;
}
{
if (!pRq)
return NULL;
return pRq;
}
{
int iDiff;
return iDiff;
}
static int usbLibMonDevicesUpdate(PVBOXUSBGLOBALSTATE pGlobal, PUSBDEVICE pDevs, uint32_t cDevs, PVBOXUSB_DEV pDevInfos, uint32_t cDevInfos)
{
{
{
continue;
if (!pDevInfos->szDriverRegName[0])
{
AssertFailed();
break;
}
USBSUP_GETDEV Dev = {0};
HANDLE hDev = CreateFile(pDevInfos->szName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL,
if (hDev == INVALID_HANDLE_VALUE)
{
AssertFailed();
break;
}
DWORD cbReturned = 0;
if (!DeviceIoControl(hDev, SUPUSB_IOCTL_GET_DEVICE, &Dev, sizeof (Dev), &Dev, sizeof (Dev), &cbReturned, NULL))
{
/* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
#ifdef DEBUG_misha
AssertMsg(winEr == ERROR_DEVICE_NOT_CONNECTED, (__FUNCTION__": DeviceIoControl failed winEr (%d)\n", winEr));
#endif
Log(("SUPUSB_IOCTL_GET_DEVICE: DeviceIoControl no longer connected\n"));
break;
}
/* we must not close the handle until we request for the device state from the monitor to ensure
* the device handle returned by the device driver does not disappear */
if (!DeviceIoControl(pGlobal->hMonitor, SUPUSBFLT_IOCTL_GET_DEVICE, &hDevice, sizeof (hDevice), &MonInfo, sizeof (MonInfo), &cbReturned, NULL))
{
/* ERROR_DEVICE_NOT_CONNECTED -> device was removed just now */
Log(("SUPUSBFLT_IOCTL_GET_DEVICE: DeviceIoControl no longer connected\n"));
break;
}
/* success!! update device info */
/* ensure the state returned is valid */
/* The following is not 100% accurate but we only care about high-speed vs. non-high-speed */
{
/* only set the interface name if device can be grabbed */
}
#ifdef DEBUG_misha
else
{
/* dbg breakpoint */
Assert(0);
}
#endif
/* we've found the device, break in any way */
break;
}
}
return VINF_SUCCESS;
}
{
*pcDevs = 0;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
}
return VINF_SUCCESS;
}
return rc;
}
{
FALSE, /* BOOL bWaitAll */
cMillies);
switch (dwResult)
{
case WAIT_OBJECT_0:
return VINF_SUCCESS;
case WAIT_OBJECT_0 + 1:
return VERR_INTERRUPTED;
case WAIT_TIMEOUT:
return VERR_TIMEOUT;
default:
{
return VERR_GENERAL_FAILURE;
}
}
}
{
}
{
if (!bRc)
{
return VERR_GENERAL_FAILURE;
}
return VINF_SUCCESS;
}
{
return usbLibInterruptWaitChange(&g_VBoxUsbGlobal);
}
/*
USBLIB_DECL(bool) USBLibHasPendingDeviceChanges(void)
{
int rc = USBLibWaitChange(0);
return rc == VINF_SUCCESS;
}
*/
{
}
{
DWORD cbReturned = 0;
{
#ifdef DEBUG_misha
AssertFailed();
#endif
return NULL;
}
Log(("usblibInsertFilter: Manufacturer=%s Product=%s Serial=%s\n",
USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
&cbReturned, NULL))
{
return NULL;
}
{
return NULL;
}
}
{
DWORD cbReturned = 0;
{
#ifdef DEBUG_misha
AssertFailed();
#endif
return;
}
if (!DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_REMOVE_FILTER, &uId, sizeof(uId), NULL, 0,&cbReturned, NULL))
}
USBLIB_DECL(int) USBLibRunFilters()
{
DWORD cbReturned = 0;
NULL, 0,
NULL, 0,
&cbReturned, NULL))
{
return RTErrConvertFromWin32(winEr);
}
return VINF_SUCCESS;
}
)
{
}
static void usbLibOnDeviceChange()
{
/* we're getting series of events like that especially on device re-attach
* (i.e. first for device detach and then for device attach)
* unfortunately the event does not tell us what actually happened.
* To avoid extra notifications, we delay the SetEvent via a timer
* and update the timer if additional notification comes before the timer fires
* */
if (g_VBoxUsbGlobal.hTimer)
{
{
}
}
NULL,
500, /* ms*/
0,
{
/* call it directly */
}
}
)
{
switch (uMsg)
{
case WM_DEVICECHANGE:
if (wParam == DBT_DEVNODES_CHANGED)
{
* and let the client decide whether the usb change actually happened
* so far this is more clean than reporting events from the Monitor
* and by the time PDO is created, device can not
* be yet started and fully functional,
* so usblib won't be able to pick it up
* */
}
break;
case WM_DESTROY:
return 0;
}
}
{
bool bExit = false;
/* Register the Window Class. */
wc.cbClsExtra = 0;
wc.cbWndExtra = sizeof(void *);
if (atomWindowClass != 0)
{
/* Create the window. */
if (g_VBoxUsbGlobal.hWnd)
{
{
}
bExit = true;
}
}
if(bExit)
{
/* no need any accuracy here, in anyway the DHCP server usually gets terminated with TerminateProcess */
exit(0);
}
return 0;
}
#endif
/**
* Initialize the USB library
*
* @returns VBox status code.
*/
USBLIB_DECL(int) USBLibInit(void)
{
int rc = VERR_GENERAL_FAILURE;
Log(("usbproxy: usbLibInit\n"));
FALSE, /* BOOL bManualReset */
#ifndef VBOX_USB_USE_DEVICE_NOTIFICATION
TRUE, /* BOOL bInitialState */
#else
FALSE, /* set to false since it will be initially used for notification thread startup sync */
#endif
NULL /* LPCTSTR lpName */);
{
FALSE, /* BOOL bManualReset */
FALSE, /* BOOL bInitialState */
NULL /* LPCTSTR lpName */);
{
g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
{
{
g_VBoxUsbGlobal.hMonitor = CreateFile(USBMON_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
{
}
}
}
{
USBSUP_VERSION Version = {0};
DWORD cbReturned = 0;
if (DeviceIoControl(g_VBoxUsbGlobal.hMonitor, SUPUSBFLT_IOCTL_GET_VERSION, NULL, 0, &Version, sizeof (Version), &cbReturned, NULL))
{
{
#ifndef VBOX_USB_USE_DEVICE_NOTIFICATION
USBSUP_SET_NOTIFY_EVENT SetEvent = {0};
&cbReturned, NULL))
{
if (RT_SUCCESS(rc))
return VINF_SUCCESS;
else
}
else
{
}
#else
{
NULL, /*__in_opt LPSECURITY_ATTRIBUTES lpThreadAttributes, */
0, /*__in SIZE_T dwStackSize, */
usbLibMsgThreadProc, /*__in LPTHREAD_START_ROUTINE lpStartAddress,*/
NULL, /*__in_opt LPVOID lpParameter,*/
0, /*__in DWORD dwCreationFlags,*/
NULL /*__out_opt LPDWORD lpThreadId*/
);
{
if (g_VBoxUsbGlobal.hWnd)
{
/* ensure the event is set so the first "wait change" request processes */
return VINF_SUCCESS;
}
if (!bRc)
{
}
}
else
{
}
}
else
{
}
#endif
}
else
{
}
}
else
{
}
}
else
{
#ifdef DEBUG_misha
AssertFailed();
#endif
}
}
else
{
}
}
else
{
}
/* since main calls us even if USBLibInit fails,
* we use hMonitor == INVALID_HANDLE_VALUE as a marker to indicate whether the lib is inited */
return rc;
}
/**
* Terminate the USB library
*
* @returns VBox status code.
*/
USBLIB_DECL(int) USBLibTerm(void)
{
{
#ifdef DEBUG_misha
AssertFailed();
#endif
return VINF_ALREADY_INITIALIZED;
}
if (!bRc)
{
}
if (!bRc)
{
}
if (g_VBoxUsbGlobal.hTimer)
{
INVALID_HANDLE_VALUE /* <-- to block until the timer is completed */
);
if (!bRc)
{
}
}
INVALID_HANDLE_VALUE /* <-- to block until all timers are completed */
);
if (!bRc)
{
}
#endif
if (!bRc)
{
}
if (!bRc)
{
}
if (!bRc)
{
}
return VINF_SUCCESS;
}