USBMon.cpp revision c55c68b6a3324172e9dc207926215845880b0f90
/** @file
*
* VBox USB drivers - USB Monitor driver - Win32 host:
*/
/*
* 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.
*/
#include "USBMon.h"
#include <excpt.h>
#include <stdio.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"
static PDRIVER_DISPATCH pfnOldPnPHandler = 0;
static struct
{
bool fValid;
} InstalledPDOHooks[16] = {0};
/**
* Driver entry point.
*
* @returns appropriate status code.
* @param pDrvObj Pointer to driver object.
* @param pRegPath Registry base path.
*/
{
DebugPrint(("VBoxUSBMon::DriverEntry\n"));
/*
* Create device.
* (That means creating a device object and a symbolic link so the DOS
* subsystems (OS/2, win32, ++) can access the device.)
*/
rc = IoCreateDevice(pDrvObj, sizeof(DEVICE_EXTENSION), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if (NT_SUCCESS(rc))
{
/* Save device object pointer in order to check whether we're being called in the entry points we register. */
if (NT_SUCCESS(rc))
{
/*
* Initialize the device extension.
*/
/*
* Setup the driver entry points in pDrvObj.
*/
}
// pDrvObj->MajorFunction[IRP_MJ_PNP] = VBoxUSBMonDispatchPnp;
// pDrvObj->MajorFunction[IRP_MJ_POWER] = VBoxUSBMonDispatchPower;
/* Note: unable to unload driver if this is set: */
//// pDrvObj->DriverExtension->AddDevice = VBoxUSBMonAddDevice;
VBoxUSBInit();
DebugPrint(("VBoxUSBMon::DriverEntry returning STATUS_SUCCESS\n"));
return STATUS_SUCCESS;
}
else
}
else
if (NT_SUCCESS(rc))
return rc;
}
/**
* Uninstall the PnP irp hook
*
* @param pDevExt Device extension
*/
{
szStandardHubName.Length = 0;
szStandardHubName.Buffer = 0;
DebugPrint(("Unhook USB hub drivers\n"));
for (int i = 0; i < RT_ELEMENTS(InstalledPDOHooks); i++)
{
char szHubName[32];
/* Don't bother to check PDO's that we haven't hooked; might even lead to host crashes during shutdown as IoGetDeviceObjectPointer can reopen a driver. */
if (!InstalledPDOHooks[i].fValid)
continue;
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
&& !RtlCompareUnicodeString(&szStandardHubName, &pHubDevObj->DriverObject->DriverName, TRUE /* case insensitive */))
{
#if 0
DebugPrint(("Associated driver"));
#endif
InterlockedCompareExchangePointer((PVOID *)&pHubDevObj->DriverObject->MajorFunction[IRP_MJ_PNP], pfnOldPnPHandler, VBoxUSBMonPnPHook);
}
}
// else
// DebugPrint(("IoGetDeviceObjectPointer for %s returned %x\n", szHubName, status));
}
}
/**
* Unload the driver.
*
* @param pDrvObj Driver object.
*/
{
if (pfnOldPnPHandler) /* very bad! */
/* Clean up the filter manager */
VBoxUSBTerm();
/*
* We ASSUME that it's not possible to unload a driver with open handles.
* Start by deleting the symbolic link
*/
}
/**
* Create (i.e. Open) file entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
DebugPrint(("VBoxUSBMonCreate\n"));
/*
* We are not remotely similar to a directory...
* (But this is possible.)
*/
{
return STATUS_NOT_A_DIRECTORY;
}
{
szStandardHubName.Length = 0;
szStandardHubName.Buffer = 0;
DebugPrint(("Hook USB hub drivers\n"));
for (int i = 0; i < RT_ELEMENTS(InstalledPDOHooks); i++)
{
char szHubName[32];
InstalledPDOHooks[i].fValid = false;
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
&& !RtlCompareUnicodeString(&szStandardHubName, &pHubDevObj->DriverObject->DriverName, TRUE /* case insensitive */))
{
#if 0
DebugPrint(("Associated driver"));
#endif
if ( !pfnOldPnPHandler
{
InstalledPDOHooks[i].fValid = true;
pfnOldPnPHandler = (PDRIVER_DISPATCH)InterlockedExchangePointer((PVOID *)&pHubDevObj->DriverObject->MajorFunction[IRP_MJ_PNP], VBoxUSBMonPnPHook);
}
}
}
// else
// DebugPrint(("IoGetDeviceObjectPointer for %s returned %x\n", szHubName, status));
}
//
// Let us use remove lock to keep count of IRPs so that we don't
// devstack are completed. Remlock is required to protect us from
// various race conditions where our driver can get unloaded while we
// are still running dispatch or completion code.
//
1, // MaxLockedMinutes
100); // HighWatermark, this parameter is
// used only on checked build. Specifies
// the maximum number of outstanding
// acquisitions allowed on the lock
}
if (!NT_SUCCESS(status))
{
return status;
}
return STATUS_SUCCESS;
}
/**
* Close file entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
DebugPrint(("VBoxUSBMonClose: pDevExt=%p pFileObj=%p pSession=%p\n", pDevExt, pFileObj, pFileObj->FsContext));
/* Uninstall hook when we close the last instance. */
VBoxUSBClose();
/* Wait for all outstanding requests to complete */
/* We're supposed to use it in the remove device function. During unload
* is no option as that crashes Vista64
*/
DebugPrint(("Waiting for outstanding requests\n"));
DebugPrint(("Done waiting.\n"));
return STATUS_SUCCESS;
}
/**
* Device I/O Control entry point.
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
if (!NT_SUCCESS(status))
{
return status;
}
/* Handle ioctl meant for us */
/* Release the remove lock */
return status;
}
/**
* Pass on or refuse entry point
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
#ifdef DEBUG
#endif
/* Meant for us; report error */
return STATUS_NOT_SUPPORTED;
}
/**
* Handle the specific PnP ioctls we need for stealing devices.
*
* @param irpStack Device object
* @param pIoStatus IO status of finished IRP
*/
{
switch(irpStack->MinorFunction)
{
case IRP_MN_QUERY_DEVICE_TEXT:
{
{
{
{
break;
case DeviceTextDescription:
{
if (!pId)
{
AssertFailed();
break;
}
}
break;
}
}
else
}
break;
}
case IRP_MN_QUERY_ID:
{
&& irpStack->DeviceObject)
{
#ifdef DEBUG
#endif
{
{
case BusQueryInstanceID:
break;
case BusQueryDeviceID:
{
if (!pId)
{
AssertFailed();
break;
}
}
break;
case BusQueryHardwareIDs:
#ifdef DEBUG
while(*pId) //MULTI_SZ
{
pId++;
}
#endif
{
if (!pId)
{
AssertFailed();
break;
}
#ifdef DEBUG
while(*pTmp) //MULTI_SZ
{
pTmp++;
}
#endif
}
break;
case BusQueryCompatibleIDs:
#ifdef DEBUG
while(*pId) //MULTI_SZ
{
pId++;
}
#endif
{
if (!pId)
{
AssertFailed();
break;
}
#ifdef DEBUG
while(*pTmp) //MULTI_SZ
{
pTmp++;
}
#endif
}
break;
}
}
else
}
break;
}
#ifdef DEBUG
{
{
case BusRelations:
{
DebugPrint(("BusRelations\n"));
{
{
{
}
}
else
}
break;
}
case TargetDeviceRelation:
DebugPrint(("TargetDeviceRelation\n"));
break;
case RemovalRelations:
DebugPrint(("RemovalRelations\n"));
break;
case EjectionRelations:
DebugPrint(("EjectionRelations\n"));
break;
}
break;
}
{
{
{
}
else
}
break;
}
#endif
} /*switch */
DebugPrint(("VBoxUSBMonHandlePnPIoctl returns %x (IRQL = %d)\n", pIoStatus->Status, KeGetCurrentIrql()));
}
/**
* IRP completion notification callback. Used for selected PNP irps.
*
* @param pDevObj Device object.(always NULL!)
* @param pIrp Request packet.
* @param context User parameter (old IRP)
*/
{
/* Note: pDevObj is NULL! */
DebugPrint(("VBoxUSBPnPCompletion %p %p %p minor=%x\n", pDevObj, pIrp, context, irpStack->MinorFunction));
#ifdef USBMON_ASYNC
/* Copy back the result to the original IRP. */
/* Unlock & free mdls of our duplicate IRP. */
{
}
/* Free our duplicate IRP */
/* Release the remove lock */
/* Complete the original request */
return STATUS_MORE_PROCESSING_REQUIRED; /* must return this as we allocated the IRP with IoBuildAsynchronousFsdRequest! */
#else
return STATUS_CONTINUE_COMPLETION;
#endif
}
/**
* Device PnP hook
*
* @param pDevObj Device object.
* @param pIrp Request packet.
*/
{
DebugPrint(("VBoxUSBMonPnPHook pDevObj=%p %s IRP:%p \n", pDevObj, PnPMinorFunctionString(irpStack->MinorFunction), pIrp));
if (!NT_SUCCESS(status))
{
return status;
}
{
switch(irpStack->MinorFunction)
{
case IRP_MN_QUERY_DEVICE_TEXT:
case IRP_MN_QUERY_ID:
#ifdef DEBUG
/* hooking this IRP causes problems for some reason */
//case IRP_MN_QUERY_DEVICE_RELATIONS:
#endif
{
#ifdef USBMON_ASYNC
/* The driver verifier claims all PNP irps must have Status preset to STATUS_NOT_SUPPORTED */
if (!pNewIrp)
break;
/* Get the next stack location as that is used for the new irp */
if (newIrpStack)
{
/* Make a copy of the original IRP */
/* Mark the original Irp as pending; will be completed above */
return STATUS_PENDING; /* always return this! */
}
else
{
break;
}
#else
/* The driver verifier claims all PNP irps must have Status preset to STATUS_NOT_SUPPORTED */
if (!pNewIrp)
break;
/* Get the next stack location as that is used for the new irp */
if (newIrpStack)
{
/* Make a copy of the original IRP */
if (status == STATUS_PENDING)
{
}
if (status == STATUS_SUCCESS)
{
}
/* Copy back the result to the original IRP. */
/* Complete the original request */
/* Release the remove lock */
return status;
}
else
{
break;
}
#endif
}
case IRP_MN_SURPRISE_REMOVAL:
case IRP_MN_REMOVE_DEVICE:
break;
/* These two IRPs are received when the PnP subsystem has determined the id of the newly arrived device */
/* IRP_MN_START_DEVICE only arrives if it's a USB device of a known class or with a present host driver */
case IRP_MN_QUERY_RESOURCES:
break;
default:
break;
}
}
return status;
}
/**
* Send IRP_MN_QUERY_DEVICE_RELATIONS
*
* @returns NT Status
* @param pDevObj USB device pointer
* @param pFileObj Valid file object pointer
* @param pDevRelations Pointer to DEVICE_RELATIONS pointer (out)
*/
NTSTATUS VBoxUSBQueryBusRelations(PDEVICE_OBJECT pDevObj, PFILE_OBJECT pFileObj, PDEVICE_RELATIONS *pDevRelations)
{
*pDevRelations = NULL;
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"));
}
if (status == STATUS_SUCCESS)
{
{
*pDevRelations = pRel;
}
else
}
return status;
}
#ifdef DEBUG
{
switch (MinorFunction)
{
case IRP_MN_START_DEVICE:
return "IRP_MN_START_DEVICE";
return "IRP_MN_QUERY_REMOVE_DEVICE";
case IRP_MN_REMOVE_DEVICE:
return "IRP_MN_REMOVE_DEVICE";
return "IRP_MN_CANCEL_REMOVE_DEVICE";
case IRP_MN_STOP_DEVICE:
return "IRP_MN_STOP_DEVICE";
case IRP_MN_QUERY_STOP_DEVICE:
return "IRP_MN_QUERY_STOP_DEVICE";
return "IRP_MN_CANCEL_STOP_DEVICE";
return "IRP_MN_QUERY_DEVICE_RELATIONS";
case IRP_MN_QUERY_INTERFACE:
return "IRP_MN_QUERY_INTERFACE";
return "IRP_MN_QUERY_CAPABILITIES";
case IRP_MN_QUERY_RESOURCES:
return "IRP_MN_QUERY_RESOURCES";
return "IRP_MN_QUERY_RESOURCE_REQUIREMENTS";
case IRP_MN_QUERY_DEVICE_TEXT:
return "IRP_MN_QUERY_DEVICE_TEXT";
return "IRP_MN_FILTER_RESOURCE_REQUIREMENTS";
case IRP_MN_READ_CONFIG:
return "IRP_MN_READ_CONFIG";
case IRP_MN_WRITE_CONFIG:
return "IRP_MN_WRITE_CONFIG";
case IRP_MN_EJECT:
return "IRP_MN_EJECT";
case IRP_MN_SET_LOCK:
return "IRP_MN_SET_LOCK";
case IRP_MN_QUERY_ID:
return "IRP_MN_QUERY_ID";
return "IRP_MN_QUERY_PNP_DEVICE_STATE";
return "IRP_MN_QUERY_BUS_INFORMATION";
return "IRP_MN_DEVICE_USAGE_NOTIFICATION";
case IRP_MN_SURPRISE_REMOVAL:
return "IRP_MN_SURPRISE_REMOVAL";
default:
return "unknown_pnp_irp";
}
}
{
int i;
{
}
}
#endif