VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/USBProxyService.cpp@ 37952

Last change on this file since 37952 was 37599, checked in by vboxsync, 13 years ago

Main/USBProxyService: implementation inheritance is not so great that we have to pretend to do it when we are not

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.9 KB
Line 
1/* $Id: USBProxyService.cpp 37599 2011-06-22 21:06:38Z vboxsync $ */
2/** @file
3 * VirtualBox USB Proxy Service (base) class.
4 */
5
6/*
7 * Copyright (C) 2006-2007 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 "USBProxyService.h"
19#include "HostUSBDeviceImpl.h"
20#include "HostImpl.h"
21#include "MachineImpl.h"
22#include "VirtualBoxImpl.h"
23
24#include "AutoCaller.h"
25#include "Logging.h"
26
27#include <VBox/com/array.h>
28#include <VBox/err.h>
29#include <iprt/asm.h>
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/mem.h>
33#include <iprt/string.h>
34
35
36/**
37 * Initialize data members.
38 */
39USBProxyService::USBProxyService(Host *aHost)
40 : mHost(aHost), mThread(NIL_RTTHREAD), mTerminate(false), mLastError(VINF_SUCCESS), mDevices()
41{
42 LogFlowThisFunc(("aHost=%p\n", aHost));
43}
44
45
46/**
47 * Stub needed as long as the class isn't virtual
48 */
49HRESULT USBProxyService::init(void)
50{
51 return S_OK;
52}
53
54
55/**
56 * Empty destructor.
57 */
58USBProxyService::~USBProxyService()
59{
60 LogFlowThisFunc(("\n"));
61 Assert(mThread == NIL_RTTHREAD);
62 mDevices.clear();
63 mTerminate = true;
64 mHost = NULL;
65}
66
67
68/**
69 * Query if the service is active and working.
70 *
71 * @returns true if the service is up running.
72 * @returns false if the service isn't running.
73 */
74bool USBProxyService::isActive(void)
75{
76 return mThread != NIL_RTTHREAD;
77}
78
79
80/**
81 * Get last error.
82 * Can be used to check why the proxy !isActive() upon construction.
83 *
84 * @returns VBox status code.
85 */
86int USBProxyService::getLastError(void)
87{
88 return mLastError;
89}
90
91
92/**
93 * Get last error message.
94 * Can be used to check why the proxy !isActive() upon construction as an
95 * extension to getLastError(). May return a NULL error.
96 *
97 * @param
98 * @returns VBox status code.
99 */
100HRESULT USBProxyService::getLastErrorMessage(BSTR *aError)
101{
102 AssertPtrReturn(aError, E_POINTER);
103 mLastErrorMessage.cloneTo(aError);
104 return S_OK;
105}
106
107
108/**
109 * We're using the Host object lock.
110 *
111 * This is just a temporary measure until all the USB refactoring is
112 * done, probably... For now it help avoiding deadlocks we don't have
113 * time to fix.
114 *
115 * @returns Lock handle.
116 */
117RWLockHandle *USBProxyService::lockHandle() const
118{
119 return mHost->lockHandle();
120}
121
122
123/**
124 * Gets the collection of USB devices, slave of Host::USBDevices.
125 *
126 * This is an interface for the HostImpl::USBDevices property getter.
127 *
128 *
129 * @param aUSBDevices Where to store the pointer to the collection.
130 *
131 * @returns COM status code.
132 *
133 * @remarks The caller must own the write lock of the host object.
134 */
135HRESULT USBProxyService::getDeviceCollection(ComSafeArrayOut(IHostUSBDevice *, aUSBDevices))
136{
137 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
138 CheckComArgOutSafeArrayPointerValid(aUSBDevices);
139
140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
141
142 SafeIfaceArray<IHostUSBDevice> Collection (mDevices);
143 Collection.detachTo(ComSafeArrayOutArg(aUSBDevices));
144
145 return S_OK;
146}
147
148
149/**
150 * Request capture of a specific device.
151 *
152 * This is in an interface for SessionMachine::CaptureUSBDevice(), which is
153 * an internal worker used by Console::AttachUSBDevice() from the VM process.
154 *
155 * When the request is completed, SessionMachine::onUSBDeviceAttach() will
156 * be called for the given machine object.
157 *
158 *
159 * @param aMachine The machine to attach the device to.
160 * @param aId The UUID of the USB device to capture and attach.
161 *
162 * @returns COM status code and error info.
163 *
164 * @remarks This method may operate synchronously as well as asynchronously. In the
165 * former case it will temporarily abandon locks because of IPC.
166 */
167HRESULT USBProxyService::captureDeviceForVM(SessionMachine *aMachine, IN_GUID aId)
168{
169 ComAssertRet(aMachine, E_INVALIDARG);
170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
171
172 /*
173 * Translate the device id into a device object.
174 */
175 ComObjPtr<HostUSBDevice> pHostDevice = findDeviceById(aId);
176 if (pHostDevice.isNull())
177 return setError(E_INVALIDARG,
178 tr("The USB device with UUID {%RTuuid} is not currently attached to the host"), Guid(aId).raw());
179
180 /*
181 * Try to capture the device
182 */
183 AutoWriteLock DevLock(pHostDevice COMMA_LOCKVAL_SRC_POS);
184 return pHostDevice->requestCaptureForVM(aMachine, true /* aSetError */);
185}
186
187
188/**
189 * Notification from VM process about USB device detaching progress.
190 *
191 * This is in an interface for SessionMachine::DetachUSBDevice(), which is
192 * an internal worker used by Console::DetachUSBDevice() from the VM process.
193 *
194 * @param aMachine The machine which is sending the notification.
195 * @param aId The UUID of the USB device is concerns.
196 * @param aDone \a false for the pre-action notification (necessary
197 * for advancing the device state to avoid confusing
198 * the guest).
199 * \a true for the post-action notification. The device
200 * will be subjected to all filters except those of
201 * of \a Machine.
202 *
203 * @returns COM status code.
204 *
205 * @remarks When \a aDone is \a true this method may end up doing IPC to other
206 * VMs when running filters. In these cases it will temporarily
207 * abandon its locks.
208 */
209HRESULT USBProxyService::detachDeviceFromVM(SessionMachine *aMachine, IN_GUID aId, bool aDone)
210{
211 LogFlowThisFunc(("aMachine=%p{%s} aId={%RTuuid} aDone=%RTbool\n",
212 aMachine,
213 aMachine->getName().c_str(),
214 Guid(aId).raw(),
215 aDone));
216
217 // get a list of all running machines while we're outside the lock
218 // (getOpenedMachines requests locks which are incompatible with the lock of the machines list)
219 SessionMachinesList llOpenedMachines;
220 mHost->parent()->getOpenedMachines(llOpenedMachines);
221
222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
223
224 ComObjPtr<HostUSBDevice> pHostDevice = findDeviceById(aId);
225 ComAssertRet(!pHostDevice.isNull(), E_FAIL);
226 AutoWriteLock DevLock(pHostDevice COMMA_LOCKVAL_SRC_POS);
227
228 /*
229 * Work the state machine.
230 */
231 LogFlowThisFunc(("id={%RTuuid} state=%s aDone=%RTbool name={%s}\n",
232 pHostDevice->getId().raw(), pHostDevice->getStateName(), aDone, pHostDevice->getName().c_str()));
233 bool fRunFilters = false;
234 HRESULT hrc = pHostDevice->onDetachFromVM(aMachine, aDone, &fRunFilters);
235
236 /*
237 * Run filters if necessary.
238 */
239 if ( SUCCEEDED(hrc)
240 && fRunFilters)
241 {
242 Assert(aDone && pHostDevice->getUnistate() == kHostUSBDeviceState_HeldByProxy && pHostDevice->getMachine().isNull());
243 HRESULT hrc2 = runAllFiltersOnDevice(pHostDevice, llOpenedMachines, aMachine);
244 ComAssertComRC(hrc2);
245 }
246 return hrc;
247}
248
249
250/**
251 * Apply filters for the machine to all eligible USB devices.
252 *
253 * This is in an interface for SessionMachine::CaptureUSBDevice(), which
254 * is an internal worker used by Console::AutoCaptureUSBDevices() from the
255 * VM process at VM startup.
256 *
257 * Matching devices will be attached to the VM and may result IPC back
258 * to the VM process via SessionMachine::onUSBDeviceAttach() depending
259 * on whether the device needs to be captured or not. If capture is
260 * required, SessionMachine::onUSBDeviceAttach() will be called
261 * asynchronously by the USB proxy service thread.
262 *
263 * @param aMachine The machine to capture devices for.
264 *
265 * @returns COM status code, perhaps with error info.
266 *
267 * @remarks Write locks the host object and may temporarily abandon
268 * its locks to perform IPC.
269 */
270HRESULT USBProxyService::autoCaptureDevicesForVM(SessionMachine *aMachine)
271{
272 LogFlowThisFunc(("aMachine=%p{%s}\n",
273 aMachine,
274 aMachine->getName().c_str()));
275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
276 AutoWriteLock mlock(aMachine COMMA_LOCKVAL_SRC_POS);
277
278 /*
279 * Make a copy of the list because we might have to exit and
280 * re-enter the lock protecting it. (This will not make copies
281 * of any HostUSBDevice objects, only reference them.)
282 */
283 HostUSBDeviceList ListCopy = mDevices;
284
285 for (HostUSBDeviceList::iterator it = ListCopy.begin();
286 it != ListCopy.end();
287 ++it)
288 {
289 ComObjPtr<HostUSBDevice> device = *it;
290 AutoWriteLock devLock(device COMMA_LOCKVAL_SRC_POS);
291 if ( device->getUnistate() == kHostUSBDeviceState_HeldByProxy
292 || device->getUnistate() == kHostUSBDeviceState_Unused
293 || device->getUnistate() == kHostUSBDeviceState_Capturable)
294 runMachineFilters(aMachine, device);
295 }
296
297 return S_OK;
298}
299
300
301/**
302 * Detach all USB devices currently attached to a VM.
303 *
304 * This is in an interface for SessionMachine::DetachAllUSBDevices(), which
305 * is an internal worker used by Console::powerDown() from the VM process
306 * at VM startup, and SessionMachine::uninit() at VM abend.
307 *
308 * This is, like #detachDeviceFromVM(), normally a two stage journey
309 * where \a aDone indicates where we are. In addition we may be called
310 * to clean up VMs that have abended, in which case there will be no
311 * preparatory call. Filters will be applied to the devices in the final
312 * call with the risk that we have to do some IPC when attaching them
313 * to other VMs.
314 *
315 * @param aMachine The machine to detach devices from.
316 *
317 * @returns COM status code, perhaps with error info.
318 *
319 * @remarks Write locks the host object and may temporarily abandon
320 * its locks to perform IPC.
321 */
322HRESULT USBProxyService::detachAllDevicesFromVM(SessionMachine *aMachine, bool aDone, bool aAbnormal)
323{
324 // get a list of all running machines while we're outside the lock
325 // (getOpenedMachines requests locks which are incompatible with the lock of the machines list)
326 SessionMachinesList llOpenedMachines;
327 mHost->parent()->getOpenedMachines(llOpenedMachines);
328
329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
330
331 /*
332 * Make a copy of the device list (not the HostUSBDevice objects, just
333 * the list) since we may end up performing IPC and temporarily have
334 * to abandon locks when applying filters.
335 */
336 HostUSBDeviceList ListCopy = mDevices;
337
338 for (HostUSBDeviceList::iterator It = ListCopy.begin();
339 It != ListCopy.end();
340 ++It)
341 {
342 ComObjPtr<HostUSBDevice> pHostDevice = *It;
343 AutoWriteLock devLock(pHostDevice COMMA_LOCKVAL_SRC_POS);
344 if (pHostDevice->getMachine() == aMachine)
345 {
346 /*
347 * Same procedure as in detachUSBDevice().
348 */
349 bool fRunFilters = false;
350 HRESULT hrc = pHostDevice->onDetachFromVM(aMachine, aDone, &fRunFilters, aAbnormal);
351 if ( SUCCEEDED(hrc)
352 && fRunFilters)
353 {
354 Assert(aDone && pHostDevice->getUnistate() == kHostUSBDeviceState_HeldByProxy && pHostDevice->getMachine().isNull());
355 HRESULT hrc2 = runAllFiltersOnDevice(pHostDevice, llOpenedMachines, aMachine);
356 ComAssertComRC(hrc2);
357 }
358 }
359 }
360
361 return S_OK;
362}
363
364
365/**
366 * Runs all the filters on the specified device.
367 *
368 * All filters mean global and active VM, with the exception of those
369 * belonging to \a aMachine. If a global ignore filter matched or if
370 * none of the filters matched, the device will be released back to
371 * the host.
372 *
373 * The device calling us here will be in the HeldByProxy, Unused, or
374 * Capturable state. The caller is aware that locks held might have
375 * to be abandond because of IPC and that the device might be in
376 * almost any state upon return.
377 *
378 *
379 * @returns COM status code (only parameter & state checks will fail).
380 * @param aDevice The USB device to apply filters to.
381 * @param aIgnoreMachine The machine to ignore filters from (we've just
382 * detached the device from this machine).
383 *
384 * @note The caller is expected to own both the device and Host write locks,
385 * and be prepared that these locks may be abandond temporarily.
386 */
387HRESULT USBProxyService::runAllFiltersOnDevice(ComObjPtr<HostUSBDevice> &aDevice,
388 SessionMachinesList &llOpenedMachines,
389 SessionMachine *aIgnoreMachine)
390{
391 LogFlowThisFunc(("{%s} ignoring=%p\n", aDevice->getName().c_str(), aIgnoreMachine));
392
393 /*
394 * Verify preconditions.
395 */
396 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
397 AssertReturn(aDevice->isWriteLockOnCurrentThread(), E_FAIL);
398 AssertMsgReturn(aDevice->isCapturableOrHeld(), ("{%s} %s\n", aDevice->getName().c_str(), aDevice->getStateName()), E_FAIL);
399
400 /*
401 * Get the lists we'll iterate.
402 */
403 Host::USBDeviceFilterList globalFilters;
404
405 mHost->getUSBFilters(&globalFilters);
406
407 /*
408 * Run global filters filerts first.
409 */
410 bool fHoldIt = false;
411 for (Host::USBDeviceFilterList::const_iterator it = globalFilters.begin();
412 it != globalFilters.end();
413 ++it)
414 {
415 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
416 const HostUSBDeviceFilter::Data &data = (*it)->getData();
417 if (aDevice->isMatch(data))
418 {
419 USBDeviceFilterAction_T action = USBDeviceFilterAction_Null;
420 (*it)->COMGETTER(Action)(&action);
421 if (action == USBDeviceFilterAction_Ignore)
422 {
423 /*
424 * Release the device to the host and we're done.
425 */
426 aDevice->requestReleaseToHost();
427 return S_OK;
428 }
429 if (action == USBDeviceFilterAction_Hold)
430 {
431 /*
432 * A device held by the proxy needs to be subjected
433 * to the machine filters.
434 */
435 fHoldIt = true;
436 break;
437 }
438 AssertMsgFailed(("action=%d\n", action));
439 }
440 }
441 globalFilters.clear();
442
443 /*
444 * Run the per-machine filters.
445 */
446 for (SessionMachinesList::const_iterator it = llOpenedMachines.begin();
447 it != llOpenedMachines.end();
448 ++it)
449 {
450 ComObjPtr<SessionMachine> pMachine = *it;
451
452 /* Skip the machine the device was just detached from. */
453 if ( aIgnoreMachine
454 && pMachine == aIgnoreMachine)
455 continue;
456
457 /* runMachineFilters takes care of checking the machine state. */
458 if (runMachineFilters(pMachine, aDevice))
459 {
460 LogFlowThisFunc(("{%s} attached to %p\n", aDevice->getName().c_str(), (void *)pMachine));
461 return S_OK;
462 }
463 }
464
465 /*
466 * No matching machine, so request hold or release depending
467 * on global filter match.
468 */
469 if (fHoldIt)
470 aDevice->requestHold();
471 else
472 aDevice->requestReleaseToHost();
473 return S_OK;
474}
475
476
477/**
478 * Runs the USB filters of the machine on the device.
479 *
480 * If a match is found we will request capture for VM. This may cause
481 * us to temporary abandon locks while doing IPC.
482 *
483 * @param aMachine Machine whose filters are to be run.
484 * @param aDevice The USB device in question.
485 * @returns @c true if the device has been or is being attached to the VM, @c false otherwise.
486 *
487 * @note Caller must own the USB and device locks for writing.
488 * @note Locks aMachine for reading.
489 */
490bool USBProxyService::runMachineFilters(SessionMachine *aMachine, ComObjPtr<HostUSBDevice> &aDevice)
491{
492 LogFlowThisFunc(("{%s} aMachine=%p \n", aDevice->getName().c_str(), aMachine));
493
494 /*
495 * Validate preconditions.
496 */
497 AssertReturn(aMachine, false);
498 AssertReturn(isWriteLockOnCurrentThread(), false);
499 AssertReturn(aDevice->isWriteLockOnCurrentThread(), false);
500 /* Let HostUSBDevice::requestCaptureToVM() validate the state. */
501
502 /*
503 * Do the job.
504 */
505 ULONG ulMaskedIfs;
506 if (aMachine->hasMatchingUSBFilter(aDevice, &ulMaskedIfs))
507 {
508 /* try to capture the device */
509 HRESULT hrc = aDevice->requestCaptureForVM(aMachine, false /* aSetError */, ulMaskedIfs);
510 return SUCCEEDED(hrc)
511 || hrc == E_UNEXPECTED /* bad device state, give up */;
512 }
513
514 return false;
515}
516
517
518/**
519 * A filter was inserted / loaded.
520 *
521 * @param aFilter Pointer to the inserted filter.
522 * @return ID of the inserted filter
523 */
524void *USBProxyService::insertFilter(PCUSBFILTER aFilter)
525{
526 // return non-NULL to fake success.
527 NOREF(aFilter);
528 return (void *)1;
529}
530
531
532/**
533 * A filter was removed.
534 *
535 * @param aId ID of the filter to remove
536 */
537void USBProxyService::removeFilter(void *aId)
538{
539 NOREF(aId);
540}
541
542
543/**
544 * A VM is trying to capture a device, do necessary preparations.
545 *
546 * @returns VBox status code.
547 * @param aDevice The device in question.
548 */
549int USBProxyService::captureDevice(HostUSBDevice *aDevice)
550{
551 NOREF(aDevice);
552 return VERR_NOT_IMPLEMENTED;
553}
554
555
556/**
557 * Notification that an async captureDevice() operation completed.
558 *
559 * This is used by the proxy to release temporary filters.
560 *
561 * @returns VBox status code.
562 * @param aDevice The device in question.
563 * @param aSuccess Whether it succeeded or failed.
564 */
565void USBProxyService::captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
566{
567 NOREF(aDevice);
568 NOREF(aSuccess);
569}
570
571
572/**
573 * The device is going to be detached from a VM.
574 *
575 * @param aDevice The device in question.
576 */
577void USBProxyService::detachingDevice(HostUSBDevice *aDevice)
578{
579 NOREF(aDevice);
580}
581
582
583/**
584 * A VM is releasing a device back to the host.
585 *
586 * @returns VBox status code.
587 * @param aDevice The device in question.
588 */
589int USBProxyService::releaseDevice(HostUSBDevice *aDevice)
590{
591 NOREF(aDevice);
592 return VERR_NOT_IMPLEMENTED;
593}
594
595
596/**
597 * Notification that an async releaseDevice() operation completed.
598 *
599 * This is used by the proxy to release temporary filters.
600 *
601 * @returns VBox status code.
602 * @param aDevice The device in question.
603 * @param aSuccess Whether it succeeded or failed.
604 */
605void USBProxyService::releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess)
606{
607 NOREF(aDevice);
608 NOREF(aSuccess);
609}
610
611
612// Internals
613/////////////////////////////////////////////////////////////////////////////
614
615
616/**
617 * Starts the service.
618 *
619 * @returns VBox status.
620 */
621int USBProxyService::start(void)
622{
623 int rc = VINF_SUCCESS;
624 if (mThread == NIL_RTTHREAD)
625 {
626 /*
627 * Force update before starting the poller thread.
628 */
629 rc = wait(0);
630 if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED || RT_SUCCESS(rc))
631 {
632 processChanges();
633
634 /*
635 * Create the poller thread which will look for changes.
636 */
637 mTerminate = false;
638 rc = RTThreadCreate(&mThread, USBProxyService::serviceThread, this,
639 0, RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "USBPROXY");
640 AssertRC(rc);
641 if (RT_SUCCESS(rc))
642 LogFlowThisFunc(("started mThread=%RTthrd\n", mThread));
643 else
644 mThread = NIL_RTTHREAD;
645 }
646 mLastError = rc;
647 }
648 else
649 LogFlowThisFunc(("already running, mThread=%RTthrd\n", mThread));
650 return rc;
651}
652
653
654/**
655 * Stops the service.
656 *
657 * @returns VBox status.
658 */
659int USBProxyService::stop(void)
660{
661 int rc = VINF_SUCCESS;
662 if (mThread != NIL_RTTHREAD)
663 {
664 /*
665 * Mark the thread for termination and kick it.
666 */
667 ASMAtomicXchgSize(&mTerminate, true);
668 rc = interruptWait();
669 AssertRC(rc);
670
671 /*
672 * Wait for the thread to finish and then update the state.
673 */
674 rc = RTThreadWait(mThread, 60000, NULL);
675 if (rc == VERR_INVALID_HANDLE)
676 rc = VINF_SUCCESS;
677 if (RT_SUCCESS(rc))
678 {
679 LogFlowThisFunc(("stopped mThread=%RTthrd\n", mThread));
680 mThread = NIL_RTTHREAD;
681 mTerminate = false;
682 }
683 else
684 {
685 AssertRC(rc);
686 mLastError = rc;
687 }
688 }
689 else
690 LogFlowThisFunc(("not active\n"));
691
692 return rc;
693}
694
695
696/**
697 * The service thread created by start().
698 *
699 * @param Thread The thread handle.
700 * @param pvUser Pointer to the USBProxyService instance.
701 */
702/*static*/ DECLCALLBACK(int) USBProxyService::serviceThread(RTTHREAD /* Thread */, void *pvUser)
703{
704 USBProxyService *pThis = (USBProxyService *)pvUser;
705 LogFlowFunc(("pThis=%p\n", pThis));
706 pThis->serviceThreadInit();
707 int rc = VINF_SUCCESS;
708
709 /*
710 * Processing loop.
711 */
712 for (;;)
713 {
714 rc = pThis->wait(RT_INDEFINITE_WAIT);
715 if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED && rc != VERR_TIMEOUT)
716 break;
717 if (pThis->mTerminate)
718 break;
719 pThis->processChanges();
720 }
721
722 pThis->serviceThreadTerm();
723 LogFlowFunc(("returns %Rrc\n", rc));
724 return rc;
725}
726
727
728/**
729 * First call made on the service thread, use it to do
730 * thread initialization.
731 *
732 * The default implementation in USBProxyService just a dummy stub.
733 */
734void USBProxyService::serviceThreadInit(void)
735{
736}
737
738
739/**
740 * Last call made on the service thread, use it to do
741 * thread termination.
742 */
743void USBProxyService::serviceThreadTerm(void)
744{
745}
746
747
748/**
749 * Wait for a change in the USB devices attached to the host.
750 *
751 * The default implementation in USBProxyService just a dummy stub.
752 *
753 * @returns VBox status code. VERR_INTERRUPTED and VERR_TIMEOUT are considered
754 * harmless, while all other error status are fatal.
755 * @param aMillies Number of milliseconds to wait.
756 */
757int USBProxyService::wait(RTMSINTERVAL aMillies)
758{
759 return RTThreadSleep(RT_MIN(aMillies, 250));
760}
761
762
763/**
764 * Interrupt any wait() call in progress.
765 *
766 * The default implementation in USBProxyService just a dummy stub.
767 *
768 * @returns VBox status.
769 */
770int USBProxyService::interruptWait(void)
771{
772 return VERR_NOT_IMPLEMENTED;
773}
774
775
776/**
777 * Sort a list of USB devices.
778 *
779 * @returns Pointer to the head of the sorted doubly linked list.
780 * @param aDevices Head pointer (can be both singly and doubly linked list).
781 */
782static PUSBDEVICE sortDevices(PUSBDEVICE pDevices)
783{
784 PUSBDEVICE pHead = NULL;
785 PUSBDEVICE pTail = NULL;
786 while (pDevices)
787 {
788 /* unlink head */
789 PUSBDEVICE pDev = pDevices;
790 pDevices = pDev->pNext;
791 if (pDevices)
792 pDevices->pPrev = NULL;
793
794 /* find location. */
795 PUSBDEVICE pCur = pTail;
796 while ( pCur
797 && HostUSBDevice::compare(pCur, pDev) > 0)
798 pCur = pCur->pPrev;
799
800 /* insert (after pCur) */
801 pDev->pPrev = pCur;
802 if (pCur)
803 {
804 pDev->pNext = pCur->pNext;
805 pCur->pNext = pDev;
806 if (pDev->pNext)
807 pDev->pNext->pPrev = pDev;
808 else
809 pTail = pDev;
810 }
811 else
812 {
813 pDev->pNext = pHead;
814 if (pHead)
815 pHead->pPrev = pDev;
816 else
817 pTail = pDev;
818 pHead = pDev;
819 }
820 }
821
822 return pHead;
823}
824
825
826/**
827 * Process any relevant changes in the attached USB devices.
828 *
829 * Except for the first call, this is always running on the service thread.
830 */
831void USBProxyService::processChanges(void)
832{
833 LogFlowThisFunc(("\n"));
834
835 /*
836 * Get the sorted list of USB devices.
837 */
838 PUSBDEVICE pDevices = getDevices();
839 pDevices = sortDevices(pDevices);
840
841 // get a list of all running machines while we're outside the lock
842 // (getOpenedMachines requests locks which are incompatible with the lock of the machines list)
843 SessionMachinesList llOpenedMachines;
844 mHost->parent()->getOpenedMachines(llOpenedMachines);
845
846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
847
848 /*
849 * Compare previous list with the previous list of devices
850 * and merge in any changes while notifying Host.
851 */
852 HostUSBDeviceList::iterator It = this->mDevices.begin();
853 while ( It != mDevices.end()
854 || pDevices)
855 {
856 ComObjPtr<HostUSBDevice> pHostDevice;
857
858 if (It != mDevices.end())
859 pHostDevice = *It;
860
861 /*
862 * Assert that the object is still alive (we still reference it in
863 * the collection and we're the only one who calls uninit() on it.
864 */
865 AutoCaller devCaller(pHostDevice.isNull() ? NULL : pHostDevice);
866 AssertComRC(devCaller.rc());
867
868 /*
869 * Lock the device object since we will read/write its
870 * properties. All Host callbacks also imply the object is locked.
871 */
872 AutoWriteLock devLock(pHostDevice.isNull() ? NULL : pHostDevice
873 COMMA_LOCKVAL_SRC_POS);
874
875 /*
876 * Compare.
877 */
878 int iDiff;
879 if (pHostDevice.isNull())
880 iDiff = 1;
881 else
882 {
883 if (!pDevices)
884 iDiff = -1;
885 else
886 iDiff = pHostDevice->compare(pDevices);
887 }
888 if (!iDiff)
889 {
890 /*
891 * The device still there, update the state and move on. The PUSBDEVICE
892 * structure is eaten by updateDeviceState / HostUSBDevice::updateState().
893 */
894 PUSBDEVICE pCur = pDevices;
895 pDevices = pDevices->pNext;
896 pCur->pPrev = pCur->pNext = NULL;
897
898 bool fRunFilters = false;
899 SessionMachine *pIgnoreMachine = NULL;
900 if (updateDeviceState(pHostDevice, pCur, &fRunFilters, &pIgnoreMachine))
901 deviceChanged(pHostDevice,
902 (fRunFilters ? &llOpenedMachines : NULL),
903 pIgnoreMachine);
904 It++;
905 }
906 else
907 {
908 if (iDiff > 0)
909 {
910 /*
911 * Head of pDevices was attached.
912 */
913 PUSBDEVICE pNew = pDevices;
914 pDevices = pDevices->pNext;
915 pNew->pPrev = pNew->pNext = NULL;
916
917 ComObjPtr<HostUSBDevice> NewObj;
918 NewObj.createObject();
919 NewObj->init(pNew, this);
920 Log(("USBProxyService::processChanges: attached %p {%s} %s / %p:{.idVendor=%#06x, .idProduct=%#06x, .pszProduct=\"%s\", .pszManufacturer=\"%s\"}\n",
921 (HostUSBDevice *)NewObj,
922 NewObj->getName().c_str(),
923 NewObj->getStateName(),
924 pNew,
925 pNew->idVendor,
926 pNew->idProduct,
927 pNew->pszProduct,
928 pNew->pszManufacturer));
929
930 mDevices.insert(It, NewObj);
931
932 /* Not really necessary to lock here, but make Assert checks happy. */
933 AutoWriteLock newDevLock(NewObj COMMA_LOCKVAL_SRC_POS);
934 deviceAdded(NewObj, llOpenedMachines, pNew);
935 }
936 else
937 {
938 /*
939 * Check if the device was actually detached or logically detached
940 * as the result of a re-enumeration.
941 */
942 if (!pHostDevice->wasActuallyDetached())
943 It++;
944 else
945 {
946 It = mDevices.erase(It);
947 deviceRemoved(pHostDevice);
948 Log(("USBProxyService::processChanges: detached %p {%s}\n",
949 (HostUSBDevice *)pHostDevice,
950 pHostDevice->getName().c_str()));
951
952 /* from now on, the object is no more valid,
953 * uninitialize to avoid abuse */
954 devCaller.release();
955 pHostDevice->uninit();
956 }
957 }
958 }
959 } /* while */
960
961 LogFlowThisFunc(("returns void\n"));
962}
963
964
965/**
966 * Get a list of USB device currently attached to the host.
967 *
968 * The default implementation in USBProxyService just a dummy stub.
969 *
970 * @returns Pointer to a list of USB devices.
971 * The list nodes are freed individually by calling freeDevice().
972 */
973PUSBDEVICE USBProxyService::getDevices(void)
974{
975 return NULL;
976}
977
978
979/**
980 * Performs the required actions when a device has been added.
981 *
982 * This means things like running filters and subsequent capturing and
983 * VM attaching. This may result in IPC and temporary lock abandonment.
984 *
985 * @param aDevice The device in question.
986 * @param aUSBDevice The USB device structure.
987 */
988void USBProxyService::deviceAdded(ComObjPtr<HostUSBDevice> &aDevice,
989 SessionMachinesList &llOpenedMachines,
990 PUSBDEVICE aUSBDevice)
991{
992 /*
993 * Validate preconditions.
994 */
995 AssertReturnVoid(isWriteLockOnCurrentThread());
996 AssertReturnVoid(aDevice->isWriteLockOnCurrentThread());
997 LogFlowThisFunc(("aDevice=%p name={%s} state=%s id={%RTuuid}\n",
998 (HostUSBDevice *)aDevice,
999 aDevice->getName().c_str(),
1000 aDevice->getStateName(),
1001 aDevice->getId().raw()));
1002
1003 /*
1004 * Run filters on the device.
1005 */
1006 if (aDevice->isCapturableOrHeld())
1007 {
1008 HRESULT rc = runAllFiltersOnDevice(aDevice, llOpenedMachines, NULL /* aIgnoreMachine */);
1009 AssertComRC(rc);
1010 }
1011
1012 NOREF(aUSBDevice);
1013}
1014
1015
1016/**
1017 * Remove device notification hook for the OS specific code.
1018 *
1019 * This is means things like
1020 *
1021 * @param aDevice The device in question.
1022 */
1023void USBProxyService::deviceRemoved(ComObjPtr<HostUSBDevice> &aDevice)
1024{
1025 /*
1026 * Validate preconditions.
1027 */
1028 AssertReturnVoid(isWriteLockOnCurrentThread());
1029 AssertReturnVoid(aDevice->isWriteLockOnCurrentThread());
1030 LogFlowThisFunc(("aDevice=%p name={%s} state=%s id={%RTuuid}\n",
1031 (HostUSBDevice *)aDevice,
1032 aDevice->getName().c_str(),
1033 aDevice->getStateName(),
1034 aDevice->getId().raw()));
1035
1036 /*
1037 * Detach the device from any machine currently using it,
1038 * reset all data and uninitialize the device object.
1039 */
1040 aDevice->onPhysicalDetached();
1041}
1042
1043
1044/**
1045 * Implement fake capture, ++.
1046 *
1047 * @returns true if there is a state change.
1048 * @param pDevice The device in question.
1049 * @param pUSBDevice The USB device structure for the last enumeration.
1050 * @param aRunFilters Whether or not to run filters.
1051 */
1052bool USBProxyService::updateDeviceStateFake(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine)
1053{
1054 *aRunFilters = false;
1055 *aIgnoreMachine = NULL;
1056 AssertReturn(aDevice, false);
1057 AssertReturn(aDevice->isWriteLockOnCurrentThread(), false);
1058
1059 /*
1060 * Just hand it to the device, it knows best what needs to be done.
1061 */
1062 return aDevice->updateStateFake(aUSBDevice, aRunFilters, aIgnoreMachine);
1063}
1064
1065
1066/**
1067 * Updates the device state.
1068 *
1069 * This is responsible for calling HostUSBDevice::updateState().
1070 *
1071 * @returns true if there is a state change.
1072 * @param aDevice The device in question.
1073 * @param aUSBDevice The USB device structure for the last enumeration.
1074 * @param aRunFilters Whether or not to run filters.
1075 * @param aIgnoreMachine Machine to ignore when running filters.
1076 */
1077bool USBProxyService::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine)
1078{
1079 AssertReturn(aDevice, false);
1080 AssertReturn(aDevice->isWriteLockOnCurrentThread(), false);
1081
1082 return aDevice->updateState(aUSBDevice, aRunFilters, aIgnoreMachine);
1083}
1084
1085
1086/**
1087 * Handle a device which state changed in some significant way.
1088 *
1089 * This means things like running filters and subsequent capturing and
1090 * VM attaching. This may result in IPC and temporary lock abandonment.
1091 *
1092 * @param aDevice The device.
1093 * @param pllOpenedMachines list of running session machines (VirtualBox::getOpenedMachines()); if NULL, we don't run filters
1094 * @param aIgnoreMachine Machine to ignore when running filters.
1095 */
1096void USBProxyService::deviceChanged(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList *pllOpenedMachines, SessionMachine *aIgnoreMachine)
1097{
1098 /*
1099 * Validate preconditions.
1100 */
1101 AssertReturnVoid(isWriteLockOnCurrentThread());
1102 AssertReturnVoid(aDevice->isWriteLockOnCurrentThread());
1103 LogFlowThisFunc(("aDevice=%p name={%s} state=%s id={%RTuuid} aRunFilters=%RTbool aIgnoreMachine=%p\n",
1104 (HostUSBDevice *)aDevice,
1105 aDevice->getName().c_str(),
1106 aDevice->getStateName(),
1107 aDevice->getId().raw(),
1108 (pllOpenedMachines != NULL), // used to be "bool aRunFilters"
1109 aIgnoreMachine));
1110
1111 /*
1112 * Run filters if requested to do so.
1113 */
1114 if (pllOpenedMachines)
1115 {
1116 HRESULT rc = runAllFiltersOnDevice(aDevice, *pllOpenedMachines, aIgnoreMachine);
1117 AssertComRC(rc);
1118 }
1119}
1120
1121
1122
1123/**
1124 * Free all the members of a USB device returned by getDevice().
1125 *
1126 * @param pDevice Pointer to the device.
1127 */
1128/*static*/ void
1129USBProxyService::freeDeviceMembers(PUSBDEVICE pDevice)
1130{
1131 RTStrFree((char *)pDevice->pszManufacturer);
1132 pDevice->pszManufacturer = NULL;
1133 RTStrFree((char *)pDevice->pszProduct);
1134 pDevice->pszProduct = NULL;
1135 RTStrFree((char *)pDevice->pszSerialNumber);
1136 pDevice->pszSerialNumber = NULL;
1137
1138 RTStrFree((char *)pDevice->pszAddress);
1139 pDevice->pszAddress = NULL;
1140#ifdef RT_OS_WINDOWS
1141 RTStrFree(pDevice->pszAltAddress);
1142 pDevice->pszAltAddress = NULL;
1143 RTStrFree(pDevice->pszHubName);
1144 pDevice->pszHubName = NULL;
1145#elif defined(RT_OS_SOLARIS)
1146 RTStrFree(pDevice->pszDevicePath);
1147 pDevice->pszDevicePath = NULL;
1148#endif
1149}
1150
1151
1152/**
1153 * Free one USB device returned by getDevice().
1154 *
1155 * @param pDevice Pointer to the device.
1156 */
1157/*static*/ void
1158USBProxyService::freeDevice(PUSBDEVICE pDevice)
1159{
1160 freeDeviceMembers(pDevice);
1161 RTMemFree(pDevice);
1162}
1163
1164
1165/**
1166 * Initializes a filter with the data from the specified device.
1167 *
1168 * @param aFilter The filter to fill.
1169 * @param aDevice The device to fill it with.
1170 */
1171/*static*/ void
1172USBProxyService::initFilterFromDevice(PUSBFILTER aFilter, HostUSBDevice *aDevice)
1173{
1174 PCUSBDEVICE pDev = aDevice->mUsb;
1175 int vrc;
1176
1177 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_VENDOR_ID, pDev->idVendor, true); AssertRC(vrc);
1178 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_PRODUCT_ID, pDev->idProduct, true); AssertRC(vrc);
1179 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_REV, pDev->bcdDevice, true); AssertRC(vrc);
1180 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_CLASS, pDev->bDeviceClass, true); AssertRC(vrc);
1181 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_SUB_CLASS, pDev->bDeviceSubClass, true); AssertRC(vrc);
1182 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_DEVICE_PROTOCOL, pDev->bDeviceProtocol, true); AssertRC(vrc);
1183 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_PORT, pDev->bPort, true); AssertRC(vrc);
1184 vrc = USBFilterSetNumExact(aFilter, USBFILTERIDX_BUS, pDev->bBus, true); AssertRC(vrc);
1185 if (pDev->pszSerialNumber)
1186 {
1187 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_SERIAL_NUMBER_STR, pDev->pszSerialNumber, true);
1188 AssertRC(vrc);
1189 }
1190 if (pDev->pszProduct)
1191 {
1192 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_PRODUCT_STR, pDev->pszProduct, true);
1193 AssertRC(vrc);
1194 }
1195 if (pDev->pszManufacturer)
1196 {
1197 vrc = USBFilterSetStringExact(aFilter, USBFILTERIDX_MANUFACTURER_STR, pDev->pszManufacturer, true);
1198 AssertRC(vrc);
1199 }
1200}
1201
1202
1203/**
1204 * Searches the list of devices (mDevices) for the given device.
1205 *
1206 *
1207 * @returns Smart pointer to the device on success, NULL otherwise.
1208 * @param aId The UUID of the device we're looking for.
1209 */
1210ComObjPtr<HostUSBDevice> USBProxyService::findDeviceById(IN_GUID aId)
1211{
1212 Guid Id(aId);
1213 ComObjPtr<HostUSBDevice> Dev;
1214 for (HostUSBDeviceList::iterator It = mDevices.begin();
1215 It != mDevices.end();
1216 ++It)
1217 if ((*It)->getId() == Id)
1218 {
1219 Dev = (*It);
1220 break;
1221 }
1222
1223 return Dev;
1224}
1225
1226/*static*/
1227HRESULT USBProxyService::setError(HRESULT aResultCode, const char *aText, ...)
1228{
1229 va_list va;
1230 va_start(va, aText);
1231 HRESULT rc = VirtualBoxBase::setErrorInternal(aResultCode,
1232 COM_IIDOF(IHost),
1233 "USBProxyService",
1234 Utf8StrFmt(aText, va),
1235 false /* aWarning*/,
1236 true /* aLogIt*/);
1237 va_end(va);
1238 return rc;
1239}
1240
1241/* 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