VirtualBox

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

Last change on this file since 62485 was 62485, checked in by vboxsync, 8 years ago

(C) 2016

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