/* $Id: VBoxUsbFlt.cpp 38488 2011-08-18 10:26:06Z vboxsync $ */ /** @file * VBox USB Monitor Device Filtering functionality */ /* * 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; * you can redistribute it and/or modify it under the terms of the GNU * 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 "VBoxUsbMon.h" #include "../cmn/VBoxUsbTool.h" #include #include #include #include #include //#include #include #include #pragma warning(disable : 4200) #include "usbdi.h" #pragma warning(default : 4200) #include "usbdlib.h" #include "VBoxUSBFilterMgr.h" #include #include /* * 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. */ static LPWSTR lpszStandardControllerName[1] = { L"\\Driver\\usbhub", }; /* * state transitions: * * (we are not filtering this device ) * ADDED --> UNCAPTURED ------------------------------->- * | | * | (we are filtering this device, | (the device is being * | waiting for our device driver | re-plugged to perform * | to pick it up) | capture-uncapture transition) * |-> CAPTURING -------------------------------->|---> REPLUGGING ----- * ^ | (device driver picked | | * | | up the device) | (remove cased | (device is removed * | ->---> CAPTURED ---------------------->| by "real" removal | the device info is removed form the list) * | | |------------------->->--> REMOVED * | | | * |-----------<->---> USED_BY_GUEST ------->| * | | * |------------------------<- * * NOTE: the order of enums DOES MATTER!! * Do not blindly modify!! as the code assumes the state is ordered this way. */ typedef enum { VBOXUSBFLT_DEVSTATE_UNKNOWN = 0, VBOXUSBFLT_DEVSTATE_REMOVED, VBOXUSBFLT_DEVSTATE_REPLUGGING, VBOXUSBFLT_DEVSTATE_ADDED, VBOXUSBFLT_DEVSTATE_UNCAPTURED, VBOXUSBFLT_DEVSTATE_CAPTURING, VBOXUSBFLT_DEVSTATE_CAPTURED, VBOXUSBFLT_DEVSTATE_USED_BY_GUEST, VBOXUSBFLT_DEVSTATE_32BIT_HACK = 0x7fffffff } VBOXUSBFLT_DEVSTATE; typedef struct VBOXUSBFLT_DEVICE { LIST_ENTRY GlobalLe; /* auxiliary list to be used for gathering devices to be re-plugged * only thread that puts the device to the REPLUGGING state can use this list */ LIST_ENTRY RepluggingLe; /* Owning session. Each matched device has an owning session. */ struct VBOXUSBFLTCTX *pOwner; /* filter id - if NULL AND device has an owner - the filter is destroyed */ uintptr_t uFltId; /* true iff device is filtered with a one-shot filter */ bool fIsFilterOneShot; /* The device state. If the non-owner session is requesting the state while the device is grabbed, * the USBDEVICESTATE_USED_BY_HOST is returned. */ VBOXUSBFLT_DEVSTATE enmState; volatile uint32_t cRefs; PDEVICE_OBJECT Pdo; uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; uint8_t bClass; uint8_t bSubClass; uint8_t bProtocol; char szSerial[MAX_USB_SERIAL_STRING]; char szMfgName[MAX_USB_SERIAL_STRING]; char szProduct[MAX_USB_SERIAL_STRING]; #if 0 char szDrvKeyName[512]; BOOLEAN fHighSpeed; #endif } VBOXUSBFLT_DEVICE, *PVBOXUSBFLT_DEVICE; #define PVBOXUSBFLT_DEVICE_FROM_LE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, GlobalLe) ) ) #define PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, RepluggingLe) ) ) #define PVBOXUSBFLTCTX_FROM_LE(_pLe) ( (PVBOXUSBFLTCTX)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLTCTX, ListEntry) ) ) typedef struct VBOXUSBFLT_LOCK { KSPIN_LOCK Lock; KIRQL OldIrql; } VBOXUSBFLT_LOCK, *PVBOXUSBFLT_LOCK; #define VBOXUSBFLT_LOCK_INIT() \ KeInitializeSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock) #define VBOXUSBFLT_LOCK_TERM() do { } while (0) #define VBOXUSBFLT_LOCK_ACQUIRE() \ KeAcquireSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, &g_VBoxUsbFltGlobals.Lock.OldIrql); #define VBOXUSBFLT_LOCK_RELEASE() \ KeReleaseSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, g_VBoxUsbFltGlobals.Lock.OldIrql); typedef struct VBOXUSBFLT_BLDEV { LIST_ENTRY ListEntry; uint16_t idVendor; uint16_t idProduct; uint16_t bcdDevice; } VBOXUSBFLT_BLDEV, *PVBOXUSBFLT_BLDEV; #define PVBOXUSBFLT_BLDEV_FROM_LE(_pLe) ( (PVBOXUSBFLT_BLDEV)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_BLDEV, ListEntry) ) ) typedef struct VBOXUSBFLTGLOBALS { LIST_ENTRY DeviceList; LIST_ENTRY ContextList; /* devices known to misbehave */ LIST_ENTRY BlackDeviceList; VBOXUSBFLT_LOCK Lock; } VBOXUSBFLTGLOBALS, *PVBOXUSBFLTGLOBALS; static VBOXUSBFLTGLOBALS g_VBoxUsbFltGlobals; static bool vboxUsbFltBlDevMatchLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice) { for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList; pEntry = pEntry->Flink) { PVBOXUSBFLT_BLDEV pDev = PVBOXUSBFLT_BLDEV_FROM_LE(pEntry); if (pDev->idVendor != idVendor) continue; if (pDev->idProduct != idProduct) continue; if (pDev->bcdDevice != bcdDevice) continue; return true; } return false; } static NTSTATUS vboxUsbFltBlDevAddLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice) { if (vboxUsbFltBlDevMatchLocked(idVendor, idProduct, bcdDevice)) return STATUS_SUCCESS; PVBOXUSBFLT_BLDEV pDev = (PVBOXUSBFLT_BLDEV)VBoxUsbMonMemAllocZ(sizeof (*pDev)); if (!pDev) { AssertFailed(); return STATUS_INSUFFICIENT_RESOURCES; } pDev->idVendor = idVendor; pDev->idProduct = idProduct; pDev->bcdDevice = bcdDevice; InsertHeadList(&g_VBoxUsbFltGlobals.BlackDeviceList, &pDev->ListEntry); return STATUS_SUCCESS; } static void vboxUsbFltBlDevClearLocked() { PLIST_ENTRY pNext; for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList; pEntry = pNext) { pNext = pEntry->Flink; VBoxUsbMonMemFree(pEntry); } } static void vboxUsbFltBlDevPopulateWithKnownLocked() { /* this one halts when trying to get string descriptors from it */ vboxUsbFltBlDevAddLocked(0x5ac, 0x921c, 0x115); } DECLINLINE(void) vboxUsbFltDevRetain(PVBOXUSBFLT_DEVICE pDevice) { Assert(pDevice->cRefs); ASMAtomicIncU32(&pDevice->cRefs); } static void vboxUsbFltDevDestroy(PVBOXUSBFLT_DEVICE pDevice) { Assert(!pDevice->cRefs); Assert(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED); VBoxUsbMonMemFree(pDevice); } DECLINLINE(void) vboxUsbFltDevRelease(PVBOXUSBFLT_DEVICE pDevice) { uint32_t cRefs = ASMAtomicDecU32(&pDevice->cRefs); Assert(cRefs < UINT32_MAX/2); if (!cRefs) { vboxUsbFltDevDestroy(pDevice); } } static void vboxUsbFltDevOwnerSetLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot) { ASSERT_WARN(!pDevice->pOwner, ("device 0x%p has an owner(0x%p)", pDevice, pDevice->pOwner)); ++pContext->cActiveFilters; pDevice->pOwner = pContext; pDevice->uFltId = uFltId; pDevice->fIsFilterOneShot = fIsOneShot; } static void vboxUsbFltDevOwnerClearLocked(PVBOXUSBFLT_DEVICE pDevice) { ASSERT_WARN(pDevice->pOwner, ("no owner for device 0x%p", pDevice)); --pDevice->pOwner->cActiveFilters; ASSERT_WARN(pDevice->pOwner->cActiveFilters < UINT32_MAX/2, ("cActiveFilters (%d)", pDevice->pOwner->cActiveFilters)); pDevice->pOwner = NULL; pDevice->uFltId = 0; } static void vboxUsbFltDevOwnerUpdateLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot) { if (pDevice->pOwner != pContext) { if (pDevice->pOwner) vboxUsbFltDevOwnerClearLocked(pDevice); if (pContext) vboxUsbFltDevOwnerSetLocked(pDevice, pContext, uFltId, fIsOneShot); } else if (pContext) { pDevice->uFltId = uFltId; pDevice->fIsFilterOneShot = fIsOneShot; } } static PVBOXUSBFLT_DEVICE vboxUsbFltDevGetLocked(PDEVICE_OBJECT pPdo) { #ifdef VBOX_USB_WITH_VERBOSE_LOGGING for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.DeviceList; pEntry = pEntry->Flink) { PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry); for (PLIST_ENTRY pEntry2 = pEntry->Flink; pEntry2 != &g_VBoxUsbFltGlobals.DeviceList; pEntry2 = pEntry2->Flink) { PVBOXUSBFLT_DEVICE pDevice2 = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry2); ASSERT_WARN( pDevice->idVendor != pDevice2->idVendor || pDevice->idProduct != pDevice2->idProduct || pDevice->bcdDevice != pDevice2->bcdDevice, ("duplicate devices in a list!!")); } } #endif for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.DeviceList; pEntry = pEntry->Flink) { PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry); ASSERT_WARN( pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING || pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST, ("Invalid device state(%d) for device(0x%p) PDO(0x%p)", pDevice->enmState, pDevice, pDevice->Pdo)); if (pDevice->Pdo == pPdo) return pDevice; } return NULL; } PVBOXUSBFLT_DEVICE vboxUsbFltDevGet(PDEVICE_OBJECT pPdo) { PVBOXUSBFLT_DEVICE pDevice; VBOXUSBFLT_LOCK_ACQUIRE(); pDevice = vboxUsbFltDevGetLocked(pPdo); if (pDevice->enmState > VBOXUSBFLT_DEVSTATE_ADDED) { vboxUsbFltDevRetain(pDevice); LOG(("found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo)); } else { LOG(("found replugging device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo)); pDevice = NULL; } VBOXUSBFLT_LOCK_RELEASE(); return pDevice; } static NTSTATUS vboxUsbFltPdoReplug(PDEVICE_OBJECT pDo) { LOG(("Replugging PDO(0x%p)", pDo)); NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_CYCLE_PORT, NULL, NULL); ASSERT_WARN(Status == STATUS_SUCCESS, ("replugging PDO(0x%p) failed Status(0x%x)", pDo, Status)); LOG(("Replugging PDO(0x%p) done with Status(0x%x)", pDo, Status)); return Status; } static PVBOXUSBFLTCTX vboxUsbFltDevMatchLocked(PVBOXUSBFLT_DEVICE pDevice, uintptr_t *puId, bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot) { USBFILTER DevFlt; USBFilterInit(&DevFlt, USBFILTERTYPE_CAPTURE); USBFilterSetNumExact(&DevFlt, USBFILTERIDX_VENDOR_ID, pDevice->idVendor, true); USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PRODUCT_ID, pDevice->idProduct, true); USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_REV, pDevice->bcdDevice, true); USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_CLASS, pDevice->bClass, true); USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_SUB_CLASS, pDevice->bSubClass, true); USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_PROTOCOL, pDevice->bProtocol, true); USBFilterSetStringExact(&DevFlt, USBFILTERIDX_MANUFACTURER_STR, pDevice->szMfgName, true); USBFilterSetStringExact(&DevFlt, USBFILTERIDX_PRODUCT_STR, pDevice->szProduct, true); USBFilterSetStringExact(&DevFlt, USBFILTERIDX_SERIAL_NUMBER_STR, pDevice->szSerial, true); /* Run filters on the thing. */ *puId = 0; *pfFilter = false; *pfIsOneShot = false; PVBOXUSBFLTCTX pOwner = VBoxUSBFilterMatchEx(&DevFlt, puId, fRemoveFltIfOneShot, pfFilter, pfIsOneShot); USBFilterDelete(&DevFlt); return pOwner; } static void vboxUsbFltDevStateMarkReplugLocked(PVBOXUSBFLT_DEVICE pDevice) { vboxUsbFltDevOwnerUpdateLocked(pDevice, NULL, 0, false); pDevice->enmState = VBOXUSBFLT_DEVSTATE_REPLUGGING; } static bool vboxUsbFltDevStateIsNotFiltered(PVBOXUSBFLT_DEVICE pDevice) { return pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED; } static bool vboxUsbFltDevStateIsFiltered(PVBOXUSBFLT_DEVICE pDevice) { return pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING; } #define VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS 10000 static NTSTATUS vboxUsbFltDevPopulate(PVBOXUSBFLT_DEVICE pDevice, PDEVICE_OBJECT pDo /*, BOOLEAN bPopulateNonFilterProps*/) { NTSTATUS Status; PUSB_DEVICE_DESCRIPTOR pDevDr = 0; pDevice->Pdo = pDo; LOG(("Populating Device(0x%p) for PDO(0x%p)", pDevice, pDo)); pDevDr = (PUSB_DEVICE_DESCRIPTOR)VBoxUsbMonMemAllocZ(sizeof(*pDevDr)); if (pDevDr == NULL) { WARN(("Failed to alloc mem for urb")); return STATUS_INSUFFICIENT_RESOURCES; } do { Status = VBoxUsbToolGetDescriptor(pDo, pDevDr, sizeof(*pDevDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS); if (!NT_SUCCESS(Status)) { WARN(("getting device descriptor failed, Status (0x%x)", Status)); break; } if (vboxUsbFltBlDevMatchLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice)) { WARN(("found a known black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice)); Status = STATUS_UNSUCCESSFUL; break; } LOG(("Device pid=%x vid=%x rev=%x", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice)); pDevice->idVendor = pDevDr->idVendor; pDevice->idProduct = pDevDr->idProduct; pDevice->bcdDevice = pDevDr->bcdDevice; pDevice->bClass = pDevDr->bDeviceClass; pDevice->bSubClass = pDevDr->bDeviceSubClass; pDevice->bProtocol = pDevDr->bDeviceProtocol; pDevice->szSerial[0] = 0; pDevice->szMfgName[0] = 0; pDevice->szProduct[0] = 0; /* If there are no strings, don't even try to get any string descriptors. */ if (pDevDr->iSerialNumber || pDevDr->iManufacturer || pDevDr->iProduct) { int langId; Status = VBoxUsbToolGetLangID(pDo, &langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS); if (!NT_SUCCESS(Status)) { WARN(("reading language ID failed")); if (Status == STATUS_CANCELLED) { WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice)); vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice); Status = STATUS_UNSUCCESSFUL; } break; } if (pDevDr->iSerialNumber) { Status = VBoxUsbToolGetStringDescriptorA(pDo, pDevice->szSerial, sizeof (pDevice->szSerial), pDevDr->iSerialNumber, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS); if (!NT_SUCCESS(Status)) { WARN(("reading serial number failed")); ASSERT_WARN(pDevice->szSerial[0] == '\0', ("serial is not zero!!")); if (Status == STATUS_CANCELLED) { WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice)); vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice); Status = STATUS_UNSUCCESSFUL; break; } LOG(("pretending success..")); Status = STATUS_SUCCESS; } } if (pDevDr->iManufacturer) { Status = VBoxUsbToolGetStringDescriptorA(pDo, pDevice->szMfgName, sizeof (pDevice->szMfgName), pDevDr->iManufacturer, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS); if (!NT_SUCCESS(Status)) { WARN(("reading manufacturer name failed")); ASSERT_WARN(pDevice->szMfgName[0] == '\0', ("szMfgName is not zero!!")); if (Status == STATUS_CANCELLED) { WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice)); vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice); Status = STATUS_UNSUCCESSFUL; break; } LOG(("pretending success..")); Status = STATUS_SUCCESS; } } if (pDevDr->iProduct) { Status = VBoxUsbToolGetStringDescriptorA(pDo, pDevice->szProduct, sizeof (pDevice->szProduct), pDevDr->iProduct, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS); if (!NT_SUCCESS(Status)) { WARN(("reading product name failed")); ASSERT_WARN(pDevice->szProduct[0] == '\0', ("szProduct is not zero!!")); if (Status == STATUS_CANCELLED) { WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice)); vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice); Status = STATUS_UNSUCCESSFUL; break; } LOG(("pretending success..")); Status = STATUS_SUCCESS; } } #if 0 if (bPopulateNonFilterProps) { WCHAR RegKeyBuf[512]; ULONG cbRegKeyBuf = sizeof (RegKeyBuf); Status = IoGetDeviceProperty(pDo, DevicePropertyDriverKeyName, cbRegKeyBuf, RegKeyBuf, &cbRegKeyBuf); if (!NT_SUCCESS(Status)) { AssertMsgFailed((__FUNCTION__": IoGetDeviceProperty failed Status (0x%x)", Status)); break; } ANSI_STRING Ansi; UNICODE_STRING Unicode; Ansi.Buffer = pDevice->szDrvKeyName; Ansi.Length = 0; Ansi.MaximumLength = sizeof(pDevice->szDrvKeyName); RtlInitUnicodeString(&Unicode, RegKeyBuf); Status = RtlUnicodeStringToAnsiString(&Ansi, &Unicode, FALSE /* do not allocate */); if (!NT_SUCCESS(Status)) { AssertMsgFailed((__FUNCTION__": RtlUnicodeStringToAnsiString failed Status (0x%x)", Status)); break; } pDevice->fHighSpend = FALSE; Status = VBoxUsbToolGetDeviceSpeed(pDo, &pDevice->fHighSpend); if (!NT_SUCCESS(Status)) { AssertMsgFailed((__FUNCTION__": VBoxUsbToolGetDeviceSpeed failed Status (0x%x)", Status)); break; } } #endif LOG((": strings: '%s':'%s':'%s' (lang ID %x)", pDevice->szMfgName, pDevice->szProduct, pDevice->szSerial, langId)); } LOG(("Populating Device(0x%p) for PDO(0x%p) Succeeded", pDevice, pDo)); Status = STATUS_SUCCESS; } while (0); VBoxUsbMonMemFree(pDevDr); LOG(("Populating Device(0x%p) for PDO(0x%p) Done, Status (0x%x)", pDevice, pDo, Status)); return Status; } static void vboxUsbFltSignalChangeLocked() { for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.ContextList.Flink; pEntry != &g_VBoxUsbFltGlobals.ContextList; pEntry = pEntry->Flink) { PVBOXUSBFLTCTX pCtx = PVBOXUSBFLTCTX_FROM_LE(pEntry); /* the removed context can not be in a list */ Assert(!pCtx->bRemoved); if (pCtx->pChangeEvent) { KeSetEvent(pCtx->pChangeEvent, 0, /* increment*/ FALSE /* wait */); } } } static bool vboxUsbFltDevCheckReplugLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext) { ASSERT_WARN(pContext, ("context is NULL!")); LOG(("Current context is (0x%p)", pContext)); LOG(("Current Device owner is (0x%p)", pDevice->pOwner)); /* check if device is already replugging */ if (pDevice->enmState <= VBOXUSBFLT_DEVSTATE_ADDED) { LOG(("Device (0x%p) is already replugging, return..", pDevice)); /* it is, do nothing */ ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING, ("Device (0x%p) state is NOT REPLUGGING (%d)", pDevice, pDevice->enmState)); return false; } if (pDevice->pOwner && pContext != pDevice->pOwner) { LOG(("Device (0x%p) is owned by another context(0x%p), current is(0x%p)", pDevice, pDevice->pOwner, pContext)); /* this device is owned by another context, we're not allowed to do anything */ return false; } uintptr_t uId = 0; bool bNeedReplug = false; bool fFilter = false; bool fIsOneShot = false; PVBOXUSBFLTCTX pNewOwner = vboxUsbFltDevMatchLocked(pDevice, &uId, false, /* do not remove a one-shot filter */ &fFilter, &fIsOneShot); LOG(("Matching Info: Filter (0x%p), NewOwner(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pNewOwner, (int)fFilter, (int)fIsOneShot)); if (pDevice->pOwner && pNewOwner && pDevice->pOwner != pNewOwner) { LOG(("Matching: Device (0x%p) is requested another owner(0x%p), current is(0x%p)", pDevice, pNewOwner, pDevice->pOwner)); /* the device is owned by another owner, we can not change the owner here */ return false; } if (!fFilter) { LOG(("Matching: Device (0x%p) should NOT be filtered", pDevice)); /* the device should NOT be filtered, check the current state */ if (vboxUsbFltDevStateIsNotFiltered(pDevice)) { LOG(("Device (0x%p) is NOT filtered", pDevice)); /* no changes */ if (fIsOneShot) { ASSERT_WARN(pNewOwner, ("no new owner")); LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId)); /* remove a one-shot filter and keep the original filter data */ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId); ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc)); if (!pDevice->pOwner) { LOG(("Matching: updating the one-shot owner to (0x%p), fltId(0x%p)", pNewOwner, uId)); /* update owner for one-shot if the owner is changed (i.e. assigned) */ vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, true); } else { LOG(("Matching: device already has owner (0x%p) assigned", pDevice->pOwner)); } } else { LOG(("Matching: This is NOT a one-shot filter (0x%p), newOwner(0x%p)", uId, pNewOwner)); if (pNewOwner) { vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, false); } } } else { LOG(("Device (0x%p) IS filtered", pDevice)); /* the device is currently filtered, we should release it only if * 1. device does not have an owner * or * 2. it should be released bue to a one-shot filter * or * 3. it is NOT grabbed by a one-shot filter */ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot) { LOG(("Matching: Need replug")); bNeedReplug = true; } } } else { LOG(("Matching: Device (0x%p) SHOULD be filtered", pDevice)); /* the device should be filtered, check the current state */ ASSERT_WARN(uId, ("zero uid")); ASSERT_WARN(pNewOwner, ("zero pNewOwner")); if (vboxUsbFltDevStateIsFiltered(pDevice)) { LOG(("Device (0x%p) IS filtered", pDevice)); /* the device is filtered */ if (pNewOwner == pDevice->pOwner) { LOG(("Device owner match")); /* no changes */ if (fIsOneShot) { LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId)); /* remove a one-shot filter and keep the original filter data */ int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId); ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc)); } else { LOG(("Matching: This is NOT a one-shot filter (0x%p), Owner(0x%p)", uId, pDevice->pOwner)); vboxUsbFltDevOwnerUpdateLocked(pDevice, pDevice->pOwner, uId, false); } } else { ASSERT_WARN(!pDevice->pOwner, ("device should NOT have owner")); LOG(("Matching: Need replug")); /* the device needs to be filtered, but the owner changes, replug needed */ bNeedReplug = true; } } else { /* the device is currently NOT filtered, * we should replug it only if * 1. device does not have an owner * or * 2. it should be captured due to a one-shot filter * or * 3. it is NOT released by a one-shot filter */ if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot) { bNeedReplug = true; LOG(("Matching: Need replug")); } } } if (bNeedReplug) { LOG(("Matching: Device needs replugging, marking as such")); vboxUsbFltDevStateMarkReplugLocked(pDevice); } else { LOG(("Matching: Device does NOT need replugging")); } return bNeedReplug; } static void vboxUsbFltReplugList(PLIST_ENTRY pList) { PLIST_ENTRY pNext; for (PLIST_ENTRY pEntry = pList->Flink; pEntry != pList; pEntry = pNext) { pNext = pEntry->Flink; PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(pEntry); LOG(("replugging matched PDO(0x%p), pDevice(0x%p)", pDevice->Pdo, pDevice)); ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING || pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED, ("invalid state(0x%x) for device(0x%p)", pDevice->enmState, pDevice)); vboxUsbFltPdoReplug(pDevice->Pdo); ObDereferenceObject(pDevice->Pdo); vboxUsbFltDevRelease(pDevice); } } typedef struct VBOXUSBFLTCHECKWALKER { PVBOXUSBFLTCTX pContext; } VBOXUSBFLTCHECKWALKER, *PVBOXUSBFLTCHECKWALKER; static DECLCALLBACK(BOOLEAN) vboxUsbFltFilterCheckWalker(PFILE_OBJECT pFile, PDEVICE_OBJECT pTopDo, PDEVICE_OBJECT pHubDo, PVOID pvContext) { PVBOXUSBFLTCHECKWALKER pData = (PVBOXUSBFLTCHECKWALKER)pvContext; PVBOXUSBFLTCTX pContext = pData->pContext; LOG(("Visiting pFile(0x%p), pTopDo(0x%p), pHubDo(0x%p), oContext(0x%p)", pFile, pTopDo, pHubDo, pContext)); KIRQL Irql = KeGetCurrentIrql(); ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql)); PDEVICE_RELATIONS pDevRelations = NULL; NTSTATUS Status = VBoxUsbMonQueryBusRelations(pTopDo, pFile, &pDevRelations); if (Status == STATUS_SUCCESS && pDevRelations) { ULONG cReplugPdos = pDevRelations->Count; LIST_ENTRY ReplugDevList; InitializeListHead(&ReplugDevList); for (ULONG k = 0; k < pDevRelations->Count; ++k) { PDEVICE_OBJECT pDevObj = pDevRelations->Objects[k]; LOG(("Found existing USB PDO 0x%p", pDevObj)); VBOXUSBFLT_LOCK_ACQUIRE(); PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pDevObj); if (pDevice) { LOG(("Found existing device info (0x%p) for PDO 0x%p", pDevice, pDevObj)); bool bReplug = vboxUsbFltDevCheckReplugLocked(pDevice, pContext); if (bReplug) { LOG(("Replug needed for device (0x%p)", pDevice)); InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe); vboxUsbFltDevRetain(pDevice); /* do not dereference object since we will use it later */ } else { LOG(("Replug NOT needed for device (0x%p)", pDevice)); ObDereferenceObject(pDevObj); } VBOXUSBFLT_LOCK_RELEASE(); pDevRelations->Objects[k] = NULL; --cReplugPdos; ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos(%d) state broken", cReplugPdos)); continue; } VBOXUSBFLT_LOCK_RELEASE(); LOG(("NO device info found for PDO 0x%p", pDevObj)); VBOXUSBFLT_DEVICE Device; Status = vboxUsbFltDevPopulate(&Device, pDevObj /*, FALSE /* only need filter properties */); if (NT_SUCCESS(Status)) { uintptr_t uId = 0; bool fFilter = false; bool fIsOneShot = false; VBOXUSBFLT_LOCK_ACQUIRE(); PVBOXUSBFLTCTX pCtx = vboxUsbFltDevMatchLocked(&Device, &uId, false, /* do not remove a one-shot filter */ &fFilter, &fIsOneShot); VBOXUSBFLT_LOCK_RELEASE(); LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot)); if (fFilter) { LOG(("Matching: This device SHOULD be filtered")); /* this device needs to be filtered, but it's not, * leave the PDO in array to issue a replug request for it * later on */ continue; } } else { WARN(("vboxUsbFltDevPopulate for PDO 0x%p failed with Status 0x%x", pDevObj, Status)); } LOG(("Matching: This device should NOT be filtered")); /* this device should not be filtered, and it's not */ ObDereferenceObject(pDevObj); pDevRelations->Objects[k] = NULL; --cReplugPdos; ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos is %d", cReplugPdos)); } LOG(("(%d) non-matched PDOs to be replugged", cReplugPdos)); if (cReplugPdos) { for (ULONG k = 0; k < pDevRelations->Count; ++k) { if (!pDevRelations->Objects[k]) continue; Status = vboxUsbFltPdoReplug(pDevRelations->Objects[k]); ASSERT_WARN(Status == STATUS_SUCCESS, ("vboxUsbFltPdoReplug ailed Status(0x%x)", Status)); ObDereferenceObject(pDevRelations->Objects[k]); if (!--cReplugPdos) break; } ASSERT_WARN(!cReplugPdos, ("cReplugPdosreached zero!")); } vboxUsbFltReplugList(&ReplugDevList); ExFreePool(pDevRelations); } else { WARN(("VBoxUsbMonQueryBusRelations failed for DO(0x%p), Status(0x%x), pDevRelations(0x%p)", pTopDo, Status, pDevRelations)); } LOG(("Done Visiting pFile(0x%p), pTopDo(0x%p), pHubDo(0x%p), oContext(0x%p)", pFile, pTopDo, pHubDo, pContext)); return TRUE; } NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext) { KIRQL Irql = KeGetCurrentIrql(); ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql)); LOG(("Running filters, Context (0x%p)..", pContext)); VBOXUSBFLTCHECKWALKER Data; Data.pContext = pContext; vboxUsbMonHubDevWalk(vboxUsbFltFilterCheckWalker, &Data, VBOXUSBMONHUBWALK_F_FDO); LOG(("DONE Running filters, Context (0x%p)", pContext)); return STATUS_SUCCESS; } NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext) { LOG(("Closing context(0x%p)", pContext)); LIST_ENTRY ReplugDevList; InitializeListHead(&ReplugDevList); ASSERT_WARN(pContext, ("null context")); KIRQL Irql = KeGetCurrentIrql(); ASSERT_WARN(Irql == PASSIVE_LEVEL, ("irql==(%d)", Irql)); VBOXUSBFLT_LOCK_ACQUIRE(); uint32_t cActiveFilters = pContext->cActiveFilters; pContext->bRemoved = TRUE; if (pContext->pChangeEvent) { LOG(("seting & closing change event (0x%p)", pContext->pChangeEvent)); KeSetEvent(pContext->pChangeEvent, 0, /* increment*/ FALSE /* wait */); ObDereferenceObject(pContext->pChangeEvent); pContext->pChangeEvent = NULL; } else { LOG(("no change event")); } RemoveEntryList(&pContext->ListEntry); LOG(("removing owner filters")); /* now re-arrange the filters */ /* 1. remove filters */ VBoxUSBFilterRemoveOwner(pContext); LOG(("enumerating devices..")); /* 2. check if there are devices owned */ for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.DeviceList; pEntry = pEntry->Flink) { PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry); if (pDevice->pOwner != pContext) continue; LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)", pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot)); ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice)); ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice)); vboxUsbFltDevOwnerClearLocked(pDevice); if (vboxUsbFltDevCheckReplugLocked(pDevice, pContext)) { LOG(("device needs replug")); InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe); /* retain to ensure the device is not removed before we issue a replug */ vboxUsbFltDevRetain(pDevice); /* keep the PDO alive */ ObReferenceObject(pDevice->Pdo); } else { LOG(("device does NOT need replug")); } } VBOXUSBFLT_LOCK_RELEASE(); /* this should replug all devices that were either skipped or grabbed due to the context's */ vboxUsbFltReplugList(&ReplugDevList); LOG(("SUCCESS done context(0x%p)", pContext)); return STATUS_SUCCESS; } NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext) { LOG(("Creating context(0x%p)", pContext)); memset(pContext, 0, sizeof (*pContext)); pContext->Process = RTProcSelf(); VBOXUSBFLT_LOCK_ACQUIRE(); InsertHeadList(&g_VBoxUsbFltGlobals.ContextList, &pContext->ListEntry); VBOXUSBFLT_LOCK_RELEASE(); LOG(("SUCCESS context(0x%p)", pContext)); return STATUS_SUCCESS; } int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId) { LOG(("adding filter, Context (0x%p)..", pContext)); *pId = 0; /* LOG the filter details. */ LOG((__FUNCTION__": %s %s %s", USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "", USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "", USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "")); #ifdef VBOX_USB_WITH_VERBOSE_LOGGING LOG(("VBoxUSBClient::addFilter: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x Type%#x", USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID), USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS), USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL), USBFilterGetNum(pFilter, USBFILTERIDX_BUS), USBFilterGetNum(pFilter, USBFILTERIDX_PORT), USBFilterGetFilterType(pFilter))); #endif /* We can't get the bus/port numbers. Ignore them while matching. */ USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false); USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PORT, false); uintptr_t uId = 0; VBOXUSBFLT_LOCK_ACQUIRE(); /* Add the filter. */ int rc = VBoxUSBFilterAdd(pFilter, pContext, &uId); VBOXUSBFLT_LOCK_RELEASE(); if (RT_SUCCESS(rc)) { LOG(("ADDED filer id 0x%p", uId)); ASSERT_WARN(uId, ("uid is NULL")); #ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY VBoxUsbFltFilterCheck(); #endif } else { WARN(("VBoxUSBFilterAdd failed rc (%d)", rc)); ASSERT_WARN(!uId, ("uid is not NULL")); } *pId = uId; return rc; } int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId) { LOG(("removing filter id(0x%p), Context (0x%p)..", pContext, uId)); Assert(uId); VBOXUSBFLT_LOCK_ACQUIRE(); int rc = VBoxUSBFilterRemove(pContext, uId); if (!RT_SUCCESS(rc)) { WARN(("VBoxUSBFilterRemove failed rc (%d)", rc)); VBOXUSBFLT_LOCK_RELEASE(); return rc; } LOG(("enumerating devices..")); for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.DeviceList; pEntry = pEntry->Flink) { PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry); if (pDevice->fIsFilterOneShot) { ASSERT_WARN(!pDevice->uFltId, ("oneshot filter on device(0x%p): unexpected uFltId(%d)", pDevice, pDevice->uFltId)); } if (pDevice->uFltId != uId) continue; ASSERT_WARN(pDevice->pOwner == pContext, ("Device(0x%p) owner(0x%p) not match to (0x%p)", pDevice, pDevice->pOwner, pContext)); if (pDevice->pOwner != pContext) continue; LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)", pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot)); ASSERT_WARN(!pDevice->fIsFilterOneShot, ("device(0x%p) is filtered with a oneshot filter", pDevice)); pDevice->uFltId = 0; /* clear the fIsFilterOneShot flag to ensure the device is replugged on the next VBoxUsbFltFilterCheck call */ pDevice->fIsFilterOneShot = false; } VBOXUSBFLT_LOCK_RELEASE(); LOG(("done enumerating devices")); if (RT_SUCCESS(rc)) { #ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY VBoxUsbFltFilterCheck(); #endif } return rc; } NTSTATUS VBoxUsbFltSetNotifyEvent(PVBOXUSBFLTCTX pContext, HANDLE hEvent) { NTSTATUS Status = STATUS_SUCCESS; PKEVENT pEvent = NULL; PKEVENT pOldEvent = NULL; if (hEvent) { Status = ObReferenceObjectByHandle(hEvent, EVENT_MODIFY_STATE, *ExEventObjectType, UserMode, (PVOID*)&pEvent, NULL); Assert(Status == STATUS_SUCCESS); if (!NT_SUCCESS(Status)) return Status; } VBOXUSBFLT_LOCK_ACQUIRE(); pOldEvent = pContext->pChangeEvent; pContext->pChangeEvent = pEvent; VBOXUSBFLT_LOCK_RELEASE(); if (pOldEvent) { ObDereferenceObject(pOldEvent); } return STATUS_SUCCESS; } static USBDEVICESTATE vboxUsbDevGetUserState(PVBOXUSBFLTCTX pContext, PVBOXUSBFLT_DEVICE pDevice) { if (vboxUsbFltDevStateIsNotFiltered(pDevice)) return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; /* the device is filtered, or replugging */ if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING) { ASSERT_WARN(!pDevice->pOwner, ("replugging device(0x%p) still has an owner(0x%p)", pDevice, pDevice->pOwner)); ASSERT_WARN(!pDevice->uFltId, ("replugging device(0x%p) still has filter(0x%p)", pDevice, pDevice->uFltId)); /* no user state for this, we should not return it tu the user */ return USBDEVICESTATE_USED_BY_HOST; } /* the device is filtered, if owner differs from the context, return as USED_BY_HOST */ ASSERT_WARN(pDevice->pOwner, ("device(0x%p) has noowner", pDevice)); /* the id can be null if a filter is removed */ // Assert(pDevice->uFltId); if (pDevice->pOwner != pContext) { LOG(("Device owner differs from the current context, returning used by host")); return USBDEVICESTATE_USED_BY_HOST; } switch (pDevice->enmState) { case VBOXUSBFLT_DEVSTATE_UNCAPTURED: case VBOXUSBFLT_DEVSTATE_CAPTURING: return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE; case VBOXUSBFLT_DEVSTATE_CAPTURED: return USBDEVICESTATE_HELD_BY_PROXY; case VBOXUSBFLT_DEVSTATE_USED_BY_GUEST: return USBDEVICESTATE_USED_BY_GUEST; default: WARN(("unexpected device state(%d) for device(0x%p)", pDevice->enmState, pDevice)); return USBDEVICESTATE_UNSUPPORTED; } } static void vboxUsbDevToUserInfo(PVBOXUSBFLTCTX pContext, PVBOXUSBFLT_DEVICE pDevice, PUSBSUP_DEVINFO pDevInfo) { #if 0 pDevInfo->usVendorId = pDevice->idVendor; pDevInfo->usProductId = pDevice->idProduct; pDevInfo->usRevision = pDevice->bcdDevice; pDevInfo->enmState = vboxUsbDevGetUserState(pContext, pDevice); strcpy(pDevInfo->szDrvKeyName, pDevice->szDrvKeyName); if (pDevInfo->enmState == USBDEVICESTATE_HELD_BY_PROXY || pDevInfo->enmState == USBDEVICESTATE_USED_BY_GUEST) { /* this is the only case where we return the obj name to the client */ strcpy(pDevInfo->szObjName, pDevice->szObjName); } pDevInfo->fHighSpeed = pDevice->fHighSpeed; #endif } NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo) { Assert(hDevice); memset (pInfo, 0, sizeof (*pInfo)); VBOXUSBFLT_LOCK_ACQUIRE(); for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.DeviceList; pEntry = pEntry->Flink) { PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry); Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED); Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED); if (pDevice != hDevice) continue; USBDEVICESTATE enmUsrState = vboxUsbDevGetUserState(pContext, pDevice); pInfo->enmState = enmUsrState; VBOXUSBFLT_LOCK_RELEASE(); return STATUS_SUCCESS; } VBOXUSBFLT_LOCK_RELEASE(); /* this should not occur */ AssertFailed(); return STATUS_INVALID_PARAMETER; } NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered) { *pbFiltered = FALSE; PVBOXUSBFLT_DEVICE pDevice; /* first check if device is in the a already */ VBOXUSBFLT_LOCK_ACQUIRE(); pDevice = vboxUsbFltDevGetLocked(pPdo); if (pDevice) { LOG(("found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo)); ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice)); ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice)); *pbFiltered = pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING; VBOXUSBFLT_LOCK_RELEASE(); return STATUS_SUCCESS; } VBOXUSBFLT_LOCK_RELEASE(); pDevice = (PVBOXUSBFLT_DEVICE)VBoxUsbMonMemAllocZ(sizeof (*pDevice)); if (!pDevice) { WARN(("VBoxUsbMonMemAllocZ failed")); return STATUS_NO_MEMORY; } pDevice->enmState = VBOXUSBFLT_DEVSTATE_ADDED; pDevice->cRefs = 1; NTSTATUS Status = vboxUsbFltDevPopulate(pDevice, pPdo /* , TRUE /* need all props */); if (!NT_SUCCESS(Status)) { WARN(("vboxUsbFltDevPopulate failed, Status 0x%x", Status)); VBoxUsbMonMemFree(pDevice); return Status; } uintptr_t uId; bool fFilter = false; bool fIsOneShot = false; PVBOXUSBFLTCTX pCtx; PVBOXUSBFLT_DEVICE pTmpDev; VBOXUSBFLT_LOCK_ACQUIRE(); /* (paranoia) re-check the device is still not here */ pTmpDev = vboxUsbFltDevGetLocked(pPdo); if (pTmpDev) { LOG(("second try: found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo)); ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("second try: VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice)); ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("second try: VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice)); *pbFiltered = pTmpDev->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING; VBOXUSBFLT_LOCK_RELEASE(); VBoxUsbMonMemFree(pDevice); return STATUS_SUCCESS; } LOG(("Created Device 0x%p for PDO 0x%p", pDevice, pPdo)); pCtx = vboxUsbFltDevMatchLocked(pDevice, &uId, true, /* remove a one-shot filter */ &fFilter, &fIsOneShot); LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot)); if (fFilter) { LOG(("Created Device 0x%p should be filtered", pDevice)); ASSERT_WARN(pCtx, ("zero ctx")); ASSERT_WARN(uId, ("zero uId")); pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING; } else { LOG(("Created Device 0x%p should NOT be filtered", pDevice)); ASSERT_WARN(!uId == !pCtx, ("invalid uid(0x%p) - ctx(0x%p) pair", uId, pCtx)); /* either both zero or both not */ pDevice->enmState = VBOXUSBFLT_DEVSTATE_UNCAPTURED; } if (pCtx) vboxUsbFltDevOwnerSetLocked(pDevice, pCtx, fIsOneShot ? 0 : uId, fIsOneShot); InsertHeadList(&g_VBoxUsbFltGlobals.DeviceList, &pDevice->GlobalLe); /* do not need to signal anything here - * going to do that once the proxy device object starts */ VBOXUSBFLT_LOCK_RELEASE(); *pbFiltered = fFilter; return STATUS_SUCCESS; } NTSTATUS VBoxUsbFltPdoAddCompleted(PDEVICE_OBJECT pPdo) { VBOXUSBFLT_LOCK_ACQUIRE(); vboxUsbFltSignalChangeLocked(); VBOXUSBFLT_LOCK_RELEASE(); return STATUS_SUCCESS; } BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo) { VBOXUSBFLT_DEVSTATE enmState = VBOXUSBFLT_DEVSTATE_REMOVED; VBOXUSBFLT_LOCK_ACQUIRE(); PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pPdo); if (pDevice) { enmState = pDevice->enmState; } VBOXUSBFLT_LOCK_RELEASE(); return enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING; } NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo) { PVBOXUSBFLT_DEVICE pDevice; VBOXUSBFLT_DEVSTATE enmOldState; VBOXUSBFLT_LOCK_ACQUIRE(); pDevice = vboxUsbFltDevGetLocked(pPdo); if (pDevice) { RemoveEntryList(&pDevice->GlobalLe); enmOldState = pDevice->enmState; pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED; if (enmOldState != VBOXUSBFLT_DEVSTATE_REPLUGGING) { vboxUsbFltSignalChangeLocked(); } else { /* the device *should* reappear, do signlling on re-appear only * to avoid extra signaling. still there might be a situation * when the device will not re-appear if it gets physically removed * before it re-appears * @todo: set a timer callback to do a notification from it */ } } VBOXUSBFLT_LOCK_RELEASE(); if (pDevice) vboxUsbFltDevRelease(pDevice); return STATUS_SUCCESS; } HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo) { PVBOXUSBFLT_DEVICE pDevice; VBOXUSBFLT_LOCK_ACQUIRE(); pDevice = vboxUsbFltDevGetLocked(pPdo); if (pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING) { pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURED; LOG(("The proxy notified proxy start for the captured device 0x%x", pDevice)); vboxUsbFltDevRetain(pDevice); vboxUsbFltSignalChangeLocked(); } else { WARN(("invalid state, %d", pDevice->enmState)); pDevice = NULL; } VBOXUSBFLT_LOCK_RELEASE(); return pDevice; } void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev) { PVBOXUSBFLT_DEVICE pDevice = (PVBOXUSBFLT_DEVICE)hDev; VBOXUSBFLT_LOCK_ACQUIRE(); if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST) { /* this is due to devie was physically removed */ LOG(("The proxy notified proxy stop for the captured device 0x%x, current state %d", pDevice, pDevice->enmState)); pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING; vboxUsbFltSignalChangeLocked(); } else { if(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REPLUGGING) { WARN(("invalid state, %d", pDevice->enmState)); } } VBOXUSBFLT_LOCK_RELEASE(); vboxUsbFltDevRelease(pDevice); } NTSTATUS VBoxUsbFltInit() { int rc = VBoxUSBFilterInit(); if (RT_FAILURE(rc)) { WARN(("VBoxUSBFilterInit failed, rc (%d)", rc)); return STATUS_UNSUCCESSFUL; } memset(&g_VBoxUsbFltGlobals, 0, sizeof (g_VBoxUsbFltGlobals)); InitializeListHead(&g_VBoxUsbFltGlobals.DeviceList); InitializeListHead(&g_VBoxUsbFltGlobals.ContextList); InitializeListHead(&g_VBoxUsbFltGlobals.BlackDeviceList); vboxUsbFltBlDevPopulateWithKnownLocked(); VBOXUSBFLT_LOCK_INIT(); return STATUS_SUCCESS; } NTSTATUS VBoxUsbFltTerm() { bool bBusy = false; VBOXUSBFLT_LOCK_ACQUIRE(); do { if (!IsListEmpty(&g_VBoxUsbFltGlobals.ContextList)) { AssertFailed(); bBusy = true; break; } PLIST_ENTRY pNext = NULL; for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.DeviceList; pEntry = pNext) { pNext = pEntry->Flink; PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry); Assert(!pDevice->uFltId); Assert(!pDevice->pOwner); if (pDevice->cRefs != 1) { AssertFailed(); bBusy = true; break; } } } while (0); VBOXUSBFLT_LOCK_RELEASE() if (bBusy) { return STATUS_DEVICE_BUSY; } for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink; pEntry != &g_VBoxUsbFltGlobals.DeviceList; pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink) { RemoveEntryList(pEntry); PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry); pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED; vboxUsbFltDevRelease(pDevice); } vboxUsbFltBlDevClearLocked(); VBOXUSBFLT_LOCK_TERM(); VBoxUSBFilterTerm(); return STATUS_SUCCESS; }