VirtualBox

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

Last change on this file since 70772 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use