VirtualBox

source: vbox/trunk/src/VBox/HostDrivers/VBoxUSB/win/mon/VBoxUsbFlt.cpp@ 88675

Last change on this file since 88675 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 66.5 KB
Line 
1/* $Id: VBoxUsbFlt.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VBox USB Monitor Device Filtering functionality
4 */
5/*
6 * Copyright (C) 2011-2020 Oracle Corporation
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * The contents of this file may alternatively be used under the terms
17 * of the Common Development and Distribution License Version 1.0
18 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
19 * VirtualBox OSE distribution, in which case the provisions of the
20 * CDDL are applicable instead of those of the GPL.
21 *
22 * You may elect to license modified versions of this file under the
23 * terms and conditions of either the GPL or the CDDL or both.
24 */
25
26
27/*********************************************************************************************************************************
28* Header Files *
29*********************************************************************************************************************************/
30#include "VBoxUsbMon.h"
31#include "../cmn/VBoxUsbTool.h"
32
33#include <VBox/cdefs.h>
34#include <VBox/types.h>
35#include <iprt/process.h>
36#include <iprt/assert.h>
37#include <iprt/errcore.h>
38
39#include <iprt/assert.h>
40
41#pragma warning(disable : 4200)
42#include "usbdi.h"
43#pragma warning(default : 4200)
44#include "usbdlib.h"
45#include "VBoxUSBFilterMgr.h"
46#include <VBox/usblib.h>
47#include <devguid.h>
48#include <devpkey.h>
49
50
51/* We should be including ntifs.h but that's not as easy as it sounds. */
52extern "C" {
53NTKERNELAPI PDEVICE_OBJECT IoGetDeviceAttachmentBaseRef(__in PDEVICE_OBJECT DeviceObject);
54}
55
56/*
57 * state transitions:
58 *
59 * (we are not filtering this device )
60 * ADDED --> UNCAPTURED ------------------------------->-
61 * | |
62 * | (we are filtering this device, | (the device is being
63 * | waiting for our device driver | re-plugged to perform
64 * | to pick it up) | capture-uncapture transition)
65 * |-> CAPTURING -------------------------------->|---> REPLUGGING -----
66 * ^ | (device driver picked | |
67 * | | up the device) | (remove cased | (device is removed
68 * | ->---> CAPTURED ---------------------->| by "real" removal | the device info is removed form the list)
69 * | | |------------------->->--> REMOVED
70 * | | |
71 * |-----------<->---> USED_BY_GUEST ------->|
72 * | |
73 * |------------------------<-
74 *
75 * NOTE: the order of enums DOES MATTER!!
76 * Do not blindly modify!! as the code assumes the state is ordered this way.
77 */
78typedef enum
79{
80 VBOXUSBFLT_DEVSTATE_UNKNOWN = 0,
81 VBOXUSBFLT_DEVSTATE_REMOVED,
82 VBOXUSBFLT_DEVSTATE_REPLUGGING,
83 VBOXUSBFLT_DEVSTATE_ADDED,
84 VBOXUSBFLT_DEVSTATE_UNCAPTURED,
85 VBOXUSBFLT_DEVSTATE_CAPTURING,
86 VBOXUSBFLT_DEVSTATE_CAPTURED,
87 VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
88 VBOXUSBFLT_DEVSTATE_32BIT_HACK = 0x7fffffff
89} VBOXUSBFLT_DEVSTATE;
90
91typedef struct VBOXUSBFLT_DEVICE
92{
93 LIST_ENTRY GlobalLe;
94 /* auxiliary list to be used for gathering devices to be re-plugged
95 * only thread that puts the device to the REPLUGGING state can use this list */
96 LIST_ENTRY RepluggingLe;
97 /* Owning session. Each matched device has an owning session. */
98 struct VBOXUSBFLTCTX *pOwner;
99 /* filter id - if NULL AND device has an owner - the filter is destroyed */
100 uintptr_t uFltId;
101 /* true iff device is filtered with a one-shot filter */
102 bool fIsFilterOneShot;
103 /* true if descriptors could not be read and only inferred from PnP Manager data */
104 bool fInferredDesc;
105 /* The device state. If the non-owner session is requesting the state while the device is grabbed,
106 * the USBDEVICESTATE_USED_BY_HOST is returned. */
107 VBOXUSBFLT_DEVSTATE enmState;
108 volatile uint32_t cRefs;
109 PDEVICE_OBJECT Pdo;
110 uint16_t idVendor;
111 uint16_t idProduct;
112 uint16_t bcdDevice;
113 uint16_t bPort;
114 uint8_t bClass;
115 uint8_t bSubClass;
116 uint8_t bProtocol;
117 char szSerial[MAX_USB_SERIAL_STRING];
118 char szMfgName[MAX_USB_SERIAL_STRING];
119 char szProduct[MAX_USB_SERIAL_STRING];
120 WCHAR szLocationPath[768];
121#if 0
122 char szDrvKeyName[512];
123 BOOLEAN fHighSpeed;
124#endif
125} VBOXUSBFLT_DEVICE, *PVBOXUSBFLT_DEVICE;
126
127#define PVBOXUSBFLT_DEVICE_FROM_LE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, GlobalLe) ) )
128#define PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(_pLe) ( (PVBOXUSBFLT_DEVICE)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_DEVICE, RepluggingLe) ) )
129#define PVBOXUSBFLTCTX_FROM_LE(_pLe) ( (PVBOXUSBFLTCTX)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLTCTX, ListEntry) ) )
130
131typedef struct VBOXUSBFLT_LOCK
132{
133 KSPIN_LOCK Lock;
134 KIRQL OldIrql;
135} VBOXUSBFLT_LOCK, *PVBOXUSBFLT_LOCK;
136
137#define VBOXUSBFLT_LOCK_INIT() \
138 KeInitializeSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock)
139#define VBOXUSBFLT_LOCK_TERM() do { } while (0)
140#define VBOXUSBFLT_LOCK_ACQUIRE() \
141 KeAcquireSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, &g_VBoxUsbFltGlobals.Lock.OldIrql);
142#define VBOXUSBFLT_LOCK_RELEASE() \
143 KeReleaseSpinLock(&g_VBoxUsbFltGlobals.Lock.Lock, g_VBoxUsbFltGlobals.Lock.OldIrql);
144
145
146typedef struct VBOXUSBFLT_BLDEV
147{
148 LIST_ENTRY ListEntry;
149 uint16_t idVendor;
150 uint16_t idProduct;
151 uint16_t bcdDevice;
152} VBOXUSBFLT_BLDEV, *PVBOXUSBFLT_BLDEV;
153
154#define PVBOXUSBFLT_BLDEV_FROM_LE(_pLe) ( (PVBOXUSBFLT_BLDEV)( ((uint8_t*)(_pLe)) - RT_OFFSETOF(VBOXUSBFLT_BLDEV, ListEntry) ) )
155
156typedef struct VBOXUSBFLTGLOBALS
157{
158 LIST_ENTRY DeviceList;
159 LIST_ENTRY ContextList;
160 /* devices known to misbehave */
161 LIST_ENTRY BlackDeviceList;
162 VBOXUSBFLT_LOCK Lock;
163 /** Flag whether to force replugging a device we can't query descirptors from.
164 * Short term workaround for @bugref{9479}. */
165 ULONG dwForceReplugWhenDevPopulateFails;
166} VBOXUSBFLTGLOBALS, *PVBOXUSBFLTGLOBALS;
167static VBOXUSBFLTGLOBALS g_VBoxUsbFltGlobals;
168
169static bool vboxUsbFltBlDevMatchLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
170{
171 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
172 pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
173 pEntry = pEntry->Flink)
174 {
175 PVBOXUSBFLT_BLDEV pDev = PVBOXUSBFLT_BLDEV_FROM_LE(pEntry);
176 if (pDev->idVendor != idVendor)
177 continue;
178 if (pDev->idProduct != idProduct)
179 continue;
180 if (pDev->bcdDevice != bcdDevice)
181 continue;
182
183 return true;
184 }
185 return false;
186}
187
188static NTSTATUS vboxUsbFltBlDevAddLocked(uint16_t idVendor, uint16_t idProduct, uint16_t bcdDevice)
189{
190 if (vboxUsbFltBlDevMatchLocked(idVendor, idProduct, bcdDevice))
191 return STATUS_SUCCESS;
192 PVBOXUSBFLT_BLDEV pDev = (PVBOXUSBFLT_BLDEV)VBoxUsbMonMemAllocZ(sizeof (*pDev));
193 if (!pDev)
194 {
195 AssertFailed();
196 return STATUS_INSUFFICIENT_RESOURCES;
197 }
198
199 pDev->idVendor = idVendor;
200 pDev->idProduct = idProduct;
201 pDev->bcdDevice = bcdDevice;
202 InsertHeadList(&g_VBoxUsbFltGlobals.BlackDeviceList, &pDev->ListEntry);
203 return STATUS_SUCCESS;
204}
205
206static void vboxUsbFltBlDevClearLocked()
207{
208 PLIST_ENTRY pNext;
209 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.BlackDeviceList.Flink;
210 pEntry != &g_VBoxUsbFltGlobals.BlackDeviceList;
211 pEntry = pNext)
212 {
213 pNext = pEntry->Flink;
214 VBoxUsbMonMemFree(pEntry);
215 }
216}
217
218static void vboxUsbFltBlDevPopulateWithKnownLocked()
219{
220 /* this one halts when trying to get string descriptors from it */
221 vboxUsbFltBlDevAddLocked(0x5ac, 0x921c, 0x115);
222}
223
224
225DECLINLINE(void) vboxUsbFltDevRetain(PVBOXUSBFLT_DEVICE pDevice)
226{
227 Assert(pDevice->cRefs);
228 ASMAtomicIncU32(&pDevice->cRefs);
229}
230
231static void vboxUsbFltDevDestroy(PVBOXUSBFLT_DEVICE pDevice)
232{
233 Assert(!pDevice->cRefs);
234 Assert(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED);
235 VBoxUsbMonMemFree(pDevice);
236}
237
238DECLINLINE(void) vboxUsbFltDevRelease(PVBOXUSBFLT_DEVICE pDevice)
239{
240 uint32_t cRefs = ASMAtomicDecU32(&pDevice->cRefs);
241 Assert(cRefs < UINT32_MAX/2);
242 if (!cRefs)
243 {
244 vboxUsbFltDevDestroy(pDevice);
245 }
246}
247
248static void vboxUsbFltDevOwnerSetLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
249{
250 ASSERT_WARN(!pDevice->pOwner, ("device 0x%p has an owner(0x%p)", pDevice, pDevice->pOwner));
251 ++pContext->cActiveFilters;
252 pDevice->pOwner = pContext;
253 pDevice->uFltId = uFltId;
254 pDevice->fIsFilterOneShot = fIsOneShot;
255}
256
257static void vboxUsbFltDevOwnerClearLocked(PVBOXUSBFLT_DEVICE pDevice)
258{
259 ASSERT_WARN(pDevice->pOwner, ("no owner for device 0x%p", pDevice));
260 --pDevice->pOwner->cActiveFilters;
261 ASSERT_WARN(pDevice->pOwner->cActiveFilters < UINT32_MAX/2, ("cActiveFilters (%d)", pDevice->pOwner->cActiveFilters));
262 pDevice->pOwner = NULL;
263 pDevice->uFltId = 0;
264}
265
266static void vboxUsbFltDevOwnerUpdateLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext, uintptr_t uFltId, bool fIsOneShot)
267{
268 if (pDevice->pOwner != pContext)
269 {
270 if (pDevice->pOwner)
271 vboxUsbFltDevOwnerClearLocked(pDevice);
272 if (pContext)
273 vboxUsbFltDevOwnerSetLocked(pDevice, pContext, uFltId, fIsOneShot);
274 }
275 else if (pContext)
276 {
277 pDevice->uFltId = uFltId;
278 pDevice->fIsFilterOneShot = fIsOneShot;
279 }
280}
281
282static PVBOXUSBFLT_DEVICE vboxUsbFltDevGetLocked(PDEVICE_OBJECT pPdo)
283{
284#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
285 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
286 pEntry != &g_VBoxUsbFltGlobals.DeviceList;
287 pEntry = pEntry->Flink)
288 {
289 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
290 for (PLIST_ENTRY pEntry2 = pEntry->Flink;
291 pEntry2 != &g_VBoxUsbFltGlobals.DeviceList;
292 pEntry2 = pEntry2->Flink)
293 {
294 PVBOXUSBFLT_DEVICE pDevice2 = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry2);
295 ASSERT_WARN( pDevice->idVendor != pDevice2->idVendor
296 || pDevice->idProduct != pDevice2->idProduct
297 || pDevice->bcdDevice != pDevice2->bcdDevice, ("duplicate devices in a list!!"));
298 }
299 }
300#endif
301 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
302 pEntry != &g_VBoxUsbFltGlobals.DeviceList;
303 pEntry = pEntry->Flink)
304 {
305 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
306 ASSERT_WARN( pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
307 || pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED
308 || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING
309 || pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
310 || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST,
311 ("Invalid device state(%d) for device(0x%p) PDO(0x%p)", pDevice->enmState, pDevice, pDevice->Pdo));
312 if (pDevice->Pdo == pPdo)
313 return pDevice;
314 }
315 return NULL;
316}
317
318static NTSTATUS vboxUsbFltPdoReplug(PDEVICE_OBJECT pDo)
319{
320 LOG(("Replugging PDO(0x%p)", pDo));
321 NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_CYCLE_PORT, NULL, NULL);
322 ASSERT_WARN(Status == STATUS_SUCCESS, ("replugging PDO(0x%p) failed Status(0x%x)", pDo, Status));
323 LOG(("Replugging PDO(0x%p) done with Status(0x%x)", pDo, Status));
324 return Status;
325}
326
327static bool vboxUsbFltDevCanBeCaptured(PVBOXUSBFLT_DEVICE pDevice)
328{
329 if (pDevice->bClass == USB_DEVICE_CLASS_HUB)
330 {
331 LOG(("device (0x%p), pdo (0x%p) is a hub, can not be captured", pDevice, pDevice->Pdo));
332 return false;
333 }
334 return true;
335}
336
337static PVBOXUSBFLTCTX vboxUsbFltDevMatchLocked(PVBOXUSBFLT_DEVICE pDevice, uintptr_t *puId, bool fRemoveFltIfOneShot, bool *pfFilter, bool *pfIsOneShot)
338{
339 *puId = 0;
340 *pfFilter = false;
341 *pfIsOneShot = false;
342 if (!vboxUsbFltDevCanBeCaptured(pDevice))
343 {
344 LOG(("vboxUsbFltDevCanBeCaptured returned false"));
345 return NULL;
346 }
347
348 USBFILTER DevFlt;
349 USBFilterInit(&DevFlt, USBFILTERTYPE_CAPTURE);
350 USBFilterSetNumExact(&DevFlt, USBFILTERIDX_VENDOR_ID, pDevice->idVendor, true);
351 USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PRODUCT_ID, pDevice->idProduct, true);
352 USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_REV, pDevice->bcdDevice, true);
353
354 /* If we could not read a string descriptor, don't set the filter item at all. */
355 if (pDevice->szMfgName[0])
356 USBFilterSetStringExact(&DevFlt, USBFILTERIDX_MANUFACTURER_STR, pDevice->szMfgName, true /*fMustBePresent*/, true /*fPurge*/);
357 if (pDevice->szProduct[0])
358 USBFilterSetStringExact(&DevFlt, USBFILTERIDX_PRODUCT_STR, pDevice->szProduct, true /*fMustBePresent*/, true /*fPurge*/);
359 if (pDevice->szSerial[0])
360 USBFilterSetStringExact(&DevFlt, USBFILTERIDX_SERIAL_NUMBER_STR, pDevice->szSerial, true /*fMustBePresent*/, true /*fPurge*/);
361
362 /* If device descriptor had to be inferred from PnP Manager data, the class/subclass/protocol may be wrong.
363 * When Windows reports CompatibleIDs 'USB\Class_03&SubClass_00&Prot_00', the device descriptor might be
364 * reporting class 3 (HID), *or* the device descriptor might be reporting class 0 (specified by interface)
365 * and the device's interface reporting class 3. Ignore the class/subclass/protocol in such case, since
366 * we are more or less guaranteed to rely on VID/PID anyway.
367 * See @bugref{9479}.
368 */
369 if (pDevice->fInferredDesc)
370 {
371 LOG(("Device descriptor was not read, only inferred; ignoring class/subclass/protocol!"));
372 }
373 else
374 {
375 LOG(("Setting filter class/subclass/protocol %02X/%02X/%02X\n", pDevice->bClass, pDevice->bSubClass, pDevice->bProtocol));
376 USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_CLASS, pDevice->bClass, true);
377 USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_SUB_CLASS, pDevice->bSubClass, true);
378 USBFilterSetNumExact(&DevFlt, USBFILTERIDX_DEVICE_PROTOCOL, pDevice->bProtocol, true);
379 }
380
381 /* If the port number looks valid, add it to the filter. */
382 if (pDevice->bPort)
383 {
384 LOG(("Setting filter port %04X\n", pDevice->bPort));
385 USBFilterSetNumExact(&DevFlt, USBFILTERIDX_PORT, pDevice->bPort, true);
386 }
387 else
388 LOG(("Port number not known, ignoring!"));
389
390 /* Run filters on the thing. */
391 PVBOXUSBFLTCTX pOwner = VBoxUSBFilterMatchEx(&DevFlt, puId, fRemoveFltIfOneShot, pfFilter, pfIsOneShot);
392 USBFilterDelete(&DevFlt);
393 return pOwner;
394}
395
396static void vboxUsbFltDevStateMarkReplugLocked(PVBOXUSBFLT_DEVICE pDevice)
397{
398 vboxUsbFltDevOwnerUpdateLocked(pDevice, NULL, 0, false);
399 pDevice->enmState = VBOXUSBFLT_DEVSTATE_REPLUGGING;
400}
401
402static bool vboxUsbFltDevStateIsNotFiltered(PVBOXUSBFLT_DEVICE pDevice)
403{
404 return pDevice->enmState == VBOXUSBFLT_DEVSTATE_UNCAPTURED;
405}
406
407static bool vboxUsbFltDevStateIsFiltered(PVBOXUSBFLT_DEVICE pDevice)
408{
409 return pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
410}
411
412static uint16_t vboxUsbParseHexNumU16(WCHAR **ppStr)
413{
414 WCHAR *pStr = *ppStr;
415 WCHAR wc;
416 uint16_t num = 0;
417 unsigned u;
418
419 for (int i = 0; i < 4; ++i)
420 {
421 if (!*pStr) /* Just in case the string is too short. */
422 break;
423
424 wc = *pStr;
425 u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
426 num |= u << (12 - 4 * i);
427 pStr++;
428 }
429 *ppStr = pStr;
430
431 return num;
432}
433
434static uint8_t vboxUsbParseHexNumU8(WCHAR **ppStr)
435{
436 WCHAR *pStr = *ppStr;
437 WCHAR wc;
438 uint16_t num = 0;
439 unsigned u;
440
441 for (int i = 0; i < 2; ++i)
442 {
443 if (!*pStr) /* Just in case the string is too short. */
444 break;
445
446 wc = *pStr;
447 u = wc >= 'A' ? wc - 'A' + 10 : wc - '0'; /* Hex digit to number. */
448 num |= u << (4 - 4 * i);
449 pStr++;
450 }
451 *ppStr = pStr;
452
453 return num;
454}
455
456static bool vboxUsbParseHardwareID(WCHAR *pchIdStr, uint16_t *pVid, uint16_t *pPid, uint16_t *pRev)
457{
458#define VID_PREFIX L"USB\\VID_"
459#define PID_PREFIX L"&PID_"
460#define REV_PREFIX L"&REV_"
461
462 *pVid = *pPid = *pRev = 0xFFFF;
463
464 /* The Hardware ID is in the format USB\VID_xxxx&PID_xxxx&REV_xxxx, with 'xxxx'
465 * being 16-bit hexadecimal numbers. The string is coming from the
466 * Windows PnP manager so OEMs should have no opportunity to mess it up.
467 */
468
469 if (wcsncmp(pchIdStr, VID_PREFIX, wcslen(VID_PREFIX)))
470 return false;
471 /* Point to the start of the vendor ID number and parse it. */
472 pchIdStr += wcslen(VID_PREFIX);
473 *pVid = vboxUsbParseHexNumU16(&pchIdStr);
474
475 if (wcsncmp(pchIdStr, PID_PREFIX, wcslen(PID_PREFIX)))
476 return false;
477 /* Point to the start of the product ID number and parse it. */
478 pchIdStr += wcslen(PID_PREFIX);
479 *pPid = vboxUsbParseHexNumU16(&pchIdStr);
480
481 /* The revision might not be there; the Windows documentation is not
482 * entirely clear if it will be always present for USB devices or not.
483 * If it's not there, still consider this a success. */
484 if (wcsncmp(pchIdStr, REV_PREFIX, wcslen(REV_PREFIX)))
485 return true;
486
487 /* Point to the start of the revision number and parse it. */
488 pchIdStr += wcslen(REV_PREFIX);
489 *pRev = vboxUsbParseHexNumU16(&pchIdStr);
490
491 return true;
492#undef VID_PREFIX
493#undef PID_PREFIX
494#undef REV_PREFIX
495}
496
497static bool vboxUsbParseCompatibleIDs(WCHAR *pchIdStr, uint8_t *pClass, uint8_t *pSubClass, uint8_t *pProt)
498{
499#define CLS_PREFIX L"USB\\Class_"
500#define SUB_PREFIX L"&SubClass_"
501#define PRO_PREFIX L"&Prot_"
502
503 *pClass = *pSubClass = *pProt = 0xFF;
504
505 /* The Compatible IDs string is in the format USB\Class_xx&SubClass_xx&Prot_xx,
506 * with 'xx' being 8-bit hexadecimal numbers. Since this string is provided by the
507 * PnP manager and USB devices always report these as part of the basic USB device
508 * descriptor, we assume all three must be present.
509 */
510
511 if (wcsncmp(pchIdStr, CLS_PREFIX, wcslen(CLS_PREFIX)))
512 return false;
513 /* Point to the start of the device class and parse it. */
514 pchIdStr += wcslen(CLS_PREFIX);
515 *pClass = vboxUsbParseHexNumU8(&pchIdStr);
516
517 if (wcsncmp(pchIdStr, SUB_PREFIX, wcslen(SUB_PREFIX)))
518 return false;
519
520 /* Point to the start of the subclass and parse it. */
521 pchIdStr += wcslen(SUB_PREFIX);
522 *pSubClass = vboxUsbParseHexNumU8(&pchIdStr);
523
524 if (wcsncmp(pchIdStr, PRO_PREFIX, wcslen(PRO_PREFIX)))
525 return false;
526
527 /* Point to the start of the protocol and parse it. */
528 pchIdStr += wcslen(PRO_PREFIX);
529 *pProt = vboxUsbParseHexNumU8(&pchIdStr);
530
531 return true;
532#undef CLS_PREFIX
533#undef SUB_PREFIX
534#undef PRO_PREFIX
535}
536
537#define VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS 10000
538
539static NTSTATUS vboxUsbFltDevPopulate(PVBOXUSBFLT_DEVICE pDevice, PDEVICE_OBJECT pDo /*, BOOLEAN bPopulateNonFilterProps*/)
540{
541 NTSTATUS Status;
542 USB_TOPOLOGY_ADDRESS TopoAddr;
543 PUSB_DEVICE_DESCRIPTOR pDevDr = 0;
544 ULONG ulResultLen;
545 DEVPROPTYPE type;
546 WCHAR wchPropBuf[256];
547 uint16_t port;
548 bool rc;
549
550 pDevice->Pdo = pDo;
551
552 LOG(("Populating Device(0x%p) for PDO(0x%p)", pDevice, pDo));
553
554 pDevDr = (PUSB_DEVICE_DESCRIPTOR)VBoxUsbMonMemAllocZ(sizeof(*pDevDr));
555 if (pDevDr == NULL)
556 {
557 WARN(("Failed to alloc mem for urb"));
558 return STATUS_INSUFFICIENT_RESOURCES;
559 }
560
561 do
562 {
563 pDevice->fInferredDesc = false;
564 Status = VBoxUsbToolGetDescriptor(pDo, pDevDr, sizeof(*pDevDr), USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
565 if (!NT_SUCCESS(Status))
566 {
567 uint16_t vid, pid, rev;
568 uint8_t cls, sub, prt;
569
570 WARN(("getting device descriptor failed, Status (0x%x); falling back to IoGetDeviceProperty", Status));
571
572 /* Try falling back to IoGetDevicePropertyData. */
573 Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_HardwareIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
574 if (!NT_SUCCESS(Status))
575 {
576 /* This just isn't our day. We have no idea what the device is. */
577 WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_HardwareIds, Status (0x%x)", Status));
578 break;
579 }
580 rc = vboxUsbParseHardwareID(wchPropBuf, &vid, &pid, &rev);
581 if (!rc)
582 {
583 /* This *really* should not happen. */
584 WARN(("Failed to parse Hardware ID"));
585 break;
586 }
587
588 /* Now grab the Compatible IDs to get the class/subclass/protocol. */
589 Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_CompatibleIds, LOCALE_NEUTRAL, 0, sizeof(wchPropBuf), wchPropBuf, &ulResultLen, &type);
590 if (!NT_SUCCESS(Status))
591 {
592 /* We really kind of need these. */
593 WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_CompatibleIds, Status (0x%x)", Status));
594 break;
595 }
596 rc = vboxUsbParseCompatibleIDs(wchPropBuf, &cls, &sub, &prt);
597 if (!rc)
598 {
599 /* This *really* should not happen. */
600 WARN(("Failed to parse Hardware ID"));
601 break;
602 }
603
604 LOG(("Parsed HardwareID: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
605 if (vid == 0xFFFF || pid == 0xFFFF)
606 break;
607
608 LOG(("Successfully fell back to IoGetDeviceProperty result"));
609 pDevDr->idVendor = vid;
610 pDevDr->idProduct = pid;
611 pDevDr->bcdDevice = rev;
612 pDevDr->bDeviceClass = cls;
613 pDevDr->bDeviceSubClass = sub;
614 pDevDr->bDeviceProtocol = prt;
615
616 /* The USB device class/subclass/protocol may not be accurate. We have to be careful when comparing
617 * and not take mismatches too seriously.
618 */
619 pDevice->fInferredDesc = true;
620 }
621
622 /* Query the location path. The path is purely a function of the physical device location
623 * and does not change if the device changes, and also does not change depending on
624 * whether the device is captured or not.
625 * NB: We ignore any additional strings and only look at the first one.
626 */
627 Status = IoGetDevicePropertyData(pDo, &DEVPKEY_Device_LocationPaths, LOCALE_NEUTRAL, 0, sizeof(pDevice->szLocationPath), pDevice->szLocationPath, &ulResultLen, &type);
628 if (!NT_SUCCESS(Status))
629 {
630 /* We do need this, but not critically. On Windows 7, we may get STATUS_OBJECT_NAME_NOT_FOUND. */
631 WARN(("IoGetDevicePropertyData failed for DEVPKEY_Device_LocationPaths, Status (0x%x)", Status));
632 }
633 else
634 {
635 LOG_STRW(pDevice->szLocationPath);
636 }
637
638 // Disabled, but could be used as a fallback instead of IoGetDevicePropertyData; it should work even
639 // when this code is entered from the PnP IRP processing path.
640#if 0
641 {
642 HUB_DEVICE_CONFIG_INFO HubInfo;
643
644 memset(&HubInfo, 0, sizeof(HubInfo));
645 HubInfo.Version = 1;
646 HubInfo.Length = sizeof(HubInfo);
647
648 NTSTATUS Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_DEVICE_CONFIG_INFO, &HubInfo, NULL);
649 ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_DEVICE_CONFIG_INFO for PDO(0x%p) failed Status(0x%x)", pDo, Status));
650 LOG(("Querying hub device config info for PDO(0x%p) done with Status(0x%x)", pDo, Status));
651
652 if (Status == STATUS_SUCCESS)
653 {
654 uint16_t vid, pid, rev;
655 uint8_t cls, sub, prt;
656
657 LOG(("Hub flags: %X\n", HubInfo.HubFlags));
658 LOG_STRW(HubInfo.HardwareIds.Buffer);
659 LOG_STRW(HubInfo.CompatibleIds.Buffer);
660 if (HubInfo.DeviceDescription.Buffer)
661 LOG_STRW(HubInfo.DeviceDescription.Buffer);
662
663 rc = vboxUsbParseHardwareID(HubInfo.HardwareIds.Buffer, &pid, &vid, &rev);
664 if (!rc)
665 {
666 /* This *really* should not happen. */
667 WARN(("Failed to parse Hardware ID"));
668 }
669
670 /* The CompatibleID the IOCTL gives is not always the same as what the PnP Manager uses
671 * (thanks, Microsoft). It might look like "USB\DevClass_00&SubClass_00&Prot_00" or like
672 * "USB\USB30_HUB". In such cases, we must consider the class/subclass/protocol
673 * information simply unavailable.
674 */
675 rc = vboxUsbParseCompatibleIDs(HubInfo.CompatibleIds.Buffer, &cls, &sub, &prt);
676 if (!rc)
677 {
678 /* This is unfortunate but not fatal. */
679 WARN(("Failed to parse Compatible ID"));
680 }
681 LOG(("Parsed HardwareID from IOCTL: vid=%04X, pid=%04X, rev=%04X, class=%02X, subcls=%02X, prot=%02X", vid, pid, rev, cls, sub, prt));
682
683 ExFreePool(HubInfo.HardwareIds.Buffer);
684 ExFreePool(HubInfo.CompatibleIds.Buffer);
685 if (HubInfo.DeviceDescription.Buffer)
686 ExFreePool(HubInfo.DeviceDescription.Buffer);
687 }
688 }
689#endif
690
691 /* Query the topology address from the hub driver. This is not trivial to translate to the location
692 * path, but at least we can get the port number this way.
693 */
694 memset(&TopoAddr, 0, sizeof(TopoAddr));
695 Status = VBoxUsbToolIoInternalCtlSendSync(pDo, IOCTL_INTERNAL_USB_GET_TOPOLOGY_ADDRESS, &TopoAddr, NULL);
696 ASSERT_WARN(Status == STATUS_SUCCESS, ("GET_TOPOLOGY_ADDRESS for PDO(0x%p) failed Status(0x%x)", pDo, Status));
697 LOG(("Querying topology address for PDO(0x%p) done with Status(0x%x)", pDo, Status));
698
699 port = 0;
700 if (Status == STATUS_SUCCESS)
701 {
702 uint16_t *pPort = &TopoAddr.RootHubPortNumber;
703
704 /* The last non-zero port number is the one we're looking for. It might be on the
705 * root hub directly, or on some downstream hub.
706 */
707 for (int i = 0; i < RT_ELEMENTS(TopoAddr.HubPortNumber) + 1; ++i) {
708 if (*pPort)
709 port = *pPort;
710 pPort++;
711 }
712 LOG(("PCI bus/dev/fn: %02X:%02X:%02X, parsed port: %u\n", TopoAddr.PciBusNumber, TopoAddr.PciDeviceNumber, TopoAddr.PciFunctionNumber, port));
713 LOG(("RH port: %u, hub ports: %u/%u/%u/%u/%u/%u\n", TopoAddr.RootHubPortNumber, TopoAddr.HubPortNumber[0],
714 TopoAddr.HubPortNumber[1], TopoAddr.HubPortNumber[2], TopoAddr.HubPortNumber[3], TopoAddr.HubPortNumber[4], TopoAddr.HubPortNumber[5]));
715
716 /* In the extremely unlikely case that the port number does not fit into 8 bits, force
717 * it to zero to indicate that we can't use it.
718 */
719 if (port > 255)
720 port = 0;
721 }
722
723 if (vboxUsbFltBlDevMatchLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice))
724 {
725 WARN(("found a known black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
726 Status = STATUS_UNSUCCESSFUL;
727 break;
728 }
729
730 LOG(("Device pid=%x vid=%x rev=%x port=%x", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice, port));
731 pDevice->bPort = port;
732 pDevice->idVendor = pDevDr->idVendor;
733 pDevice->idProduct = pDevDr->idProduct;
734 pDevice->bcdDevice = pDevDr->bcdDevice;
735 pDevice->bClass = pDevDr->bDeviceClass;
736 pDevice->bSubClass = pDevDr->bDeviceSubClass;
737 pDevice->bProtocol = pDevDr->bDeviceProtocol;
738 pDevice->szSerial[0] = 0;
739 pDevice->szMfgName[0] = 0;
740 pDevice->szProduct[0] = 0;
741
742 /* If there are no strings, don't even try to get any string descriptors. */
743 if (pDevDr->iSerialNumber || pDevDr->iManufacturer || pDevDr->iProduct)
744 {
745 int langId;
746
747 Status = VBoxUsbToolGetLangID(pDo, &langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
748 if (!NT_SUCCESS(Status))
749 {
750 WARN(("reading language ID failed"));
751 if (Status == STATUS_CANCELLED)
752 {
753 WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
754 vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
755 Status = STATUS_UNSUCCESSFUL;
756 }
757 break;
758 }
759
760 if (pDevDr->iSerialNumber)
761 {
762 Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szSerial, sizeof (pDevice->szSerial), pDevDr->iSerialNumber, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
763 if (!NT_SUCCESS(Status))
764 {
765 WARN(("reading serial number failed"));
766 ASSERT_WARN(pDevice->szSerial[0] == '\0', ("serial is not zero!!"));
767 if (Status == STATUS_CANCELLED)
768 {
769 WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
770 vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
771 Status = STATUS_UNSUCCESSFUL;
772 break;
773 }
774 LOG(("pretending success.."));
775 Status = STATUS_SUCCESS;
776 }
777 }
778
779 if (pDevDr->iManufacturer)
780 {
781 Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szMfgName, sizeof (pDevice->szMfgName), pDevDr->iManufacturer, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
782 if (!NT_SUCCESS(Status))
783 {
784 WARN(("reading manufacturer name failed"));
785 ASSERT_WARN(pDevice->szMfgName[0] == '\0', ("szMfgName is not zero!!"));
786 if (Status == STATUS_CANCELLED)
787 {
788 WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
789 vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
790 Status = STATUS_UNSUCCESSFUL;
791 break;
792 }
793 LOG(("pretending success.."));
794 Status = STATUS_SUCCESS;
795 }
796 }
797
798 if (pDevDr->iProduct)
799 {
800 Status = VBoxUsbToolGetStringDescriptor(pDo, pDevice->szProduct, sizeof (pDevice->szProduct), pDevDr->iProduct, langId, VBOXUSBMON_POPULATE_REQUEST_TIMEOUT_MS);
801 if (!NT_SUCCESS(Status))
802 {
803 WARN(("reading product name failed"));
804 ASSERT_WARN(pDevice->szProduct[0] == '\0', ("szProduct is not zero!!"));
805 if (Status == STATUS_CANCELLED)
806 {
807 WARN(("found a new black list device, vid(0x%x), pid(0x%x), rev(0x%x)", pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice));
808 vboxUsbFltBlDevAddLocked(pDevDr->idVendor, pDevDr->idProduct, pDevDr->bcdDevice);
809 Status = STATUS_UNSUCCESSFUL;
810 break;
811 }
812 LOG(("pretending success.."));
813 Status = STATUS_SUCCESS;
814 }
815 }
816
817 LOG((": strings: '%s':'%s':'%s' (lang ID %x)",
818 pDevice->szMfgName, pDevice->szProduct, pDevice->szSerial, langId));
819 }
820
821 LOG(("Populating Device(0x%p) for PDO(0x%p) Succeeded", pDevice, pDo));
822 Status = STATUS_SUCCESS;
823 } while (0);
824
825 VBoxUsbMonMemFree(pDevDr);
826 LOG(("Populating Device(0x%p) for PDO(0x%p) Done, Status (0x%x)", pDevice, pDo, Status));
827 return Status;
828}
829
830static bool vboxUsbFltDevCheckReplugLocked(PVBOXUSBFLT_DEVICE pDevice, PVBOXUSBFLTCTX pContext)
831{
832 ASSERT_WARN(pContext, ("context is NULL!"));
833
834 LOG(("Current context is (0x%p)", pContext));
835 LOG(("Current Device owner is (0x%p)", pDevice->pOwner));
836
837 /* check if device is already replugging */
838 if (pDevice->enmState <= VBOXUSBFLT_DEVSTATE_ADDED)
839 {
840 LOG(("Device (0x%p) is already replugging, return..", pDevice));
841 /* it is, do nothing */
842 ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING,
843 ("Device (0x%p) state is NOT REPLUGGING (%d)", pDevice, pDevice->enmState));
844 return false;
845 }
846
847 if (pDevice->pOwner && pContext != pDevice->pOwner)
848 {
849 LOG(("Device (0x%p) is owned by another context(0x%p), current is(0x%p)", pDevice, pDevice->pOwner, pContext));
850 /* this device is owned by another context, we're not allowed to do anything */
851 return false;
852 }
853
854 uintptr_t uId = 0;
855 bool bNeedReplug = false;
856 bool fFilter = false;
857 bool fIsOneShot = false;
858 PVBOXUSBFLTCTX pNewOwner = vboxUsbFltDevMatchLocked(pDevice, &uId,
859 false, /* do not remove a one-shot filter */
860 &fFilter, &fIsOneShot);
861 LOG(("Matching Info: Filter (0x%p), NewOwner(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pNewOwner, (int)fFilter, (int)fIsOneShot));
862 if (pDevice->pOwner && pNewOwner && pDevice->pOwner != pNewOwner)
863 {
864 LOG(("Matching: Device (0x%p) is requested another owner(0x%p), current is(0x%p)", pDevice, pNewOwner, pDevice->pOwner));
865 /* the device is owned by another owner, we can not change the owner here */
866 return false;
867 }
868
869 if (!fFilter)
870 {
871 LOG(("Matching: Device (0x%p) should NOT be filtered", pDevice));
872 /* the device should NOT be filtered, check the current state */
873 if (vboxUsbFltDevStateIsNotFiltered(pDevice))
874 {
875 LOG(("Device (0x%p) is NOT filtered", pDevice));
876 /* no changes */
877 if (fIsOneShot)
878 {
879 ASSERT_WARN(pNewOwner, ("no new owner"));
880 LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
881 /* remove a one-shot filter and keep the original filter data */
882 int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
883 ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
884 if (!pDevice->pOwner)
885 {
886 LOG(("Matching: updating the one-shot owner to (0x%p), fltId(0x%p)", pNewOwner, uId));
887 /* update owner for one-shot if the owner is changed (i.e. assigned) */
888 vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, true);
889 }
890 else
891 {
892 LOG(("Matching: device already has owner (0x%p) assigned", pDevice->pOwner));
893 }
894 }
895 else
896 {
897 LOG(("Matching: This is NOT a one-shot filter (0x%p), newOwner(0x%p)", uId, pNewOwner));
898 if (pNewOwner)
899 {
900 vboxUsbFltDevOwnerUpdateLocked(pDevice, pNewOwner, uId, false);
901 }
902 }
903 }
904 else
905 {
906 LOG(("Device (0x%p) IS filtered", pDevice));
907 /* the device is currently filtered, we should release it only if
908 * 1. device does not have an owner
909 * or
910 * 2. it should be released bue to a one-shot filter
911 * or
912 * 3. it is NOT grabbed by a one-shot filter */
913 if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
914 {
915 LOG(("Matching: Need replug"));
916 bNeedReplug = true;
917 }
918 }
919 }
920 else
921 {
922 LOG(("Matching: Device (0x%p) SHOULD be filtered", pDevice));
923 /* the device should be filtered, check the current state */
924 ASSERT_WARN(uId, ("zero uid"));
925 ASSERT_WARN(pNewOwner, ("zero pNewOwner"));
926 if (vboxUsbFltDevStateIsFiltered(pDevice))
927 {
928 LOG(("Device (0x%p) IS filtered", pDevice));
929 /* the device is filtered */
930 if (pNewOwner == pDevice->pOwner)
931 {
932 LOG(("Device owner match"));
933 /* no changes */
934 if (fIsOneShot)
935 {
936 LOG(("Matching: This is a one-shot filter (0x%p), removing..", uId));
937 /* remove a one-shot filter and keep the original filter data */
938 int tmpRc = VBoxUSBFilterRemove(pNewOwner, uId);
939 ASSERT_WARN(RT_SUCCESS(tmpRc), ("remove filter failed, rc (%d)", tmpRc));
940 }
941 else
942 {
943 LOG(("Matching: This is NOT a one-shot filter (0x%p), Owner(0x%p)", uId, pDevice->pOwner));
944 vboxUsbFltDevOwnerUpdateLocked(pDevice, pDevice->pOwner, uId, false);
945 }
946 }
947 else
948 {
949 ASSERT_WARN(!pDevice->pOwner, ("device should NOT have owner"));
950 LOG(("Matching: Need replug"));
951 /* the device needs to be filtered, but the owner changes, replug needed */
952 bNeedReplug = true;
953 }
954 }
955 else
956 {
957 /* the device is currently NOT filtered,
958 * we should replug it only if
959 * 1. device does not have an owner
960 * or
961 * 2. it should be captured due to a one-shot filter
962 * or
963 * 3. it is NOT released by a one-shot filter */
964 if (!pDevice->pOwner || fIsOneShot || !pDevice->fIsFilterOneShot)
965 {
966 bNeedReplug = true;
967 LOG(("Matching: Need replug"));
968 }
969 }
970 }
971
972 if (bNeedReplug)
973 {
974 LOG(("Matching: Device needs replugging, marking as such"));
975 vboxUsbFltDevStateMarkReplugLocked(pDevice);
976 }
977 else
978 {
979 LOG(("Matching: Device does NOT need replugging"));
980 }
981
982 return bNeedReplug;
983}
984
985static void vboxUsbFltReplugList(PLIST_ENTRY pList)
986{
987 PLIST_ENTRY pNext;
988 for (PLIST_ENTRY pEntry = pList->Flink;
989 pEntry != pList;
990 pEntry = pNext)
991 {
992 pNext = pEntry->Flink;
993 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_REPLUGGINGLE(pEntry);
994 LOG(("replugging matched PDO(0x%p), pDevice(0x%p)", pDevice->Pdo, pDevice));
995 ASSERT_WARN(pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING
996 || pDevice->enmState == VBOXUSBFLT_DEVSTATE_REMOVED,
997 ("invalid state(0x%x) for device(0x%p)", pDevice->enmState, pDevice));
998
999 vboxUsbFltPdoReplug(pDevice->Pdo);
1000 ObDereferenceObject(pDevice->Pdo);
1001 vboxUsbFltDevRelease(pDevice);
1002 }
1003}
1004
1005typedef struct VBOXUSBFLTCHECKWALKER
1006{
1007 PVBOXUSBFLTCTX pContext;
1008} VBOXUSBFLTCHECKWALKER, *PVBOXUSBFLTCHECKWALKER;
1009
1010static DECLCALLBACK(BOOLEAN) vboxUsbFltFilterCheckWalker(PFILE_OBJECT pHubFile,
1011 PDEVICE_OBJECT pHubDo, PVOID pvContext)
1012{
1013 PVBOXUSBFLTCHECKWALKER pData = (PVBOXUSBFLTCHECKWALKER)pvContext;
1014 PVBOXUSBFLTCTX pContext = pData->pContext;
1015
1016 LOG(("Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
1017 KIRQL Irql = KeGetCurrentIrql();
1018 ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
1019
1020 PDEVICE_RELATIONS pDevRelations = NULL;
1021
1022 NTSTATUS Status = VBoxUsbMonQueryBusRelations(pHubDo, pHubFile, &pDevRelations);
1023 if (Status == STATUS_SUCCESS && pDevRelations)
1024 {
1025 ULONG cReplugPdos = pDevRelations->Count;
1026 LIST_ENTRY ReplugDevList;
1027 InitializeListHead(&ReplugDevList);
1028 for (ULONG k = 0; k < pDevRelations->Count; ++k)
1029 {
1030 PDEVICE_OBJECT pDevObj;
1031
1032 /* Grab the PDO+reference. We won't need the upper layer device object
1033 * anymore, so dereference that right here, and drop the PDO ref later.
1034 */
1035 pDevObj = IoGetDeviceAttachmentBaseRef(pDevRelations->Objects[k]);
1036 LOG(("DevObj=%p, PDO=%p\n", pDevRelations->Objects[k], pDevObj));
1037 ObDereferenceObject(pDevRelations->Objects[k]);
1038 pDevRelations->Objects[k] = pDevObj;
1039
1040 LOG(("Found existing USB PDO 0x%p", pDevObj));
1041 VBOXUSBFLT_LOCK_ACQUIRE();
1042 PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pDevObj);
1043 if (pDevice)
1044 {
1045 LOG(("Found existing device info (0x%p) for PDO 0x%p", pDevice, pDevObj));
1046 bool bReplug = vboxUsbFltDevCheckReplugLocked(pDevice, pContext);
1047 if (bReplug)
1048 {
1049 LOG(("Replug needed for device (0x%p)", pDevice));
1050 InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
1051 vboxUsbFltDevRetain(pDevice);
1052 /* do not dereference object since we will use it later */
1053 }
1054 else
1055 {
1056 LOG(("Replug NOT needed for device (0x%p)", pDevice));
1057 ObDereferenceObject(pDevObj);
1058 }
1059
1060 VBOXUSBFLT_LOCK_RELEASE();
1061
1062 pDevRelations->Objects[k] = NULL;
1063 --cReplugPdos;
1064 ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos(%d) state broken", cReplugPdos));
1065 continue;
1066 }
1067 VBOXUSBFLT_LOCK_RELEASE();
1068
1069 LOG(("NO device info found for PDO 0x%p", pDevObj));
1070 VBOXUSBFLT_DEVICE Device;
1071 Status = vboxUsbFltDevPopulate(&Device, pDevObj /*, FALSE /* only need filter properties */);
1072 if (NT_SUCCESS(Status))
1073 {
1074 uintptr_t uId = 0;
1075 bool fFilter = false;
1076 bool fIsOneShot = false;
1077 VBOXUSBFLT_LOCK_ACQUIRE();
1078 PVBOXUSBFLTCTX pCtx = vboxUsbFltDevMatchLocked(&Device, &uId,
1079 false, /* do not remove a one-shot filter */
1080 &fFilter, &fIsOneShot);
1081 VBOXUSBFLT_LOCK_RELEASE();
1082 NOREF(pCtx);
1083 LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
1084 if (fFilter)
1085 {
1086 LOG(("Matching: This device SHOULD be filtered"));
1087 /* this device needs to be filtered, but it's not,
1088 * leave the PDO in array to issue a replug request for it
1089 * later on */
1090 continue;
1091 }
1092 }
1093 else
1094 {
1095 WARN(("vboxUsbFltDevPopulate for PDO 0x%p failed with Status 0x%x", pDevObj, Status));
1096 if ( Status == STATUS_CANCELLED
1097 && g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
1098 {
1099 /*
1100 * This can happen if the device got suspended and is in D3 state where we can't query any strings.
1101 * There is no known way to set the power state of the device, especially if there is no driver attached yet.
1102 * The sledgehammer approach is to just replug the device to force it out of suspend, see bugref @{9479}.
1103 */
1104 continue;
1105 }
1106 }
1107
1108 LOG(("Matching: This device should NOT be filtered"));
1109 /* this device should not be filtered, and it's not */
1110 ObDereferenceObject(pDevObj);
1111 pDevRelations->Objects[k] = NULL;
1112 --cReplugPdos;
1113 ASSERT_WARN((uint32_t)cReplugPdos < UINT32_MAX/2, ("cReplugPdos is %d", cReplugPdos));
1114 }
1115
1116 LOG(("(%d) non-matched PDOs to be replugged", cReplugPdos));
1117
1118 if (cReplugPdos)
1119 {
1120 for (ULONG k = 0; k < pDevRelations->Count; ++k)
1121 {
1122 if (!pDevRelations->Objects[k])
1123 continue;
1124
1125 Status = vboxUsbFltPdoReplug(pDevRelations->Objects[k]);
1126 ASSERT_WARN(Status == STATUS_SUCCESS, ("vboxUsbFltPdoReplug failed! Status(0x%x)", Status));
1127 ObDereferenceObject(pDevRelations->Objects[k]);
1128 if (!--cReplugPdos)
1129 break;
1130 }
1131
1132 ASSERT_WARN(!cReplugPdos, ("cReplugPdos reached zero!"));
1133 }
1134
1135 vboxUsbFltReplugList(&ReplugDevList);
1136
1137 ExFreePool(pDevRelations);
1138 }
1139 else
1140 {
1141 WARN(("VBoxUsbMonQueryBusRelations failed for hub DO(0x%p), Status(0x%x), pDevRelations(0x%p)",
1142 pHubDo, Status, pDevRelations));
1143 }
1144
1145 LOG(("Done Visiting pHubFile(0x%p), pHubDo(0x%p), oContext(0x%p)", pHubFile, pHubDo, pContext));
1146
1147 return TRUE;
1148}
1149
1150NTSTATUS VBoxUsbFltFilterCheck(PVBOXUSBFLTCTX pContext)
1151{
1152 KIRQL Irql = KeGetCurrentIrql();
1153 ASSERT_WARN(Irql == PASSIVE_LEVEL, ("unexpected IRQL (%d)", Irql));
1154
1155 LOG(("Running filters, Context (0x%p)..", pContext));
1156
1157 VBOXUSBFLTCHECKWALKER Data;
1158 Data.pContext = pContext;
1159 vboxUsbMonHubDevWalk(vboxUsbFltFilterCheckWalker, &Data);
1160
1161 LOG(("DONE Running filters, Context (0x%p)", pContext));
1162
1163 return STATUS_SUCCESS;
1164}
1165
1166NTSTATUS VBoxUsbFltClose(PVBOXUSBFLTCTX pContext)
1167{
1168 LOG(("Closing context(0x%p)", pContext));
1169 LIST_ENTRY ReplugDevList;
1170 InitializeListHead(&ReplugDevList);
1171
1172 ASSERT_WARN(pContext, ("null context"));
1173
1174 KIRQL Irql = KeGetCurrentIrql();
1175 ASSERT_WARN(Irql == PASSIVE_LEVEL, ("irql==(%d)", Irql));
1176
1177 VBOXUSBFLT_LOCK_ACQUIRE();
1178
1179 pContext->bRemoved = TRUE;
1180 RemoveEntryList(&pContext->ListEntry);
1181
1182 LOG(("removing owner filters"));
1183 /* now re-arrange the filters */
1184 /* 1. remove filters */
1185 VBoxUSBFilterRemoveOwner(pContext);
1186
1187 LOG(("enumerating devices.."));
1188 /* 2. check if there are devices owned */
1189 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
1190 pEntry != &g_VBoxUsbFltGlobals.DeviceList;
1191 pEntry = pEntry->Flink)
1192 {
1193 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
1194 if (pDevice->pOwner != pContext)
1195 continue;
1196
1197 LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
1198 pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
1199 ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
1200 ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
1201
1202 vboxUsbFltDevOwnerClearLocked(pDevice);
1203
1204 if (vboxUsbFltDevCheckReplugLocked(pDevice, pContext))
1205 {
1206 LOG(("device needs replug"));
1207 InsertHeadList(&ReplugDevList, &pDevice->RepluggingLe);
1208 /* retain to ensure the device is not removed before we issue a replug */
1209 vboxUsbFltDevRetain(pDevice);
1210 /* keep the PDO alive */
1211 ObReferenceObject(pDevice->Pdo);
1212 }
1213 else
1214 {
1215 LOG(("device does NOT need replug"));
1216 }
1217 }
1218
1219 VBOXUSBFLT_LOCK_RELEASE();
1220
1221 /* this should replug all devices that were either skipped or grabbed due to the context's */
1222 vboxUsbFltReplugList(&ReplugDevList);
1223
1224 LOG(("SUCCESS done context(0x%p)", pContext));
1225 return STATUS_SUCCESS;
1226}
1227
1228NTSTATUS VBoxUsbFltCreate(PVBOXUSBFLTCTX pContext)
1229{
1230 LOG(("Creating context(0x%p)", pContext));
1231 memset(pContext, 0, sizeof (*pContext));
1232 pContext->Process = RTProcSelf();
1233 VBOXUSBFLT_LOCK_ACQUIRE();
1234 InsertHeadList(&g_VBoxUsbFltGlobals.ContextList, &pContext->ListEntry);
1235 VBOXUSBFLT_LOCK_RELEASE();
1236 LOG(("SUCCESS context(0x%p)", pContext));
1237 return STATUS_SUCCESS;
1238}
1239
1240int VBoxUsbFltAdd(PVBOXUSBFLTCTX pContext, PUSBFILTER pFilter, uintptr_t *pId)
1241{
1242 LOG(("adding filter, Context (0x%p)..", pContext));
1243 *pId = 0;
1244 /* LOG the filter details. */
1245 LOG((__FUNCTION__": %s %s %s",
1246 USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
1247 USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
1248 USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
1249#ifdef VBOX_USB_WITH_VERBOSE_LOGGING
1250 LOG(("VBoxUSBClient::addFilter: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x Type%#x",
1251 USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID),
1252 USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID),
1253 USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
1254 USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS),
1255 USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS),
1256 USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
1257 USBFilterGetNum(pFilter, USBFILTERIDX_BUS),
1258 USBFilterGetNum(pFilter, USBFILTERIDX_PORT),
1259 USBFilterGetFilterType(pFilter)));
1260#endif
1261
1262 /* We can't get the bus/port numbers. Ignore them while matching. */
1263 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false);
1264 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PORT, false);
1265
1266 /* We may not be able to reconstruct the class/subclass/protocol if we aren't able to
1267 * read the device descriptor. Don't require these to be present. See also the fInferredDesc flag.
1268 */
1269 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_CLASS, false);
1270 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS, false);
1271 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_DEVICE_PROTOCOL, false);
1272
1273 /* We may also be unable to read string descriptors. Often the userland can't read the
1274 * string descriptors either because the device is in a low-power state, but it can happen
1275 * that the userland gets lucky and reads the strings, but by the time we get to read them
1276 * they're inaccessible due to power management. So, don't require the strings to be present.
1277 */
1278 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_MANUFACTURER_STR, false);
1279 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_PRODUCT_STR, false);
1280 USBFilterSetMustBePresent(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR, false);
1281
1282 uintptr_t uId = 0;
1283 VBOXUSBFLT_LOCK_ACQUIRE();
1284 /* Add the filter. */
1285 int rc = VBoxUSBFilterAdd(pFilter, pContext, &uId);
1286 VBOXUSBFLT_LOCK_RELEASE();
1287 if (RT_SUCCESS(rc))
1288 {
1289 LOG(("ADDED filter id 0x%p", uId));
1290 ASSERT_WARN(uId, ("uid is NULL"));
1291#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
1292 VBoxUsbFltFilterCheck();
1293#endif
1294 }
1295 else
1296 {
1297 WARN(("VBoxUSBFilterAdd failed rc (%d)", rc));
1298 ASSERT_WARN(!uId, ("uid is not NULL"));
1299 }
1300
1301 *pId = uId;
1302 return rc;
1303}
1304
1305int VBoxUsbFltRemove(PVBOXUSBFLTCTX pContext, uintptr_t uId)
1306{
1307 LOG(("removing filter id(0x%p), Context (0x%p)..", pContext, uId));
1308 Assert(uId);
1309
1310 VBOXUSBFLT_LOCK_ACQUIRE();
1311 int rc = VBoxUSBFilterRemove(pContext, uId);
1312 if (!RT_SUCCESS(rc))
1313 {
1314 WARN(("VBoxUSBFilterRemove failed rc (%d)", rc));
1315 VBOXUSBFLT_LOCK_RELEASE();
1316 return rc;
1317 }
1318
1319 LOG(("enumerating devices.."));
1320 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
1321 pEntry != &g_VBoxUsbFltGlobals.DeviceList;
1322 pEntry = pEntry->Flink)
1323 {
1324 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
1325 if (pDevice->fIsFilterOneShot)
1326 {
1327 ASSERT_WARN(!pDevice->uFltId, ("oneshot filter on device(0x%p): unexpected uFltId(%d)", pDevice, pDevice->uFltId));
1328 }
1329
1330 if (pDevice->uFltId != uId)
1331 continue;
1332
1333 ASSERT_WARN(pDevice->pOwner == pContext, ("Device(0x%p) owner(0x%p) not match to (0x%p)", pDevice, pDevice->pOwner, pContext));
1334 if (pDevice->pOwner != pContext)
1335 continue;
1336
1337 LOG(("found device(0x%p), pdo(0x%p), state(%d), filter id(0x%p), oneshot(%d)",
1338 pDevice, pDevice->Pdo, pDevice->enmState, pDevice->uFltId, (int)pDevice->fIsFilterOneShot));
1339 ASSERT_WARN(!pDevice->fIsFilterOneShot, ("device(0x%p) is filtered with a oneshot filter", pDevice));
1340 pDevice->uFltId = 0;
1341 /* clear the fIsFilterOneShot flag to ensure the device is replugged on the next VBoxUsbFltFilterCheck call */
1342 pDevice->fIsFilterOneShot = false;
1343 }
1344 VBOXUSBFLT_LOCK_RELEASE();
1345
1346 LOG(("done enumerating devices"));
1347
1348 if (RT_SUCCESS(rc))
1349 {
1350#ifdef VBOX_USBMON_WITH_FILTER_AUTOAPPLY
1351 VBoxUsbFltFilterCheck();
1352#endif
1353 }
1354 return rc;
1355}
1356
1357static USBDEVICESTATE vboxUsbDevGetUserState(PVBOXUSBFLTCTX pContext, PVBOXUSBFLT_DEVICE pDevice)
1358{
1359 if (vboxUsbFltDevStateIsNotFiltered(pDevice))
1360 return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1361
1362 /* the device is filtered, or replugging */
1363 if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_REPLUGGING)
1364 {
1365 ASSERT_WARN(!pDevice->pOwner, ("replugging device(0x%p) still has an owner(0x%p)", pDevice, pDevice->pOwner));
1366 ASSERT_WARN(!pDevice->uFltId, ("replugging device(0x%p) still has filter(0x%p)", pDevice, pDevice->uFltId));
1367 /* no user state for this, we should not return it tu the user */
1368 return USBDEVICESTATE_USED_BY_HOST;
1369 }
1370
1371 /* the device is filtered, if owner differs from the context, return as USED_BY_HOST */
1372 ASSERT_WARN(pDevice->pOwner, ("device(0x%p) has noowner", pDevice));
1373 /* the id can be null if a filter is removed */
1374// Assert(pDevice->uFltId);
1375
1376 if (pDevice->pOwner != pContext)
1377 {
1378 LOG(("Device owner differs from the current context, returning used by host"));
1379 return USBDEVICESTATE_USED_BY_HOST;
1380 }
1381
1382 switch (pDevice->enmState)
1383 {
1384 case VBOXUSBFLT_DEVSTATE_UNCAPTURED:
1385 case VBOXUSBFLT_DEVSTATE_CAPTURING:
1386 return USBDEVICESTATE_USED_BY_HOST_CAPTURABLE;
1387 case VBOXUSBFLT_DEVSTATE_CAPTURED:
1388 return USBDEVICESTATE_HELD_BY_PROXY;
1389 case VBOXUSBFLT_DEVSTATE_USED_BY_GUEST:
1390 return USBDEVICESTATE_USED_BY_GUEST;
1391 default:
1392 WARN(("unexpected device state(%d) for device(0x%p)", pDevice->enmState, pDevice));
1393 return USBDEVICESTATE_UNSUPPORTED;
1394 }
1395}
1396
1397NTSTATUS VBoxUsbFltGetDevice(PVBOXUSBFLTCTX pContext, HVBOXUSBDEVUSR hDevice, PUSBSUP_GETDEV_MON pInfo)
1398{
1399 if (!hDevice)
1400 return STATUS_INVALID_PARAMETER;
1401
1402 memset (pInfo, 0, sizeof (*pInfo));
1403 VBOXUSBFLT_LOCK_ACQUIRE();
1404 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
1405 pEntry != &g_VBoxUsbFltGlobals.DeviceList;
1406 pEntry = pEntry->Flink)
1407 {
1408 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
1409 Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED);
1410 Assert(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED);
1411
1412 if (pDevice != hDevice)
1413 continue;
1414
1415 USBDEVICESTATE enmUsrState = vboxUsbDevGetUserState(pContext, pDevice);
1416 pInfo->enmState = enmUsrState;
1417 VBOXUSBFLT_LOCK_RELEASE();
1418 return STATUS_SUCCESS;
1419 }
1420
1421 VBOXUSBFLT_LOCK_RELEASE();
1422
1423 /* We should not get this far with valid input. */
1424 return STATUS_INVALID_PARAMETER;
1425}
1426
1427NTSTATUS VBoxUsbFltPdoAdd(PDEVICE_OBJECT pPdo, BOOLEAN *pbFiltered)
1428{
1429 *pbFiltered = FALSE;
1430 PVBOXUSBFLT_DEVICE pDevice;
1431
1432 /* Find the real PDO+reference. Dereference when we're done with it. Note that
1433 * the input pPdo was not explicitly referenced so we're not dropping its ref.
1434 */
1435 PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
1436 LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
1437 pPdo = pDevObj;
1438
1439 /* first check if device is in the a already */
1440 VBOXUSBFLT_LOCK_ACQUIRE();
1441 pDevice = vboxUsbFltDevGetLocked(pPdo);
1442 if (pDevice)
1443 {
1444 LOG(("found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
1445 ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
1446 ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
1447 *pbFiltered = pDevice->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
1448 VBOXUSBFLT_LOCK_RELEASE();
1449 ObDereferenceObject(pPdo);
1450 return STATUS_SUCCESS;
1451 }
1452 VBOXUSBFLT_LOCK_RELEASE();
1453 pDevice = (PVBOXUSBFLT_DEVICE)VBoxUsbMonMemAllocZ(sizeof (*pDevice));
1454 if (!pDevice)
1455 {
1456 WARN(("VBoxUsbMonMemAllocZ failed"));
1457 ObDereferenceObject(pPdo);
1458 return STATUS_NO_MEMORY;
1459 }
1460
1461 pDevice->enmState = VBOXUSBFLT_DEVSTATE_ADDED;
1462 pDevice->cRefs = 1;
1463 NTSTATUS Status = vboxUsbFltDevPopulate(pDevice, pPdo /* , TRUE /* need all props */);
1464 if (!NT_SUCCESS(Status))
1465 {
1466 WARN(("vboxUsbFltDevPopulate failed, Status 0x%x", Status));
1467 ObDereferenceObject(pPdo);
1468 VBoxUsbMonMemFree(pDevice);
1469 return Status;
1470 }
1471
1472 uintptr_t uId;
1473 bool fFilter = false;
1474 bool fIsOneShot = false;
1475 PVBOXUSBFLTCTX pCtx;
1476 PVBOXUSBFLT_DEVICE pTmpDev;
1477 VBOXUSBFLT_LOCK_ACQUIRE();
1478 /* (paranoia) re-check the device is still not here */
1479 pTmpDev = vboxUsbFltDevGetLocked(pPdo);
1480
1481 /* Drop the PDO ref, now we won't need it anymore. */
1482 ObDereferenceObject(pPdo);
1483
1484 if (pTmpDev)
1485 {
1486 LOG(("second try: found device (0x%p), state(%d) for PDO(0x%p)", pDevice, pDevice->enmState, pPdo));
1487 ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_ADDED, ("second try: VBOXUSBFLT_DEVSTATE_ADDED state for device(0x%p)", pDevice));
1488 ASSERT_WARN(pDevice->enmState != VBOXUSBFLT_DEVSTATE_REMOVED, ("second try: VBOXUSBFLT_DEVSTATE_REMOVED state for device(0x%p)", pDevice));
1489 *pbFiltered = pTmpDev->enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
1490 VBOXUSBFLT_LOCK_RELEASE();
1491 VBoxUsbMonMemFree(pDevice);
1492 return STATUS_SUCCESS;
1493 }
1494
1495 LOG(("Created Device 0x%p for PDO 0x%p", pDevice, pPdo));
1496
1497 pCtx = vboxUsbFltDevMatchLocked(pDevice, &uId,
1498 true, /* remove a one-shot filter */
1499 &fFilter, &fIsOneShot);
1500 LOG(("Matching Info: Filter (0x%p), pCtx(0x%p), fFilter(%d), fIsOneShot(%d)", uId, pCtx, (int)fFilter, (int)fIsOneShot));
1501 if (fFilter)
1502 {
1503 LOG(("Created Device 0x%p should be filtered", pDevice));
1504 ASSERT_WARN(pCtx, ("zero ctx"));
1505 ASSERT_WARN(uId, ("zero uId"));
1506 pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
1507 }
1508 else
1509 {
1510 LOG(("Created Device 0x%p should NOT be filtered", pDevice));
1511 ASSERT_WARN(!uId == !pCtx, ("invalid uid(0x%p) - ctx(0x%p) pair", uId, pCtx)); /* either both zero or both not */
1512 pDevice->enmState = VBOXUSBFLT_DEVSTATE_UNCAPTURED;
1513 }
1514
1515 if (pCtx)
1516 vboxUsbFltDevOwnerSetLocked(pDevice, pCtx, fIsOneShot ? 0 : uId, fIsOneShot);
1517
1518 InsertHeadList(&g_VBoxUsbFltGlobals.DeviceList, &pDevice->GlobalLe);
1519
1520 /* do not need to signal anything here -
1521 * going to do that once the proxy device object starts */
1522 VBOXUSBFLT_LOCK_RELEASE();
1523
1524 *pbFiltered = fFilter;
1525
1526 return STATUS_SUCCESS;
1527}
1528
1529BOOLEAN VBoxUsbFltPdoIsFiltered(PDEVICE_OBJECT pPdo)
1530{
1531 VBOXUSBFLT_DEVSTATE enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
1532
1533 /* Find the real PDO+reference. Dereference when we're done with it. Note that
1534 * the input pPdo was not explicitly referenced so we're not dropping its ref.
1535 */
1536 PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
1537 LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
1538 pPdo = pDevObj;
1539
1540 VBOXUSBFLT_LOCK_ACQUIRE();
1541
1542 PVBOXUSBFLT_DEVICE pDevice = vboxUsbFltDevGetLocked(pPdo);
1543 if (pDevice)
1544 enmState = pDevice->enmState;
1545
1546 VBOXUSBFLT_LOCK_RELEASE();
1547 ObDereferenceObject(pPdo);
1548
1549 return enmState >= VBOXUSBFLT_DEVSTATE_CAPTURING;
1550}
1551
1552NTSTATUS VBoxUsbFltPdoRemove(PDEVICE_OBJECT pPdo)
1553{
1554 PVBOXUSBFLT_DEVICE pDevice;
1555 VBOXUSBFLT_DEVSTATE enmOldState;
1556
1557 /* Find the real PDO+reference. Dereference when we're done with it. Note that
1558 * the input pPdo was not explicitly referenced so we're not dropping its ref.
1559 */
1560 PDEVICE_OBJECT pDevObj = IoGetDeviceAttachmentBaseRef(pPdo);
1561 LOG(("DevObj=%p, real PDO=%p\n", pPdo, pDevObj));
1562 pPdo = pDevObj;
1563
1564 VBOXUSBFLT_LOCK_ACQUIRE();
1565 pDevice = vboxUsbFltDevGetLocked(pPdo);
1566 if (pDevice)
1567 {
1568 RemoveEntryList(&pDevice->GlobalLe);
1569 enmOldState = pDevice->enmState;
1570 pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
1571 }
1572 VBOXUSBFLT_LOCK_RELEASE();
1573 ObDereferenceObject(pPdo);
1574 if (pDevice)
1575 vboxUsbFltDevRelease(pDevice);
1576 return STATUS_SUCCESS;
1577}
1578
1579HVBOXUSBFLTDEV VBoxUsbFltProxyStarted(PDEVICE_OBJECT pPdo)
1580{
1581 PVBOXUSBFLT_DEVICE pDevice;
1582 VBOXUSBFLT_LOCK_ACQUIRE();
1583
1584 /* NB: The USB proxy (VBoxUSB.sys) passes us the real PDO, not anything above that. */
1585 pDevice = vboxUsbFltDevGetLocked(pPdo);
1586 /*
1587 * Prevent a host crash when vboxUsbFltDevGetLocked fails to locate the matching PDO
1588 * in g_VBoxUsbFltGlobals.DeviceList (see @bugref{6509}).
1589 */
1590 if (pDevice == NULL)
1591 {
1592 WARN(("failed to get device for PDO(0x%p)", pPdo));
1593 }
1594 else if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURING)
1595 {
1596 pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURED;
1597 LOG(("The proxy notified proxy start for the captured device 0x%p", pDevice));
1598 vboxUsbFltDevRetain(pDevice);
1599 }
1600 else
1601 {
1602 WARN(("invalid state, %d", pDevice->enmState));
1603 pDevice = NULL;
1604 }
1605 VBOXUSBFLT_LOCK_RELEASE();
1606 return pDevice;
1607}
1608
1609void VBoxUsbFltProxyStopped(HVBOXUSBFLTDEV hDev)
1610{
1611 PVBOXUSBFLT_DEVICE pDevice = (PVBOXUSBFLT_DEVICE)hDev;
1612 /*
1613 * Prevent a host crash when VBoxUsbFltProxyStarted fails, returning NULL.
1614 * See @bugref{6509}.
1615 */
1616 if (pDevice == NULL)
1617 {
1618 WARN(("VBoxUsbFltProxyStopped called with NULL device pointer"));
1619 return;
1620 }
1621 VBOXUSBFLT_LOCK_ACQUIRE();
1622 if (pDevice->enmState == VBOXUSBFLT_DEVSTATE_CAPTURED
1623 || pDevice->enmState == VBOXUSBFLT_DEVSTATE_USED_BY_GUEST)
1624 {
1625 /* this is due to devie was physically removed */
1626 LOG(("The proxy notified proxy stop for the captured device 0x%p, current state %d", pDevice, pDevice->enmState));
1627 pDevice->enmState = VBOXUSBFLT_DEVSTATE_CAPTURING;
1628 }
1629 else
1630 {
1631 if (pDevice->enmState != VBOXUSBFLT_DEVSTATE_REPLUGGING)
1632 {
1633 WARN(("invalid state, %d", pDevice->enmState));
1634 }
1635 }
1636 VBOXUSBFLT_LOCK_RELEASE();
1637
1638 vboxUsbFltDevRelease(pDevice);
1639}
1640
1641
1642static NTSTATUS vboxUsbFltRegKeyQuery(PWSTR ValueName, ULONG ValueType, PVOID ValueData, ULONG ValueLength, PVOID Context, PVOID EntryContext)
1643{
1644 NTSTATUS Status = STATUS_SUCCESS;
1645
1646 RT_NOREF(ValueName, Context);
1647 if ( ValueType == REG_DWORD
1648 && ValueLength == sizeof(ULONG))
1649 *(ULONG *)EntryContext = *(ULONG *)ValueData;
1650 else
1651 Status = STATUS_OBJECT_TYPE_MISMATCH;
1652
1653 return Status;
1654}
1655
1656
1657NTSTATUS VBoxUsbFltInit()
1658{
1659 int rc = VBoxUSBFilterInit();
1660 if (RT_FAILURE(rc))
1661 {
1662 WARN(("VBoxUSBFilterInit failed, rc (%d)", rc));
1663 return STATUS_UNSUCCESSFUL;
1664 }
1665
1666 memset(&g_VBoxUsbFltGlobals, 0, sizeof (g_VBoxUsbFltGlobals));
1667 InitializeListHead(&g_VBoxUsbFltGlobals.DeviceList);
1668 InitializeListHead(&g_VBoxUsbFltGlobals.ContextList);
1669 InitializeListHead(&g_VBoxUsbFltGlobals.BlackDeviceList);
1670 vboxUsbFltBlDevPopulateWithKnownLocked();
1671 VBOXUSBFLT_LOCK_INIT();
1672
1673 /*
1674 * Check whether the setting to force replugging USB devices when
1675 * querying string descriptors fail is set in the registry,
1676 * see @bugref{9479}.
1677 */
1678 RTL_QUERY_REGISTRY_TABLE aParams[] =
1679 {
1680 {vboxUsbFltRegKeyQuery, 0, L"ForceReplugWhenDevPopulateFails", &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, REG_DWORD, &g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails, sizeof(ULONG) },
1681 { NULL, 0, NULL, NULL, 0, 0, 0 }
1682 };
1683 UNICODE_STRING UnicodePath = RTL_CONSTANT_STRING(L"\\VBoxUSB");
1684
1685 NTSTATUS Status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL, UnicodePath.Buffer, &aParams[0], NULL, NULL);
1686 if (Status == STATUS_SUCCESS)
1687 {
1688 if (g_VBoxUsbFltGlobals.dwForceReplugWhenDevPopulateFails)
1689 LOG(("Forcing replug of USB devices where querying the descriptors fail\n"));
1690 }
1691 else
1692 LOG(("RtlQueryRegistryValues() -> %#x, assuming defaults\n", Status));
1693
1694 return STATUS_SUCCESS;
1695}
1696
1697NTSTATUS VBoxUsbFltTerm()
1698{
1699 bool bBusy = false;
1700 VBOXUSBFLT_LOCK_ACQUIRE();
1701 do
1702 {
1703 if (!IsListEmpty(&g_VBoxUsbFltGlobals.ContextList))
1704 {
1705 AssertFailed();
1706 bBusy = true;
1707 break;
1708 }
1709
1710 PLIST_ENTRY pNext = NULL;
1711 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
1712 pEntry != &g_VBoxUsbFltGlobals.DeviceList;
1713 pEntry = pNext)
1714 {
1715 pNext = pEntry->Flink;
1716 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
1717 Assert(!pDevice->uFltId);
1718 Assert(!pDevice->pOwner);
1719 if (pDevice->cRefs != 1)
1720 {
1721 AssertFailed();
1722 bBusy = true;
1723 break;
1724 }
1725 }
1726 } while (0);
1727
1728 VBOXUSBFLT_LOCK_RELEASE()
1729
1730 if (bBusy)
1731 {
1732 return STATUS_DEVICE_BUSY;
1733 }
1734
1735 for (PLIST_ENTRY pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink;
1736 pEntry != &g_VBoxUsbFltGlobals.DeviceList;
1737 pEntry = g_VBoxUsbFltGlobals.DeviceList.Flink)
1738 {
1739 RemoveEntryList(pEntry);
1740 PVBOXUSBFLT_DEVICE pDevice = PVBOXUSBFLT_DEVICE_FROM_LE(pEntry);
1741 pDevice->enmState = VBOXUSBFLT_DEVSTATE_REMOVED;
1742 vboxUsbFltDevRelease(pDevice);
1743 }
1744
1745 vboxUsbFltBlDevClearLocked();
1746
1747 VBOXUSBFLT_LOCK_TERM();
1748
1749 VBoxUSBFilterTerm();
1750
1751 return STATUS_SUCCESS;
1752}
1753
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette