VirtualBox

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

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

Main/USBProxyBackend: Add flag for each backend indicating whether we have to fake the device state update or Linux and USB/IP backends don't work

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