USBMonFlt.cpp revision c55c68b6a3324172e9dc207926215845880b0f90
/** @file
* VBox host drivers - USB drivers - Win32 USB monitor driver
*/
/*
* 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 *
*******************************************************************************/
#include "USBMon.h"
#include <stdio.h>
#include "usbdi.h"
#pragma warning(default : 4200)
#include "usbdlib.h"
#include "VBoxUSBFilterMgr.h"
#include <devguid.h>
/*
* Note: Must match the VID & PID in the USB driver .inf file!!
*/
/*
BusQueryDeviceID USB\Vid_80EE&Pid_CAFE
BusQueryInstanceID 2
BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE&Rev_0100
BusQueryHardwareIDs USB\Vid_80EE&Pid_CAFE
BusQueryCompatibleIDs USB\Class_ff&SubClass_00&Prot_00
BusQueryCompatibleIDs USB\Class_ff&SubClass_00
BusQueryCompatibleIDs USB\Class_ff
*/
#define szBusQueryDeviceId L"USB\\Vid_80EE&Pid_CAFE"
#define szBusQueryHardwareIDs L"USB\\Vid_80EE&Pid_CAFE&Rev_0100\0USB\\Vid_80EE&Pid_CAFE\0\0"
#define szBusQueryCompatibleIDs L"USB\\Class_ff&SubClass_00&Prot_00\0USB\\Class_ff&SubClass_00\0USB\\Class_ff\0\0"
#define szDeviceTextDescription L"VirtualBox USB"
/* Possible USB bus driver names. */
{
L"\\Driver\\usbhub",
};
#define MAX_ATTACHED_USB_DEVICES 64
typedef struct
{
char szSerial[MAX_USB_SERIAL_STRING];
char szMfgName[MAX_USB_SERIAL_STRING];
char szProduct[MAX_USB_SERIAL_STRING];
/* Device driver instance data */
typedef struct
{
/* Set to force grabbing of newly arrived devices */
bool fForceGrab;
/* Set to disable all filters */
bool fDisableFilters;
} DRVINSTANCE, *PDRVINSTANCE;
DRVINSTANCE DrvInstance = {0};
/* Forward declarations. */
NTSTATUS VBoxUSBGetDeviceDescription(PDEVICE_OBJECT pDevObj, USHORT *pusVendorId, USHORT *pusProductId, USHORT *pusRevision);
#define ACQUIRE_LOCK() \
#define RELEASE_LOCK() \
{
return STATUS_SUCCESS;
}
{
return STATUS_SUCCESS;
}
/**
* Device I/O Control entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
{
{
{
AssertMsgFailed(("SUPUSBFLT_IOCTL_USB_CHANGE: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.\n",
break;
}
//// DebugPrint(("SUPUSBFLT_IOCTL_USB_CHANGE -> %d\n", DrvInstance.cUSBStateChange));
break;
}
{
{
AssertMsgFailed(("SUPUSBFLT_IOCTL_GET_NUM_DEVICES: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.\n",
break;
}
break;
}
{
DebugPrint(("SUPUSBFLT_IOCTL_CAPTURE_DEVICE\n"));
{
AssertMsgFailed(("SUPUSBFLT_IOCTL_CAPTURE_DEVICE: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.\n",
break;
}
break;
}
{
DebugPrint(("SUPUSBFLT_IOCTL_RELEASE_DEVICE\n"));
{
AssertMsgFailed(("SUPUSBFLT_IOCTL_RELEASE_DEVICE: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.\n",
break;
}
break;
}
{
DebugPrint(("SUPUSBFLT_IOCTL_GET_VERSION\n"));
{
AssertMsgFailed(("SUPUSBFLT_IOCTL_GET_VERSION: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.\n",
break;
}
break;
}
{
/* Validate input. */
if (RT_UNLIKELY(!ioBuffer || inputBufferLength != sizeof(*pFilter) || outputBufferLength != sizeof(*pOut)))
{
AssertMsgFailed(("SUPUSBFLT_IOCTL_ADD_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.\n",
break;
}
/* Log the filter details. */
DebugPrint(("SUPUSBFLT_IOCTL_ADD_FILTER %s %s %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>"));
#ifdef DEBUG
DebugPrint(("VBoxUSBClient::addFilter: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n",
#endif
/* Add the filter. */
break;
}
{
{
AssertMsgFailed(("SUPUSBFLT_IOCTL_REMOVE_FILTER: Invalid input/output sizes. cbIn=%d expected %d. cbOut=%d expected %d.\n",
break;
}
break;
}
default:
break;
}
return status;
}
{
return STATUS_SUCCESS;
}
{
ACQUIRE_LOCK();
if (DrvInstance.CaptureCount == 0)
{
DrvInstance.cUSBDevices = 0;
}
RELEASE_LOCK();
return STATUS_SUCCESS;
}
unsigned myxdigit(char c)
{
if (c >= 'a' && c <= 'z')
c = c - 'a' + 'A';
if (c >= '0' && c <= '9')
return c - '0';
if (c >= 'A' && c <= 'F')
return c - 'A' + 10;
return 0;
}
#if 1
{
int index;
if (DrvInstance.fForceGrab)
{
DebugPrint(("VBoxMatchFilter -> Force Grab -> TRUE\n"));
return true;
}
{
DebugPrint(("VBoxMatchFilter -> filters disabled -> FALSE\n"));
return false;
}
if (index == -1)
{
DebugPrint(("VBoxMatchFilter -> unknown PDO -> FALSE\n"));
return false;
}
ACQUIRE_LOCK();
/* Run filters on the thing. */
if (Owner == NIL_RTPROCESS)
{
RELEASE_LOCK();
return false;
}
DebugPrint(("VBoxMatchFilter: HIT\n"));
RELEASE_LOCK();
return true;
}
#else
{
//#define szBusQueryDeviceId L"USB\\Vid_80EE&Pid_CAFE"
// char szRevision[4];
#ifdef DEBUG
#endif
if (DrvInstance.fForceGrab)
{
DebugPrint(("VBoxMatchFilter -> Force Grab -> TRUE\n"));
return true;
}
{
DebugPrint(("VBoxMatchFilter -> filters disabled -> FALSE\n"));
return false;
}
return false;
pszDeviceId++;
if(*pszDeviceId == 0)
return false;
pszDeviceId++;
/* Vid_ skipped */
pszDeviceId++;
if(*pszDeviceId == 0)
return false;
pszDeviceId++;
/* Pid_ skipped */
ACQUIRE_LOCK();
/* Run filters on the thing. */
if (Owner == NIL_RTPROCESS)
{
RELEASE_LOCK();
return false;
}
DebugPrint(("VBoxMatchFilter: HIT\n"));
RELEASE_LOCK();
return true;
}
#endif
{
return true;
for (int i=0;i<MAX_ATTACHED_USB_DEVICES;i++)
{
{
DebugPrint(("Signal USB change ADD\n"));
RELEASE_LOCK();
/* There doesn't appear to be any good way to get device information
* from Windows. Reading its descriptor should do the trick though.
*/
return true;
}
}
RELEASE_LOCK();
return false;
}
{
ACQUIRE_LOCK();
for (int i=0;i<MAX_ATTACHED_USB_DEVICES;i++)
{
{
RELEASE_LOCK();
return i;
}
}
RELEASE_LOCK();
return -1;
}
{
ACQUIRE_LOCK();
for (int i=0;i<MAX_ATTACHED_USB_DEVICES;i++)
{
{
{
}
DebugPrint(("Signal USB change REMOVE\n"));
RELEASE_LOCK();
return true;
}
}
RELEASE_LOCK();
return false;
}
{
if (DrvInstance.CaptureCount == 0)
return false;
ACQUIRE_LOCK();
for (int i=0;i<MAX_ATTACHED_USB_DEVICES;i++)
{
{
RELEASE_LOCK();
return true;
}
}
RELEASE_LOCK();
return false;
}
{
/* If we manually release a device, then all filters will be temporarily disabled; Enable them again when the
* device has been started.
*/
DrvInstance.fDisableFilters = false;
/* If we manually capture a device, we are forced to grab the next device that arrives. Disable this mode here */
DrvInstance.fForceGrab = false;
return;
}
{
bool ret;
ACQUIRE_LOCK();
for (int i=0;i<MAX_ATTACHED_USB_DEVICES;i++)
{
{
RELEASE_LOCK();
return ret;
}
}
RELEASE_LOCK();
return false;
}
/**
* Send USB ioctl
*
* @returns NT Status
* @param pDevObj USB device pointer
* @param control_code ioctl
* @param buffer Descriptor buffer
* @param size size of buffer
*/
{
pIrp = IoBuildDeviceIoControlRequest(control_code, pDevObj, NULL, 0, NULL, 0, TRUE, &event, &io_status);
if (!pIrp)
{
AssertMsgFailed(("IoBuildDeviceIoControlRequest failed!!\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
/* Get the next stack location as that is used for the new irp */
if (status == STATUS_PENDING)
{
DebugPrint(("IoCallDriver returned STATUS_PENDING!!\n"));
}
return status;
}
/**
* Get USB descriptor
*
* @returns NT Status
* @param pDevObj USB device pointer
* @param buffer Descriptor buffer
* @param size size of buffer
* @param type descriptor type
* @param index descriptor index
* @param language_id descriptor language id
*/
NTSTATUS VBoxUSBGetDescriptor(PDEVICE_OBJECT pDevObj, void *buffer, int size, int type, int index, int language_id)
{
{
DebugPrint(("Failed to alloc mem for urb\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
#ifdef DEBUG
DebugPrint(("VBoxUSBGetDescriptor: VBoxUSBSendIOCTL failed with %x (%x)\n", rc, urb->UrbHeader.Status));
#endif
return rc;
}
/**
* Get a valid USB string descriptor language ID (the first ID found).
*
* @returns NT Status
* @param pDevObj USB device pointer
* @param lang_id pointer to language id
*/
{
unsigned length;
char buffer[MAXIMUM_USB_STRING_LENGTH];
*lang_id = 0;
if (!NT_SUCCESS(status))
{
DebugPrint(("VBoxUSBGetLangID: language ID table not present (?)\n"));
goto fail;
}
/* Just grab the first lang ID if available. In 99% cases, it will be US English (0x0409).*/
{
}
else
fail:
return status;
}
NTSTATUS VBoxUSBGetStringDescriptor(PDEVICE_OBJECT pDevObj, char *dest, unsigned size, int index, int lang_id)
{
unsigned length;
*dest = '\0';
if (index)
{
/* An entire USB string descriptor is Unicode and can't be longer than 256 bytes.
* Hence 128 bytes is enough for an ASCII string.
*/
if (!pstrdescr)
{
AssertMsgFailed(("VBoxUSBGetStringDescriptor: ExAllocatePool failed\n"));
goto fail;
}
status = VBoxUSBGetDescriptor(pDevObj, pstrdescr, length, USB_STRING_DESCRIPTOR_TYPE, index, lang_id);
if (!NT_SUCCESS(status))
{
DebugPrint(("VBoxUSBGetStringDescriptor: requested string not present (?)\n"));
goto fail;
}
{
}
}
fail:
if (pstrdescr)
return status;
}
{
{
DebugPrint(("Failed to alloc mem for urb\n"));
goto fail;
}
status = VBoxUSBGetDescriptor(pDevObj, devdescr, sizeof(*devdescr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0);
if (!NT_SUCCESS(status))
{
AssertMsgFailed(("VBoxUSBGetDeviceDescription: getting device descriptor failed\n"));
goto fail;
}
DebugPrint(("Device pid=%x vid=%x rev=%x\n", devdescr->idVendor, devdescr->idProduct, devdescr->bcdDevice));
/* If there are no strings, don't even try to get any string descriptors. */
{
int langId;
if (!NT_SUCCESS(status))
{
AssertMsgFailed(("VBoxUSBGetDeviceDescription: reading language ID failed\n"));
goto fail;
}
status = VBoxUSBGetStringDescriptor(pDevObj, pFltDev->szSerial, sizeof(pFltDev->szSerial), devdescr->iSerialNumber, langId);
if (!NT_SUCCESS(status))
{
AssertMsgFailed(("VBoxUSBGetDeviceDescription: reading serial number failed\n"));
goto fail;
}
status = VBoxUSBGetStringDescriptor(pDevObj, pFltDev->szMfgName, sizeof(pFltDev->szMfgName), devdescr->iManufacturer, langId);
if (!NT_SUCCESS(status))
{
AssertMsgFailed(("VBoxUSBGetDeviceDescription: reading manufacturer name failed\n"));
goto fail;
}
status = VBoxUSBGetStringDescriptor(pDevObj, pFltDev->szProduct, sizeof(pFltDev->szProduct), devdescr->iProduct, langId);
if (!NT_SUCCESS(status))
{
AssertMsgFailed(("VBoxUSBGetDeviceDescription: reading product name failed\n"));
goto fail;
}
DebugPrint(("VBoxUSBGetStringDescriptor: strings: '%s':'%s':'%s' (lang ID %x)\n",
}
fail:
if (devdescr)
return status;
}
/**
* Get USB device description
*
* @returns NT Status
* @param pDevObj USB device pointer
* @param pusVendorId Vendor id (out)
* @param pusProductId Product id (out)
* @param pusRevision Revision (out)
*/
NTSTATUS VBoxUSBGetDeviceDescription(PDEVICE_OBJECT pDevObj, USHORT *pusVendorId, USHORT *pusProductId, USHORT *pusRevision)
{
{
DebugPrint(("Failed to alloc mem for urb\n"));
goto fail;
}
status = VBoxUSBGetDescriptor(pDevObj, devdescr, sizeof(*devdescr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0);
if (!NT_SUCCESS(status))
{
DebugPrint(("VBoxUSBGetDeviceDescription: getting device descriptor failed\n"));
goto fail;
}
DebugPrint(("Device pid=%x vid=%x rev=%x\n", devdescr->idVendor, devdescr->idProduct, devdescr->bcdDevice));
return STATUS_SUCCESS;
fail:
if (devdescr)
return status;
}
/**
* Unplug and replug the specified USB device
*
* @returns NT status code
* @param usVendorId Vendor id
* @param usProductId Product id
* @param usRevision Revision
* @param fCaptured Already captured or not
* @param pfReplugged Replugged or not (out)
*/
NTSTATUS VBoxUSBReplugDevice(USHORT usVendorId, USHORT usProductId, USHORT usRevision, bool fCaptured, bool *pfReplugged)
{
*pfReplugged = false;
for (int i=0;i<RT_ELEMENTS(lpszStandardControllerName);i++)
{
szStandardControllerName[i].Length = 0;
szStandardControllerName[i].Buffer = 0;
}
for (int i=0;i<16;i++)
{
char szHubName[32];
UnicodeName.Length = 0;
UnicodeName.MaximumLength = 0;
UnicodeName.Buffer = 0;
if (status == STATUS_SUCCESS)
{
DebugPrint(("IoGetDeviceObjectPointer for %s returned %p %p\n", szHubName, pHubDevObj, pHubFileObj));
if ( pHubDevObj->DriverObject
)
{
for (int j=0;j<RT_ELEMENTS(lpszStandardControllerName);j++)
{
if (!RtlCompareUnicodeString(&szStandardControllerName[j], &pHubDevObj->DriverObject->DriverName, TRUE /* case insensitive */))
{
DebugPrint(("Associated driver %wZ -> related dev obj=%p\n", pHubDevObj->DriverObject->DriverName, IoGetRelatedDeviceObject(pHubFileObj)));
if ( status == STATUS_SUCCESS
&& pDevRelations)
{
for (unsigned k=0;k<pDevRelations->Count;k++)
{
VBoxUSBGetDeviceDescription(pDevRelations->Objects[k], &usPDOVendorId, &usPDOProductId, &usPDORevision);
&& usPDOVendorId == usVendorId
&& usPDOProductId == usProductId
&& usPDORevision == usRevision)
{
DebugPrint(("REPLUG device -> \n"));
/* Simulate a device replug */
*pfReplugged = true;
}
if (*pfReplugged == true)
break;
}
}
if (*pfReplugged == true)
break;
}
}
}
}
if (*pfReplugged == true)
break;
}
return STATUS_SUCCESS;
}
/**
* Capture specified USB device
*
* @returns NT status code
* @param usVendorId Vendor id
* @param usProductId Product id
* @param usRevision Revision
*/
{
bool fReplugged;
DebugPrint(("VBoxUSBReleaseDevice\n"));
DrvInstance.fDisableFilters = true;
if ( status != STATUS_SUCCESS
|| !fReplugged)
DrvInstance.fDisableFilters = false;
return status;
}
/**
* Capture specified USB device
*
* @returns NT status code
* @param usVendorId Vendor id
* @param usProductId Product id
* @param usRevision Revision
*/
{
bool fReplugged;
DebugPrint(("VBoxUSBGrabDevice\n"));
DrvInstance.fForceGrab = true;
if ( status != STATUS_SUCCESS
|| !fReplugged)
DrvInstance.fForceGrab = false;
return status;
}