VirtualBox

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

Last change on this file since 80981 was 80981, checked in by vboxsync, 6 years ago

USB/win: Work around a case where userland can read USB string descriptors but kernel can't, don't require exact match. See bugref:9518 for details.

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

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