VirtualBox

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

最後變更 在這個檔案從60742是 60742,由 vboxsync 提交於 9 年 前

Main/USBProxy{Service|Backend}: Rework interaction between thos two to be more sane than it currently is (calling into each other back and forth)

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 18.7 KB
 
1/* $Id: USBProxyBackend.cpp 60742 2016-04-28 13:55:03Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service (base) class.
4 */
5
6/*
7 * Copyright (C) 2006-2014 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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
235// Internals
236/////////////////////////////////////////////////////////////////////////////
237
238
239/**
240 * Starts the service.
241 *
242 * @returns VBox status code.
243 */
244int USBProxyBackend::start(void)
245{
246 int rc = VINF_SUCCESS;
247 if (mThread == NIL_RTTHREAD)
248 {
249 /*
250 * Force update before starting the poller thread.
251 */
252 rc = wait(0);
253 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED || RT_SUCCESS(rc))
254 {
255 PUSBDEVICE pDevices = getDevices();
256 updateDeviceList(pDevices);
257
258 /*
259 * Create the poller thread which will look for changes.
260 */
261 mTerminate = false;
262 rc = RTThreadCreate(&mThread, USBProxyBackend::serviceThread, this,
263 0, RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "USBPROXY");
264 AssertRC(rc);
265 if (RT_SUCCESS(rc))
266 LogFlowThisFunc(("started mThread=%RTthrd\n", mThread));
267 else
268 mThread = NIL_RTTHREAD;
269 }
270 }
271 else
272 LogFlowThisFunc(("already running, mThread=%RTthrd\n", mThread));
273 return rc;
274}
275
276
277/**
278 * Stops the service.
279 *
280 * @returns VBox status code.
281 */
282int USBProxyBackend::stop(void)
283{
284 int rc = VINF_SUCCESS;
285 if (mThread != NIL_RTTHREAD)
286 {
287 /*
288 * Mark the thread for termination and kick it.
289 */
290 ASMAtomicXchgSize(&mTerminate, true);
291 rc = interruptWait();
292 AssertRC(rc);
293
294 /*
295 * Wait for the thread to finish and then update the state.
296 */
297 rc = RTThreadWait(mThread, 60000, NULL);
298 if (rc == VERR_INVALID_HANDLE)
299 rc = VINF_SUCCESS;
300 if (RT_SUCCESS(rc))
301 {
302 LogFlowThisFunc(("stopped mThread=%RTthrd\n", mThread));
303 mThread = NIL_RTTHREAD;
304 mTerminate = false;
305 }
306 else
307 AssertRC(rc);
308 }
309 else
310 LogFlowThisFunc(("not active\n"));
311
312 /* Make sure there is no device from us in the list anymore. */
313 updateDeviceList(NULL);
314
315 return rc;
316}
317
318
319/**
320 * The service thread created by start().
321 *
322 * @param Thread The thread handle.
323 * @param pvUser Pointer to the USBProxyBackend instance.
324 */
325/*static*/ DECLCALLBACK(int) USBProxyBackend::serviceThread(RTTHREAD /* Thread */, void *pvUser)
326{
327 USBProxyBackend *pThis = (USBProxyBackend *)pvUser;
328 LogFlowFunc(("pThis=%p\n", pThis));
329 pThis->serviceThreadInit();
330 int rc = VINF_SUCCESS;
331
332 /*
333 * Processing loop.
334 */
335 for (;;)
336 {
337 rc = pThis->wait(RT_INDEFINITE_WAIT);
338 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED && rc != VERR_TIMEOUT)
339 break;
340 if (pThis->mTerminate)
341 break;
342
343 PUSBDEVICE pDevices = pThis->getDevices();
344 pThis->updateDeviceList(pDevices);
345 }
346
347 pThis->serviceThreadTerm();
348 LogFlowFunc(("returns %Rrc\n", rc));
349 return rc;
350}
351
352
353/**
354 * First call made on the service thread, use it to do
355 * thread initialization.
356 *
357 * The default implementation in USBProxyBackend just a dummy stub.
358 */
359void USBProxyBackend::serviceThreadInit(void)
360{
361}
362
363
364/**
365 * Last call made on the service thread, use it to do
366 * thread termination.
367 */
368void USBProxyBackend::serviceThreadTerm(void)
369{
370}
371
372
373/**
374 * Wait for a change in the USB devices attached to the host.
375 *
376 * The default implementation in USBProxyBackend just a dummy stub.
377 *
378 * @returns VBox status code. VERR_INTERRUPTED and VERR_TIMEOUT are considered
379 * harmless, while all other error status are fatal.
380 * @param aMillies Number of milliseconds to wait.
381 */
382int USBProxyBackend::wait(RTMSINTERVAL aMillies)
383{
384 return RTThreadSleep(RT_MIN(aMillies, 250));
385}
386
387
388/**
389 * Interrupt any wait() call in progress.
390 *
391 * The default implementation in USBProxyBackend just a dummy stub.
392 *
393 * @returns VBox status code.
394 */
395int USBProxyBackend::interruptWait(void)
396{
397 return VERR_NOT_IMPLEMENTED;
398}
399
400
401/**
402 * Get a list of USB device currently attached to the host.
403 *
404 * The default implementation in USBProxyBackend just a dummy stub.
405 *
406 * @returns Pointer to a list of USB devices.
407 * The list nodes are freed individually by calling freeDevice().
408 */
409PUSBDEVICE USBProxyBackend::getDevices(void)
410{
411 return NULL;
412}
413
414
415/**
416 * Increments the reference counter.
417 *
418 * @returns New reference count value.
419 */
420uint32_t USBProxyBackend::incRef()
421{
422 Assert(isWriteLockOnCurrentThread());
423
424 return ++m_cRefs;
425}
426
427/**
428 * Decrements the reference counter.
429 *
430 * @returns New reference count value.
431 */
432uint32_t USBProxyBackend::decRef()
433{
434 Assert(isWriteLockOnCurrentThread());
435
436 return --m_cRefs;
437}
438
439
440/**
441 * Free all the members of a USB device returned by getDevice().
442 *
443 * @param pDevice Pointer to the device.
444 */
445/*static*/ void
446USBProxyBackend::freeDeviceMembers(PUSBDEVICE pDevice)
447{
448 RTStrFree((char *)pDevice->pszManufacturer);
449 pDevice->pszManufacturer = NULL;
450 RTStrFree((char *)pDevice->pszProduct);
451 pDevice->pszProduct = NULL;
452 RTStrFree((char *)pDevice->pszSerialNumber);
453 pDevice->pszSerialNumber = NULL;
454
455 RTStrFree((char *)pDevice->pszAddress);
456 pDevice->pszAddress = NULL;
457 RTStrFree((char *)pDevice->pszBackend);
458 pDevice->pszBackend = NULL;
459#ifdef RT_OS_WINDOWS
460 RTStrFree(pDevice->pszAltAddress);
461 pDevice->pszAltAddress = NULL;
462 RTStrFree(pDevice->pszHubName);
463 pDevice->pszHubName = NULL;
464#elif defined(RT_OS_SOLARIS)
465 RTStrFree(pDevice->pszDevicePath);
466 pDevice->pszDevicePath = NULL;
467#endif
468}
469
470
471/**
472 * Free one USB device returned by getDevice().
473 *
474 * @param pDevice Pointer to the device.
475 */
476/*static*/ void
477USBProxyBackend::freeDevice(PUSBDEVICE pDevice)
478{
479 freeDeviceMembers(pDevice);
480 RTMemFree(pDevice);
481}
482
483void USBProxyBackend::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, PUSBDEVICE pDev)
484{
485 /* Nothing to do. */
486 NOREF(aDevice);
487 NOREF(pDev);
488}
489
490/**
491 * Initializes a filter with the data from the specified device.
492 *
493 * @param aFilter The filter to fill.
494 * @param aDevice The device to fill it with.
495 */
496/*static*/ void
497USBProxyBackend::initFilterFromDevice(PUSBFILTER aFilter, HostUSBDevice *aDevice)
498{
499 PCUSBDEVICE pDev = aDevice->i_getUsbData();
500 int vrc;
501
502 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_VENDOR_ID, pDev->idVendor, true); AssertRC(vrc);
503 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_PRODUCT_ID, pDev->idProduct, true); AssertRC(vrc);
504 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_REV, pDev->bcdDevice, true); AssertRC(vrc);
505 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_CLASS, pDev->bDeviceClass, true); AssertRC(vrc);
506 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_SUB_CLASS, pDev->bDeviceSubClass, true); AssertRC(vrc);
507 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_PROTOCOL, pDev->bDeviceProtocol, true); AssertRC(vrc);
508 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_PORT, pDev->bPort, false); AssertRC(vrc);
509 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_BUS, pDev->bBus, false); AssertRC(vrc);
510 if (pDev->pszSerialNumber)
511 {
512 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_SERIAL_NUMBER_STR, pDev->pszSerialNumber,
513 true /*fMustBePresent*/, false /*fPurge*/);
514 AssertRC(vrc);
515 }
516 if (pDev->pszProduct)
517 {
518 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_PRODUCT_STR, pDev->pszProduct,
519 true /*fMustBePresent*/, false /*fPurge*/);
520 AssertRC(vrc);
521 }
522 if (pDev->pszManufacturer)
523 {
524 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_MANUFACTURER_STR, pDev->pszManufacturer,
525 true /*fMustBePresent*/, false /*fPurge*/);
526 AssertRC(vrc);
527 }
528}
529
530HRESULT USBProxyBackend::getName(com::Utf8Str &aName)
531{
532 /* strId is constant during life time, no need to lock */
533 aName = m_strId;
534 return S_OK;
535}
536
537HRESULT USBProxyBackend::getType(com::Utf8Str &aType)
538{
539 aType = Utf8Str::Empty;
540 return S_OK;
541}
542
543/**
544 * Sort a list of USB devices.
545 *
546 * @returns Pointer to the head of the sorted doubly linked list.
547 * @param aDevices Head pointer (can be both singly and doubly linked list).
548 */
549static PUSBDEVICE sortDevices(PUSBDEVICE pDevices)
550{
551 PUSBDEVICE pHead = NULL;
552 PUSBDEVICE pTail = NULL;
553 while (pDevices)
554 {
555 /* unlink head */
556 PUSBDEVICE pDev = pDevices;
557 pDevices = pDev->pNext;
558 if (pDevices)
559 pDevices->pPrev = NULL;
560
561 /* find location. */
562 PUSBDEVICE pCur = pTail;
563 while ( pCur
564 && HostUSBDevice::i_compare(pCur, pDev) > 0)
565 pCur = pCur->pPrev;
566
567 /* insert (after pCur) */
568 pDev->pPrev = pCur;
569 if (pCur)
570 {
571 pDev->pNext = pCur->pNext;
572 pCur->pNext = pDev;
573 if (pDev->pNext)
574 pDev->pNext->pPrev = pDev;
575 else
576 pTail = pDev;
577 }
578 else
579 {
580 pDev->pNext = pHead;
581 if (pHead)
582 pHead->pPrev = pDev;
583 else
584 pTail = pDev;
585 pHead = pDev;
586 }
587 }
588
589 LogFlowFuncLeave();
590 return pHead;
591}
592
593
594/**
595 * Process any relevant changes in the attached USB devices.
596 *
597 * This is called from any available USB proxy backends service thread when they discover
598 * a change.
599 */
600void USBProxyBackend::updateDeviceList(PUSBDEVICE pDevices)
601{
602 LogFlowThisFunc(("\n"));
603
604 pDevices = sortDevices(pDevices);
605
606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
607
608 /*
609 * Compare previous list with the new list of devices
610 * and merge in any changes while notifying Host.
611 */
612 HostUSBDeviceList::iterator it = this->m_llDevices.begin();
613 while ( it != m_llDevices.end()
614 || pDevices)
615 {
616 ComObjPtr<HostUSBDevice> pHostDevice;
617
618 if (it != m_llDevices.end())
619 pHostDevice = *it;
620
621 /*
622 * Assert that the object is still alive (we still reference it in
623 * the collection and we're the only one who calls uninit() on it.
624 */
625 AutoCaller devCaller(pHostDevice.isNull() ? NULL : pHostDevice);
626 AssertComRC(devCaller.rc());
627
628 /*
629 * Lock the device object since we will read/write its
630 * properties. All Host callbacks also imply the object is locked.
631 */
632 AutoWriteLock devLock(pHostDevice.isNull() ? NULL : pHostDevice
633 COMMA_LOCKVAL_SRC_POS);
634
635 /* We should never get devices from other backends here. */
636 Assert(pHostDevice.isNull() || pHostDevice->i_getUsbProxyBackend() == this);
637
638 /*
639 * Compare.
640 */
641 int iDiff;
642 if (pHostDevice.isNull())
643 iDiff = 1;
644 else
645 {
646 if (!pDevices)
647 iDiff = -1;
648 else
649 iDiff = pHostDevice->i_compare(pDevices);
650 }
651 if (!iDiff)
652 {
653 /*
654 * The device still there, update the state and move on. The PUSBDEVICE
655 * structure is eaten by updateDeviceState / HostUSBDevice::updateState().
656 */
657 PUSBDEVICE pCur = pDevices;
658 pDevices = pDevices->pNext;
659 pCur->pPrev = pCur->pNext = NULL;
660
661 devLock.release();
662 alock.release();
663 /** @todo: Add mthod for every backend indicating whether to use fake updating. */
664#if defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)
665 m_pUsbProxyService->i_updateDeviceState(pHostDevice, pCur, true /* fFakeUpdate */);
666#else
667 m_pUsbProxyService->i_updateDeviceState(pHostDevice, pCur, false /* fFakeUpdate */);
668#endif
669 alock.acquire();
670 ++it;
671 }
672 else
673 {
674 if (iDiff > 0)
675 {
676 /*
677 * Head of pDevices was attached.
678 */
679 PUSBDEVICE pNew = pDevices;
680 pDevices = pDevices->pNext;
681 pNew->pPrev = pNew->pNext = NULL;
682
683 ComObjPtr<HostUSBDevice> NewObj;
684 NewObj.createObject();
685 NewObj->init(pNew, this);
686 LogFlowThisFunc(("attached %p {%s} %s / %p:{.idVendor=%#06x, .idProduct=%#06x, .pszProduct=\"%s\", .pszManufacturer=\"%s\"}\n",
687 (HostUSBDevice *)NewObj,
688 NewObj->i_getName().c_str(),
689 NewObj->i_getStateName(),
690 pNew,
691 pNew->idVendor,
692 pNew->idProduct,
693 pNew->pszProduct,
694 pNew->pszManufacturer));
695
696 m_llDevices.insert(it, NewObj);
697
698 devLock.release();
699 alock.release();
700 /* Do any backend specific work. */
701 deviceAdded(NewObj, pNew);
702 m_pUsbProxyService->i_deviceAdded(NewObj, pNew);
703 alock.acquire();
704 }
705 else
706 {
707 /*
708 * Check if the device was actually detached or logically detached
709 * as the result of a re-enumeration.
710 */
711 if (!pHostDevice->i_wasActuallyDetached())
712 ++it;
713 else
714 {
715 it = m_llDevices.erase(it);
716 devLock.release();
717 alock.release();
718 m_pUsbProxyService->i_deviceRemoved(pHostDevice);
719 LogFlowThisFunc(("detached %p {%s}\n",
720 (HostUSBDevice *)pHostDevice,
721 pHostDevice->i_getName().c_str()));
722
723 /* from now on, the object is no more valid,
724 * uninitialize to avoid abuse */
725 devCaller.release();
726 pHostDevice->uninit();
727 alock.acquire();
728 }
729 }
730 }
731 } /* while */
732
733 LogFlowThisFunc(("returns void\n"));
734}
735
736/* vi: set tabstop=4 shiftwidth=4 expandtab: */
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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