[vbox-dev] code patch to auto detect USB insertion
Huihong Luo
huisinro at yahoo.com
Thu Sep 15 09:01:03 PDT 2011
since there are other changes, so I post here manual changes, instead of a pacth file.
The patch is attached as a text file, also shown as below:
UIMachineWindow.h:
public:
/* usb support, monitor usb device insertion */
#ifdef Q_WS_WIN32
QStringList m_hostUSBDevices;
void updateHostUSBDeviceList();
QString getDriverKey(QString devId);
QString getDeviceIdFromDriverKey(QString driverKey);
#endif
protected:
/* vmlite, usb support, monitor usb device insertion */
#ifdef Q_WS_WIN32
void registerUSBDeviceNotification();
void onDeviceChange(MSG *aMsg);
#endif
UIMachineWindow.cpp:
#ifdef Q_WS_WIN32
#include <Dbt.h>
#include <setupapi.h>
#endif
/* vmlite, usb support, monitor usb device insertion */
#ifdef Q_WS_WIN32
void UIMachineWindow::registerUSBDeviceNotification()
{
//return; // this disables USB insertion detection
const GUID GUID_DEVINTERFACE_USB_DEVICE =
{ 0xA5DCBF10, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
const GUID GUID_DEVINTERFACE_HID =
{ 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
updateHostUSBDeviceList();
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter;
ZeroMemory( &NotificationFilter, sizeof(NotificationFilter) );
NotificationFilter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE);
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_USB_DEVICE;
HDEVNOTIFY hDevNotify = RegisterDeviceNotification(m_pMachineWindow->winId(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
NotificationFilter.dbcc_classguid = GUID_DEVINTERFACE_HID;
hDevNotify = RegisterDeviceNotification(m_pMachineWindow->winId(), &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE);
}
// sync the following with our usb drivers
//#define VMLITE_USB_DEVICE_ID "USB\\VID_80EE&PID_CAFE"
#define VMLITE_USB_DEVICE_ID "USB\\VMLITEUSBSTUB"
typedef struct _DEVICE_CHANGE_PARAMETER
{
UIMachineWindow* pThis;
DWORD dbch_devicetype;
WPARAM kind;
WCHAR dbcc_name[MAX_PATH];
} DEVICE_CHANGE_PARAMETER, *PDEVICE_CHANGE_PARAMETER;
static DWORD WINAPI deviceChangeThreadProc(LPVOID Parameter)
{
PDEVICE_CHANGE_PARAMETER pDeviceChange = (PDEVICE_CHANGE_PARAMETER)Parameter;
UIMachineWindow* pThis = pDeviceChange->pThis;
WPARAM kind = pDeviceChange->kind;
/* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
szDevId: USB#VID_04e8&PID_503b#0002F9A9828E0F06
szClass: USB
*/
// extract the devId portion, covert '#' to '\'
QString devIdStr((QChar *)pDeviceChange->dbcc_name + 4, wcslen(pDeviceChange->dbcc_name + 4));
devIdStr = devIdStr.left(devIdStr.lastIndexOf('#'));
devIdStr = devIdStr.replace('#', '\\');
free(pDeviceChange);
// address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025
QString address = pThis->getDriverKey(devIdStr);
// only hanlde devices that are really inserted,
// when an attached device is released, arrival event will come here too, ignore such insertion
if (pThis->m_hostUSBDevices.contains(address))
{
return -1;
}
/* Get HOST: */
CHost host = vboxGlobal().virtualBox().GetHost();
/* find the host usb device, and attach it, try 180 secs */
for (int i=0; i<2 * 180; i++)
{
// this is extremely import, freezes otherwise
QCoreApplication::processEvents();
/* Get USB devices list: */
CHostUSBDeviceVector devices = host.GetUSBDevices();
foreach (const CHostUSBDevice hostDevice, devices)
{
if (hostDevice.GetAddress() == address)
{
/* Get current console: */
CConsole console = pThis->session().GetConsole();
CUSBDevice attachedDevice = console.FindUSBDeviceById(hostDevice.GetId());
if (attachedDevice.isNull())
{
console.AttachUSBDevice(hostDevice.GetId());
return console.isOk() ? 0 : -1;
}
else
{
return -1;
}
}
}
Sleep(500);
}
return -1;
}
/* get driver key for the specified usb device id, from registry, so works even if device is off or deteched
use registry to read the driver key, we don't use Setup api because the device is not active
e.g., devId: USB\VID_046D&PID_C016\5&9e58382&0&1
*/
static QString getDriverKeyThroughRegistry(const QString& devId)
{
WCHAR keyName[MAX_PATH];
//e.g., HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_046D&PID_C016\5&9e58382&0&1
swprintf(keyName, L"SYSTEM\\CurrentControlSet\\Enum\\%ls", (WCHAR *)devId.unicode());
HKEY hkey;
long lret = RegOpenKeyW(HKEY_LOCAL_MACHINE, keyName, &hkey);
if (lret != ERROR_SUCCESS)
return "";
WCHAR driver[MAX_PATH];
DWORD type;
DWORD size = sizeof(driver);
lret = RegQueryValueExW(hkey, L"Driver", 0, &type, (PBYTE)driver, &size);
RegCloseKey(hkey);
if (lret == ERROR_SUCCESS)
{
return QString((QChar *)driver, wcslen(driver));
}
else
{
return "";
}
}
void UIMachineWindow::onDeviceChange(MSG *aMsg)
{
if (aMsg->wParam != DBT_DEVICEREMOVECOMPLETE && aMsg->wParam != DBT_DEVICEARRIVAL)
return;
PDEV_BROADCAST_HDR pHdr = (PDEV_BROADCAST_HDR)aMsg->lParam;
if ( pHdr->dbch_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
{
return;
}
if ( DBT_DEVICEREMOVECOMPLETE == aMsg->wParam)
{
updateHostUSBDeviceList();
// need to remove the device that is just removed, in case our updated list still has it in the list
PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
/* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
*/
// extract the devId portion, covert '#' to '\'
QString devIdStr((QChar *)pDevInf->dbcc_name + 4, wcslen(pDevInf->dbcc_name + 4));
devIdStr = devIdStr.left(devIdStr.lastIndexOf('#'));
devIdStr = devIdStr.replace('#', '\\');
// address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025
QString address = getDriverKeyThroughRegistry(devIdStr);
if (!address.isEmpty() && m_hostUSBDevices.contains(address))
{
m_hostUSBDevices.removeAll(address);
}
}
else if ( DBT_DEVICEARRIVAL == aMsg->wParam)
{
PDEV_BROADCAST_DEVICEINTERFACE pDevInf = (PDEV_BROADCAST_DEVICEINTERFACE)pHdr;
if (!m_pMachineWindow->isActiveWindow())
{
updateHostUSBDeviceList();
return;
}
/* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
szDevId: USB#VID_04e8&PID_503b#0002F9A9828E0F06
szClass: USB
*/
// extract the devId portion, covert '#' to '\'
QString devIdStr((QChar *)pDevInf->dbcc_name + 4, wcslen(pDevInf->dbcc_name + 4));
devIdStr = devIdStr.left(devIdStr.lastIndexOf('#'));
devIdStr = devIdStr.replace('#', '\\');
// when an usb device is attached, our vmlite virtualizazed usb will come here, ignore them
if (devIdStr.startsWith(VMLITE_USB_DEVICE_ID, Qt::CaseInsensitive))
{
updateHostUSBDeviceList();
return;
}
DWORD ThreadId;
PDEVICE_CHANGE_PARAMETER param = (PDEVICE_CHANGE_PARAMETER)malloc(sizeof(DEVICE_CHANGE_PARAMETER));
if (param)
{
param->pThis = this;
param->dbch_devicetype = pHdr->dbch_devicetype;
param->kind = aMsg->wParam;
wcscpy(param->dbcc_name, pDevInf->dbcc_name);
HANDLE ThreadHandle = CreateThread(
NULL,
0,
deviceChangeThreadProc,
param,
0,
&ThreadId
);
}
}
}
/* get driver key for the original host usb device being attached from the attached device
*/
static QString getDriverKeyForOriginalDevice(const CHostUSBDevice& attachedDevice)
{
// the attached device and the host device have same vendor id, product id, etc.
unsigned short vid = attachedDevice.GetVendorId();
unsigned short pid = attachedDevice.GetProductId();
// for non attached usb device, address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025
// attached host device has address of device name
QString address = attachedDevice.GetAddress();
if (!address.startsWith("\\\\?\\"))
{
return address;
}
/* pDevInf->dbcc_name, e.g. \\?\USB#VID_04e8&PID_503b#0002F9A9828E0F06#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
szDevId: USB#VID_04e8&PID_503b#0002F9A9828E0F06
*/
address = address.right(address.length() - 4);
int pos = address.lastIndexOf('#');
address = address.left(pos);
// the string after 2nd # is the serial #, same as the original device being attached
pos = address.indexOf("#");
address = address.right(address.length() - pos - 1);
pos = address.indexOf("#");
address = address.right(address.length() - pos - 1);
WCHAR *serial = (WCHAR *)address.unicode();
// use registry to read the driver key, we don't use Setup api because the device is not active
// e.g., HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USB\VID_046D&PID_C016\5&9e58382&0&1
WCHAR devId[MAX_PATH];
swprintf(devId, L"SYSTEM\\CurrentControlSet\\Enum\\USB\\VID_%04x&PID_%04x\\%ls", vid, pid, serial);
HKEY hkey;
long lret = RegOpenKeyW(HKEY_LOCAL_MACHINE, devId, &hkey);
if (lret != ERROR_SUCCESS)
return "";
WCHAR driver[MAX_PATH];
DWORD type;
DWORD size = sizeof(driver);
lret = RegQueryValueExW(hkey, L"Driver", 0, &type, (PBYTE)driver, &size);
RegCloseKey(hkey);
if (lret == ERROR_SUCCESS)
{
return QString((QChar *)driver, wcslen(driver));
}
else
{
return "";
}
}
void UIMachineWindow::updateHostUSBDeviceList()
{
m_hostUSBDevices.clear();
/* Get HOST: */
CHost host = vboxGlobal().virtualBox().GetHost();
/* Get USB devices list: */
CHostUSBDeviceVector devices = host.GetUSBDevices();
foreach (const CHostUSBDevice hostDevice, devices)
{
// address is the driver key, e.g., {36FC9E60-C465-11CF-8056-444553540000}\0025
QString address = hostDevice.GetAddress();
if (address.startsWith("\\\\?\\")) /* attached host device has address of device name */
{
address = getDriverKeyForOriginalDevice(hostDevice);
}
m_hostUSBDevices << address;
}
}
/*
Get driver key from a usb id, devId: USB\Vid_04e8&Pid_503b\0002F9A9828E0F06
we use driver key to identify a host usb device, as this is unique,
vid/pid/serial is not unique because serial might be emppty
*/
QString UIMachineWindow::getDriverKey(QString devId)
{
HDEVINFO hDevInfo = SetupDiGetClassDevsW(NULL, L"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
if( INVALID_HANDLE_VALUE == hDevInfo )
{
return "";
}
WCHAR driverKey[MAX_PATH] = L"";
SP_DEVINFO_DATA spDevInfoData;
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for(int i=0; SetupDiEnumDeviceInfo(hDevInfo, i, &spDevInfoData); i++)
{
DWORD nSize = 0;
WCHAR buf[MAX_PATH];
if ( !SetupDiGetDeviceInstanceIdW(hDevInfo, &spDevInfoData, buf, sizeof(buf), &nSize) )
return "";
if (0 == QString((QChar *)buf, wcslen(buf)).compare(devId, Qt::CaseInsensitive))
{
nSize = 0;
if ( SetupDiGetDeviceRegistryPropertyW(hDevInfo, &spDevInfoData,
SPDRP_DRIVER, NULL, (PBYTE)buf, sizeof(buf), &nSize) )
{
wcscpy(driverKey, buf);
}
break;
}
}
SetupDiDestroyDeviceInfoList(hDevInfo);
if (driverKey[0])
return QString((QChar *)driverKey, wcslen(driverKey));
else
return "";
}
/*
Get usb id from driver key, devId: USB\Vid_04e8&Pid_503b\0002F9A9828E0F06, driver key: {36FC9E60-C465-11CF-8056-444553540000}\0025
*/
QString UIMachineWindow::getDeviceIdFromDriverKey(QString driverKey)
{
HDEVINFO hDevInfo = SetupDiGetClassDevsW(NULL, L"USB", NULL, DIGCF_ALLCLASSES | DIGCF_PRESENT);
if( INVALID_HANDLE_VALUE == hDevInfo )
{
return "";
}
WCHAR deviceId[MAX_PATH] = L"";
SP_DEVINFO_DATA spDevInfoData;
spDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for(int i=0; SetupDiEnumDeviceInfo(hDevInfo, i, &spDevInfoData); i++)
{
DWORD nSize = 0;
WCHAR buf[MAX_PATH];
if ( !SetupDiGetDeviceInstanceIdW(hDevInfo, &spDevInfoData, buf, sizeof(buf), &nSize) )
return "";
wcscpy(deviceId, buf);
nSize = 0;
if ( SetupDiGetDeviceRegistryPropertyW(hDevInfo, &spDevInfoData,
SPDRP_DRIVER, NULL, (PBYTE)buf, sizeof(buf), &nSize) )
{
if (0 == QString((QChar *)buf, wcslen(buf)).compare(driverKey, Qt::CaseInsensitive))
{
break;
}
}
deviceId[0] = 0;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
if (deviceId[0])
return QString((QChar *)deviceId, wcslen(deviceId));
else
return "";
}
#endif /* Q_WS_WIN32 */
UIMachineWindowNormal.cpp:
UIMachineWindowNormal::UIMachineWindowNormal(UIMachineLogic *pMachineLogic, ulong uScreenId)
: QIWithRetranslateUI2<QMainWindow>(0, Qt::Window)
, UIMachineWindow(pMachineLogic, uScreenId)
, m_pIndicatorsPool(new UIIndicatorsPool(pMachineLogic->uisession()->session(), this))
, m_pIdleTimer(0)
{
........
// add as last line
/* vmlite, usb and dnd support, monitor usb device insertion */
#ifdef Q_WS_WIN32
registerUSBDeviceNotification();
#endif
}
UIMachineWindowFullscreen.cpp:
UIMachineWindowFullscreen::UIMachineWindowFullscreen(UIMachineLogic *pMachineLogic, ulong uScreenId)
: QIWithRetranslateUI2<QMainWindow>(0, Qt::FramelessWindowHint)
, UIMachineWindow(pMachineLogic, uScreenId)
, m_pMainMenu(0)
, m_pMiniToolBar(0)
{
........
// add as last line
/* vmlite, usb and dnd support, monitor usb device insertion */
#ifdef Q_WS_WIN32
registerUSBDeviceNotification();
#endif
}
--- On Thu, 9/15/11, Michael Thayer <michael.thayer at oracle.com> wrote:
From: Michael Thayer <michael.thayer at oracle.com>
Subject: Re: [vbox-dev] code patch to auto detect USB insertion
To: "Huihong Luo" <huisinro at yahoo.com>
Cc: vbox-dev at virtualbox.org
Date: Thursday, September 15, 2011, 2:31 AM
Hello Huihong,
On 09/13/2011 11:49 PM, Huihong Luo wrote:
> I have some code (around 600 lines) that auto attaches usb device when
> inserted to the windows host while vm window is on focus, similar to
> vmware's handling.
> not sure if vbox team wants to integrate this patch.
We might do if it is something which can be enabled or disabled by the user, but we would have to see the patch first (the usual requirements apply [1]). Would you like to include it?
Regards and thanks,
Michael
[1] http://www.virtualbox.org/wiki/Contributor_information
-- ORACLE Deutschland B.V. & Co. KG Michael Thayer
Werkstrasse 24 VirtualBox engineering
71384 Weinstadt, Germany mailto:michael.thayer at oracle.com
Hauptverwaltung: Riesstr. 25, D-80992 München
Registergericht: Amtsgericht München, HRA 95603
Komplementärin: ORACLE Deutschland Verwaltung B.V.
Hertogswetering 163/167, 3543 AS Utrecht, Niederlande
Handelsregister der Handelskammer Midden-Niederlande, Nr. 30143697
Geschäftsführer: Jürgen Kunz, Marcel van de Molen, Alexander van der Ven
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://www.virtualbox.org/pipermail/vbox-dev/attachments/20110915/6e10b659/attachment-0001.html
-------------- next part --------------
An embedded and charset-unspecified text was scrubbed...
Name: usb-insertion-detection-patch.txt
Url: http://www.virtualbox.org/pipermail/vbox-dev/attachments/20110915/6e10b659/attachment-0001.txt
More information about the vbox-dev
mailing list