VirtualBox

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

Last change on this file since 98262 was 98262, checked in by vboxsync, 23 months ago

Main: rc() -> hrc()/vrc(). bugref:10223

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