VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/USBProxyBackend.cpp@ 88539

Last change on this file since 88539 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: 18.9 KB
Line 
1/* $Id: USBProxyBackend.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service (base) class.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_USBPROXYBACKEND
19#include "USBProxyBackend.h"
20#include "USBProxyService.h"
21#include "HostUSBDeviceImpl.h"
22#include "HostImpl.h"
23#include "MachineImpl.h"
24#include "VirtualBoxImpl.h"
25
26#include "AutoCaller.h"
27#include "LoggingNew.h"
28
29#include <VBox/com/array.h>
30#include <iprt/errcore.h>
31#include <iprt/asm.h>
32#include <iprt/semaphore.h>
33#include <iprt/thread.h>
34#include <iprt/mem.h>
35#include <iprt/string.h>
36
37
38/**
39 * Empty constructor.
40 */
41USBProxyBackend::USBProxyBackend()
42{
43 LogFlowThisFunc(("\n"));
44}
45
46
47/**
48 * Empty destructor.
49 */
50USBProxyBackend::~USBProxyBackend()
51{
52}
53
54
55HRESULT USBProxyBackend::FinalConstruct()
56{
57 return BaseFinalConstruct();
58}
59
60void USBProxyBackend::FinalRelease()
61{
62 uninit();
63 BaseFinalRelease();
64}
65
66/**
67 * Stub needed as long as the class isn't virtual
68 */
69int USBProxyBackend::init(USBProxyService *pUsbProxyService, const com::Utf8Str &strId,
70 const com::Utf8Str &strAddress, bool fLoadingSettings)
71{
72 RT_NOREF1(fLoadingSettings);
73
74 m_pUsbProxyService = pUsbProxyService;
75 mThread = NIL_RTTHREAD;
76 mTerminate = false;
77 unconst(m_strId) = strId;
78 m_cRefs = 0;
79 unconst(m_strAddress) = strAddress;
80
81 unconst(m_strBackend) = Utf8Str::Empty;
82
83 return VINF_SUCCESS;
84}
85
86
87void USBProxyBackend::uninit()
88{
89 LogFlowThisFunc(("\n"));
90 Assert(mThread == NIL_RTTHREAD);
91 mTerminate = true;
92 m_pUsbProxyService = NULL;
93 m_llDevices.clear();
94}
95
96/**
97 * Query if the service is active and working.
98 *
99 * @returns true if the service is up running.
100 * @returns false if the service isn't running.
101 */
102bool USBProxyBackend::isActive(void)
103{
104 return mThread != NIL_RTTHREAD;
105}
106
107
108/**
109 * Returns the ID of the instance.
110 *
111 * @returns ID string for the instance.
112 */
113const com::Utf8Str &USBProxyBackend::i_getId()
114{
115 return m_strId;
116}
117
118
119/**
120 * Returns the address of the instance.
121 *
122 * @returns ID string for the instance.
123 */
124const com::Utf8Str &USBProxyBackend::i_getAddress()
125{
126 return m_strAddress;
127}
128
129
130/**
131 * Returns the backend of the instance.
132 *
133 * @returns ID string for the instance.
134 */
135const com::Utf8Str &USBProxyBackend::i_getBackend()
136{
137 return m_strBackend;
138}
139
140/**
141 * Returns the current reference counter for the backend.
142 */
143uint32_t USBProxyBackend::i_getRefCount()
144{
145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
146 return m_cRefs;
147}
148
149
150/**
151 * A filter was inserted / loaded.
152 *
153 * @param aFilter Pointer to the inserted filter.
154 * @return ID of the inserted filter
155 */
156void *USBProxyBackend::insertFilter(PCUSBFILTER aFilter)
157{
158 // return non-NULL to fake success.
159 NOREF(aFilter);
160 return (void *)1;
161}
162
163
164/**
165 * A filter was removed.
166 *
167 * @param aId ID of the filter to remove
168 */
169void USBProxyBackend::removeFilter(void *aId)
170{
171 NOREF(aId);
172}
173
174
175/**
176 * A VM is trying to capture a device, do necessary preparations.
177 *
178 * @returns VBox status code.
179 * @param aDevice The device in question.
180 */
181int USBProxyBackend::captureDevice(HostUSBDevice *aDevice)
182{
183 NOREF(aDevice);
184 return VERR_NOT_IMPLEMENTED;
185}
186
187
188/**
189 * Notification that an async captureDevice() operation completed.
190 *
191 * This is used by the proxy to release temporary filters.
192 *
193 * @returns VBox status code.
194 * @param aDevice The device in question.
195 * @param aSuccess Whether it succeeded or failed.
196 */
197void USBProxyBackend::captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
198{
199 NOREF(aDevice);
200 NOREF(aSuccess);
201
202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
203 incRef();
204}
205
206
207/**
208 * A VM is releasing a device back to the host.
209 *
210 * @returns VBox status code.
211 * @param aDevice The device in question.
212 */
213int USBProxyBackend::releaseDevice(HostUSBDevice *aDevice)
214{
215 NOREF(aDevice);
216 return VERR_NOT_IMPLEMENTED;
217}
218
219
220/**
221 * Notification that an async releaseDevice() operation completed.
222 *
223 * This is used by the proxy to release temporary filters.
224 *
225 * @returns VBox status code.
226 * @param aDevice The device in question.
227 * @param aSuccess Whether it succeeded or failed.
228 */
229void USBProxyBackend::releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
230{
231 NOREF(aDevice);
232 NOREF(aSuccess);
233
234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
235 decRef();
236}
237
238
239bool USBProxyBackend::isFakeUpdateRequired()
240{
241 return false;
242}
243
244/**
245 * Returns whether devices reported by this backend go through a de/re-attach
246 * and device re-enumeration cycle when they are captured or released.
247 */
248bool USBProxyBackend::i_isDevReEnumerationRequired()
249{
250 return false;
251}
252
253// Internals
254/////////////////////////////////////////////////////////////////////////////
255
256
257/**
258 * Starts the service.
259 *
260 * @returns VBox status code.
261 */
262int USBProxyBackend::start(void)
263{
264 int rc = VINF_SUCCESS;
265 if (mThread == NIL_RTTHREAD)
266 {
267 /*
268 * Force update before starting the poller thread.
269 */
270 rc = wait(0);
271 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED || RT_SUCCESS(rc))
272 {
273 PUSBDEVICE pDevices = getDevices();
274 updateDeviceList(pDevices);
275
276 /*
277 * Create the poller thread which will look for changes.
278 */
279 mTerminate = false;
280 rc = RTThreadCreate(&mThread, USBProxyBackend::serviceThread, this,
281 0, RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "USBPROXY");
282 AssertRC(rc);
283 if (RT_SUCCESS(rc))
284 LogFlowThisFunc(("started mThread=%RTthrd\n", mThread));
285 else
286 mThread = NIL_RTTHREAD;
287 }
288 }
289 else
290 LogFlowThisFunc(("already running, mThread=%RTthrd\n", mThread));
291 return rc;
292}
293
294
295/**
296 * Stops the service.
297 *
298 * @returns VBox status code.
299 */
300int USBProxyBackend::stop(void)
301{
302 int rc = VINF_SUCCESS;
303 if (mThread != NIL_RTTHREAD)
304 {
305 /*
306 * Mark the thread for termination and kick it.
307 */
308 ASMAtomicXchgSize(&mTerminate, true);
309 rc = interruptWait();
310 AssertRC(rc);
311
312 /*
313 * Wait for the thread to finish and then update the state.
314 */
315 rc = RTThreadWait(mThread, 60000, NULL);
316 if (rc == VERR_INVALID_HANDLE)
317 rc = VINF_SUCCESS;
318 if (RT_SUCCESS(rc))
319 {
320 LogFlowThisFunc(("stopped mThread=%RTthrd\n", mThread));
321 mThread = NIL_RTTHREAD;
322 mTerminate = false;
323 }
324 else
325 AssertRC(rc);
326 }
327 else
328 LogFlowThisFunc(("not active\n"));
329
330 /* Make sure there is no device from us in the list anymore. */
331 updateDeviceList(NULL);
332
333 return rc;
334}
335
336
337/**
338 * The service thread created by start().
339 *
340 * @param Thread The thread handle.
341 * @param pvUser Pointer to the USBProxyBackend instance.
342 */
343/*static*/ DECLCALLBACK(int) USBProxyBackend::serviceThread(RTTHREAD /* Thread */, void *pvUser)
344{
345 USBProxyBackend *pThis = (USBProxyBackend *)pvUser;
346 LogFlowFunc(("pThis=%p\n", pThis));
347 pThis->serviceThreadInit();
348 int rc = VINF_SUCCESS;
349
350 /*
351 * Processing loop.
352 */
353 for (;;)
354 {
355 rc = pThis->wait(RT_INDEFINITE_WAIT);
356 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED && rc != VERR_TIMEOUT)
357 break;
358 if (pThis->mTerminate)
359 break;
360
361 PUSBDEVICE pDevices = pThis->getDevices();
362 pThis->updateDeviceList(pDevices);
363 }
364
365 pThis->serviceThreadTerm();
366 LogFlowFunc(("returns %Rrc\n", rc));
367 return rc;
368}
369
370
371/**
372 * First call made on the service thread, use it to do
373 * thread initialization.
374 *
375 * The default implementation in USBProxyBackend just a dummy stub.
376 */
377void USBProxyBackend::serviceThreadInit(void)
378{
379}
380
381
382/**
383 * Last call made on the service thread, use it to do
384 * thread termination.
385 */
386void USBProxyBackend::serviceThreadTerm(void)
387{
388}
389
390
391/**
392 * Wait for a change in the USB devices attached to the host.
393 *
394 * The default implementation in USBProxyBackend just a dummy stub.
395 *
396 * @returns VBox status code. VERR_INTERRUPTED and VERR_TIMEOUT are considered
397 * harmless, while all other error status are fatal.
398 * @param aMillies Number of milliseconds to wait.
399 */
400int USBProxyBackend::wait(RTMSINTERVAL aMillies)
401{
402 return RTThreadSleep(RT_MIN(aMillies, 250));
403}
404
405
406/**
407 * Interrupt any wait() call in progress.
408 *
409 * The default implementation in USBProxyBackend just a dummy stub.
410 *
411 * @returns VBox status code.
412 */
413int USBProxyBackend::interruptWait(void)
414{
415 return VERR_NOT_IMPLEMENTED;
416}
417
418
419/**
420 * Get a list of USB device currently attached to the host.
421 *
422 * The default implementation in USBProxyBackend just a dummy stub.
423 *
424 * @returns Pointer to a list of USB devices.
425 * The list nodes are freed individually by calling freeDevice().
426 */
427PUSBDEVICE USBProxyBackend::getDevices(void)
428{
429 return NULL;
430}
431
432
433/**
434 * Increments the reference counter.
435 *
436 * @returns New reference count value.
437 */
438uint32_t USBProxyBackend::incRef()
439{
440 Assert(isWriteLockOnCurrentThread());
441
442 return ++m_cRefs;
443}
444
445/**
446 * Decrements the reference counter.
447 *
448 * @returns New reference count value.
449 */
450uint32_t USBProxyBackend::decRef()
451{
452 Assert(isWriteLockOnCurrentThread());
453
454 return --m_cRefs;
455}
456
457
458/**
459 * Free all the members of a USB device returned by getDevice().
460 *
461 * @param pDevice Pointer to the device.
462 */
463/*static*/ void
464USBProxyBackend::freeDeviceMembers(PUSBDEVICE pDevice)
465{
466 RTStrFree((char *)pDevice->pszManufacturer);
467 pDevice->pszManufacturer = NULL;
468 RTStrFree((char *)pDevice->pszProduct);
469 pDevice->pszProduct = NULL;
470 RTStrFree((char *)pDevice->pszSerialNumber);
471 pDevice->pszSerialNumber = NULL;
472
473 RTStrFree((char *)pDevice->pszAddress);
474 pDevice->pszAddress = NULL;
475 RTStrFree((char *)pDevice->pszBackend);
476 pDevice->pszBackend = NULL;
477#ifdef RT_OS_WINDOWS
478 RTStrFree(pDevice->pszAltAddress);
479 pDevice->pszAltAddress = NULL;
480 RTStrFree(pDevice->pszHubName);
481 pDevice->pszHubName = NULL;
482#elif defined(RT_OS_SOLARIS)
483 RTStrFree(pDevice->pszDevicePath);
484 pDevice->pszDevicePath = NULL;
485#endif
486}
487
488
489/**
490 * Free one USB device returned by getDevice().
491 *
492 * @param pDevice Pointer to the device.
493 */
494/*static*/ void
495USBProxyBackend::freeDevice(PUSBDEVICE pDevice)
496{
497 freeDeviceMembers(pDevice);
498 RTMemFree(pDevice);
499}
500
501void USBProxyBackend::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, PUSBDEVICE pDev)
502{
503 /* Nothing to do. */
504 NOREF(aDevice);
505 NOREF(pDev);
506}
507
508/**
509 * Initializes a filter with the data from the specified device.
510 *
511 * @param aFilter The filter to fill.
512 * @param aDevice The device to fill it with.
513 */
514/*static*/ void
515USBProxyBackend::initFilterFromDevice(PUSBFILTER aFilter, HostUSBDevice *aDevice)
516{
517 PCUSBDEVICE pDev = aDevice->i_getUsbData();
518 int vrc;
519
520 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_VENDOR_ID, pDev->idVendor, true); AssertRC(vrc);
521 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_PRODUCT_ID, pDev->idProduct, true); AssertRC(vrc);
522 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_REV, pDev->bcdDevice, true); AssertRC(vrc);
523 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_CLASS, pDev->bDeviceClass, true); AssertRC(vrc);
524 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_SUB_CLASS, pDev->bDeviceSubClass, true); AssertRC(vrc);
525 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_PROTOCOL, pDev->bDeviceProtocol, true); AssertRC(vrc);
526 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_PORT, pDev->bPort, false); AssertRC(vrc);
527 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_BUS, pDev->bBus, false); AssertRC(vrc);
528 if (pDev->pszSerialNumber)
529 {
530 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_SERIAL_NUMBER_STR, pDev->pszSerialNumber,
531 true /*fMustBePresent*/, true /*fPurge*/);
532 AssertRC(vrc);
533 }
534 if (pDev->pszProduct)
535 {
536 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_PRODUCT_STR, pDev->pszProduct,
537 true /*fMustBePresent*/, true /*fPurge*/);
538 AssertRC(vrc);
539 }
540 if (pDev->pszManufacturer)
541 {
542 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_MANUFACTURER_STR, pDev->pszManufacturer,
543 true /*fMustBePresent*/, true /*fPurge*/);
544 AssertRC(vrc);
545 }
546}
547
548HRESULT USBProxyBackend::getName(com::Utf8Str &aName)
549{
550 /* strId is constant during life time, no need to lock */
551 aName = m_strId;
552 return S_OK;
553}
554
555HRESULT USBProxyBackend::getType(com::Utf8Str &aType)
556{
557 aType = Utf8Str::Empty;
558 return S_OK;
559}
560
561/**
562 * Sort a list of USB devices.
563 *
564 * @returns Pointer to the head of the sorted doubly linked list.
565 * @param pDevices Head pointer (can be both singly and doubly linked list).
566 */
567static PUSBDEVICE sortDevices(PUSBDEVICE pDevices)
568{
569 PUSBDEVICE pHead = NULL;
570 PUSBDEVICE pTail = NULL;
571 while (pDevices)
572 {
573 /* unlink head */
574 PUSBDEVICE pDev = pDevices;
575 pDevices = pDev->pNext;
576 if (pDevices)
577 pDevices->pPrev = NULL;
578
579 /* find location. */
580 PUSBDEVICE pCur = pTail;
581 while ( pCur
582 && HostUSBDevice::i_compare(pCur, pDev) > 0)
583 pCur = pCur->pPrev;
584
585 /* insert (after pCur) */
586 pDev->pPrev = pCur;
587 if (pCur)
588 {
589 pDev->pNext = pCur->pNext;
590 pCur->pNext = pDev;
591 if (pDev->pNext)
592 pDev->pNext->pPrev = pDev;
593 else
594 pTail = pDev;
595 }
596 else
597 {
598 pDev->pNext = pHead;
599 if (pHead)
600 pHead->pPrev = pDev;
601 else
602 pTail = pDev;
603 pHead = pDev;
604 }
605 }
606
607 LogFlowFuncLeave();
608 return pHead;
609}
610
611
612/**
613 * Process any relevant changes in the attached USB devices.
614 *
615 * This is called from any available USB proxy backends service thread when they discover
616 * a change.
617 */
618void USBProxyBackend::updateDeviceList(PUSBDEVICE pDevices)
619{
620 LogFlowThisFunc(("\n"));
621
622 pDevices = sortDevices(pDevices);
623
624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
625
626 /*
627 * Compare previous list with the new list of devices
628 * and merge in any changes while notifying Host.
629 */
630 HostUSBDeviceList::iterator it = this->m_llDevices.begin();
631 while ( it != m_llDevices.end()
632 || pDevices)
633 {
634 ComObjPtr<HostUSBDevice> pHostDevice;
635
636 if (it != m_llDevices.end())
637 pHostDevice = *it;
638
639 /*
640 * Assert that the object is still alive (we still reference it in
641 * the collection and we're the only one who calls uninit() on it.
642 */
643 AutoCaller devCaller(pHostDevice.isNull() ? NULL : pHostDevice);
644 AssertComRC(devCaller.rc());
645
646 /*
647 * Lock the device object since we will read/write its
648 * properties. All Host callbacks also imply the object is locked.
649 */
650 AutoWriteLock devLock(pHostDevice.isNull() ? NULL : pHostDevice
651 COMMA_LOCKVAL_SRC_POS);
652
653 /* We should never get devices from other backends here. */
654 Assert(pHostDevice.isNull() || pHostDevice->i_getUsbProxyBackend() == this);
655
656 /*
657 * Compare.
658 */
659 int iDiff;
660 if (pHostDevice.isNull())
661 iDiff = 1;
662 else
663 {
664 if (!pDevices)
665 iDiff = -1;
666 else
667 iDiff = pHostDevice->i_compare(pDevices);
668 }
669 if (!iDiff)
670 {
671 /*
672 * The device still there, update the state and move on. The PUSBDEVICE
673 * structure is eaten by updateDeviceState / HostUSBDevice::updateState().
674 */
675 PUSBDEVICE pCur = pDevices;
676 pDevices = pDevices->pNext;
677 pCur->pPrev = pCur->pNext = NULL;
678
679 devLock.release();
680 alock.release();
681 m_pUsbProxyService->i_updateDeviceState(pHostDevice, pCur, isFakeUpdateRequired());
682 alock.acquire();
683 ++it;
684 }
685 else
686 {
687 if (iDiff > 0)
688 {
689 /*
690 * Head of pDevices was attached.
691 */
692 PUSBDEVICE pNew = pDevices;
693 pDevices = pDevices->pNext;
694 pNew->pPrev = pNew->pNext = NULL;
695
696 ComObjPtr<HostUSBDevice> NewObj;
697 NewObj.createObject();
698 NewObj->init(pNew, this);
699 LogFlowThisFunc(("attached %p {%s} %s / %p:{.idVendor=%#06x, .idProduct=%#06x, .pszProduct=\"%s\", .pszManufacturer=\"%s\"}\n",
700 (HostUSBDevice *)NewObj,
701 NewObj->i_getName().c_str(),
702 NewObj->i_getStateName(),
703 pNew,
704 pNew->idVendor,
705 pNew->idProduct,
706 pNew->pszProduct,
707 pNew->pszManufacturer));
708
709 m_llDevices.insert(it, NewObj);
710
711 devLock.release();
712 alock.release();
713 /* Do any backend specific work. */
714 deviceAdded(NewObj, pNew);
715 m_pUsbProxyService->i_deviceAdded(NewObj, pNew);
716 alock.acquire();
717 }
718 else
719 {
720 /*
721 * Check if the device was actually detached or logically detached
722 * as the result of a re-enumeration.
723 */
724 if (!pHostDevice->i_wasActuallyDetached())
725 ++it;
726 else
727 {
728 it = m_llDevices.erase(it);
729 devLock.release();
730 alock.release();
731 m_pUsbProxyService->i_deviceRemoved(pHostDevice);
732 LogFlowThisFunc(("detached %p {%s}\n",
733 (HostUSBDevice *)pHostDevice,
734 pHostDevice->i_getName().c_str()));
735
736 /* from now on, the object is no more valid,
737 * uninitialize to avoid abuse */
738 devCaller.release();
739 pHostDevice->uninit();
740 alock.acquire();
741 }
742 }
743 }
744 } /* while */
745
746 LogFlowThisFunc(("returns void\n"));
747}
748
749/* vi: set tabstop=4 shiftwidth=4 expandtab: */
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