VirtualBox

source: vbox/trunk/src/VBox/Main/USBControllerImpl.cpp@ 26060

Last change on this file since 26060 was 26046, checked in by vboxsync, 15 years ago

Main: move Auto*StateDependency templates out of MachineImpl.h

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.9 KB
Line 
1/* $Id: USBControllerImpl.cpp 26046 2010-01-26 14:06:05Z vboxsync $ */
2/** @file
3 * Implementation of IUSBController.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "USBControllerImpl.h"
23
24#include "Global.h"
25#include "MachineImpl.h"
26#include "VirtualBoxImpl.h"
27#include "HostImpl.h"
28#ifdef VBOX_WITH_USB
29# include "USBDeviceImpl.h"
30# include "HostUSBDeviceImpl.h"
31# include "USBProxyService.h"
32#endif
33
34#include <iprt/string.h>
35#include <iprt/cpp/utils.h>
36
37#include <VBox/err.h>
38#include <VBox/settings.h>
39
40#include <algorithm>
41
42#include "AutoStateDep.h"
43#include "AutoCaller.h"
44#include "Logging.h"
45
46// defines
47/////////////////////////////////////////////////////////////////////////////
48
49typedef std::list< ComObjPtr<USBDeviceFilter> > DeviceFilterList;
50
51struct BackupableUSBData
52{
53 BackupableUSBData()
54 : fEnabled(false),
55 fEnabledEHCI(false)
56 { }
57
58 bool operator==(const BackupableUSBData &that) const
59 {
60 return this == &that || (fEnabled == that.fEnabled && fEnabledEHCI == that.fEnabledEHCI);
61 }
62
63 BOOL fEnabled;
64 BOOL fEnabledEHCI;
65};
66
67struct USBController::Data
68{
69 Data() {};
70 ~Data() {};
71
72 /** Parent object. */
73 const ComObjPtr<Machine, ComWeakRef> pParent;
74 /** Peer object. */
75 const ComObjPtr<USBController> pPeer;
76
77 Backupable<BackupableUSBData> bd;
78#ifdef VBOX_WITH_USB
79 // the following fields need special backup/rollback/commit handling,
80 // so they cannot be a part of BackupableData
81 Backupable<DeviceFilterList> llDeviceFilters;
82#endif
83};
84
85
86
87// constructor / destructor
88/////////////////////////////////////////////////////////////////////////////
89
90DEFINE_EMPTY_CTOR_DTOR (USBController)
91
92HRESULT USBController::FinalConstruct()
93{
94 return S_OK;
95}
96
97void USBController::FinalRelease()
98{
99 uninit();
100}
101
102// public initializer/uninitializer for internal purposes only
103/////////////////////////////////////////////////////////////////////////////
104
105/**
106 * Initializes the USB controller object.
107 *
108 * @returns COM result indicator.
109 * @param aParent Pointer to our parent object.
110 */
111HRESULT USBController::init(Machine *aParent)
112{
113 LogFlowThisFunc(("aParent=%p\n", aParent));
114
115 ComAssertRet (aParent, E_INVALIDARG);
116
117 /* Enclose the state transition NotReady->InInit->Ready */
118 AutoInitSpan autoInitSpan(this);
119 AssertReturn(autoInitSpan.isOk(), E_FAIL);
120
121 m = new Data();
122
123 unconst(m->pParent) = aParent;
124 /* mPeer is left null */
125
126 m->bd.allocate();
127#ifdef VBOX_WITH_USB
128 m->llDeviceFilters.allocate();
129#endif
130
131 /* Confirm a successful initialization */
132 autoInitSpan.setSucceeded();
133
134 return S_OK;
135}
136
137/**
138 * Initializes the USB controller object given another USB controller object
139 * (a kind of copy constructor). This object shares data with
140 * the object passed as an argument.
141 *
142 * @returns COM result indicator.
143 * @param aParent Pointer to our parent object.
144 * @param aPeer The object to share.
145 *
146 * @note This object must be destroyed before the original object
147 * it shares data with is destroyed.
148 */
149HRESULT USBController::init(Machine *aParent, USBController *aPeer)
150{
151 LogFlowThisFunc(("aParent=%p, aPeer=%p\n", aParent, aPeer));
152
153 ComAssertRet (aParent && aPeer, E_INVALIDARG);
154
155 /* Enclose the state transition NotReady->InInit->Ready */
156 AutoInitSpan autoInitSpan(this);
157 AssertReturn(autoInitSpan.isOk(), E_FAIL);
158
159 m = new Data();
160
161 unconst(m->pParent) = aParent;
162 unconst(m->pPeer) = aPeer;
163
164 AutoWriteLock thatlock(aPeer COMMA_LOCKVAL_SRC_POS);
165 m->bd.share(aPeer->m->bd);
166
167#ifdef VBOX_WITH_USB
168 /* create copies of all filters */
169 m->llDeviceFilters.allocate();
170 DeviceFilterList::const_iterator it = aPeer->m->llDeviceFilters->begin();
171 while (it != aPeer->m->llDeviceFilters->end())
172 {
173 ComObjPtr<USBDeviceFilter> filter;
174 filter.createObject();
175 filter->init (this, *it);
176 m->llDeviceFilters->push_back(filter);
177 ++ it;
178 }
179#endif /* VBOX_WITH_USB */
180
181 /* Confirm a successful initialization */
182 autoInitSpan.setSucceeded();
183
184 return S_OK;
185}
186
187
188/**
189 * Initializes the USB controller object given another guest object
190 * (a kind of copy constructor). This object makes a private copy of data
191 * of the original object passed as an argument.
192 */
193HRESULT USBController::initCopy(Machine *aParent, USBController *aPeer)
194{
195 LogFlowThisFunc(("aParent=%p, aPeer=%p\n", aParent, aPeer));
196
197 ComAssertRet (aParent && aPeer, E_INVALIDARG);
198
199 /* Enclose the state transition NotReady->InInit->Ready */
200 AutoInitSpan autoInitSpan(this);
201 AssertReturn(autoInitSpan.isOk(), E_FAIL);
202
203 m = new Data();
204
205 unconst(m->pParent) = aParent;
206 /* mPeer is left null */
207
208 AutoWriteLock thatlock(aPeer COMMA_LOCKVAL_SRC_POS);
209 m->bd.attachCopy(aPeer->m->bd);
210
211#ifdef VBOX_WITH_USB
212 /* create private copies of all filters */
213 m->llDeviceFilters.allocate();
214 DeviceFilterList::const_iterator it = aPeer->m->llDeviceFilters->begin();
215 while (it != aPeer->m->llDeviceFilters->end())
216 {
217 ComObjPtr<USBDeviceFilter> filter;
218 filter.createObject();
219 filter->initCopy(this, *it);
220 m->llDeviceFilters->push_back(filter);
221 ++ it;
222 }
223#endif /* VBOX_WITH_USB */
224
225 /* Confirm a successful initialization */
226 autoInitSpan.setSucceeded();
227
228 return S_OK;
229}
230
231
232/**
233 * Uninitializes the instance and sets the ready flag to FALSE.
234 * Called either from FinalRelease() or by the parent when it gets destroyed.
235 */
236void USBController::uninit()
237{
238 LogFlowThisFunc(("\n"));
239
240 /* Enclose the state transition Ready->InUninit->NotReady */
241 AutoUninitSpan autoUninitSpan(this);
242 if (autoUninitSpan.uninitDone())
243 return;
244
245#ifdef VBOX_WITH_USB
246 // uninit all device filters on the list (it's a standard std::list not an ObjectsList
247 // so we must uninit() manually)
248 for (DeviceFilterList::iterator it = m->llDeviceFilters->begin();
249 it != m->llDeviceFilters->end();
250 ++it)
251 (*it)->uninit();
252
253 m->llDeviceFilters.free();
254#endif
255 m->bd.free();
256
257 unconst(m->pPeer).setNull();
258 unconst(m->pParent).setNull();
259
260 delete m;
261 m = NULL;
262}
263
264
265// IUSBController properties
266/////////////////////////////////////////////////////////////////////////////
267
268STDMETHODIMP USBController::COMGETTER(Enabled) (BOOL *aEnabled)
269{
270 CheckComArgOutPointerValid(aEnabled);
271
272 AutoCaller autoCaller(this);
273 if (FAILED(autoCaller.rc())) return autoCaller.rc();
274
275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
276
277 *aEnabled = m->bd->fEnabled;
278
279 return S_OK;
280}
281
282
283STDMETHODIMP USBController::COMSETTER(Enabled) (BOOL aEnabled)
284{
285 LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled));
286
287 AutoCaller autoCaller(this);
288 if (FAILED(autoCaller.rc())) return autoCaller.rc();
289
290 /* the machine needs to be mutable */
291 AutoMutableStateDependency adep(m->pParent);
292 if (FAILED(adep.rc())) return adep.rc();
293
294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
295
296 if (m->bd->fEnabled != aEnabled)
297 {
298 m->bd.backup();
299 m->bd->fEnabled = aEnabled;
300
301 /* leave the lock for safety */
302 alock.leave();
303
304 m->pParent->onUSBControllerChange();
305 }
306
307 return S_OK;
308}
309
310STDMETHODIMP USBController::COMGETTER(EnabledEhci) (BOOL *aEnabled)
311{
312 CheckComArgOutPointerValid(aEnabled);
313
314 AutoCaller autoCaller(this);
315 if (FAILED(autoCaller.rc())) return autoCaller.rc();
316
317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
318
319 *aEnabled = m->bd->fEnabledEHCI;
320
321 return S_OK;
322}
323
324STDMETHODIMP USBController::COMSETTER(EnabledEhci) (BOOL aEnabled)
325{
326 LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled));
327
328 AutoCaller autoCaller(this);
329 if (FAILED(autoCaller.rc())) return autoCaller.rc();
330
331 /* the machine needs to be mutable */
332 AutoMutableStateDependency adep(m->pParent);
333 if (FAILED(adep.rc())) return adep.rc();
334
335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
336
337 if (m->bd->fEnabledEHCI != aEnabled)
338 {
339 m->bd.backup();
340 m->bd->fEnabledEHCI = aEnabled;
341
342 /* leave the lock for safety */
343 alock.leave();
344
345 m->pParent->onUSBControllerChange();
346 }
347
348 return S_OK;
349}
350
351STDMETHODIMP USBController::COMGETTER(USBStandard) (USHORT *aUSBStandard)
352{
353 CheckComArgOutPointerValid(aUSBStandard);
354
355 AutoCaller autoCaller(this);
356 if (FAILED(autoCaller.rc())) return autoCaller.rc();
357
358 /* not accessing data -- no need to lock */
359
360 /** @todo This is no longer correct */
361 *aUSBStandard = 0x0101;
362
363 return S_OK;
364}
365
366#ifndef VBOX_WITH_USB
367/**
368 * Fake class for build without USB.
369 * We need an empty collection & enum for deviceFilters, that's all.
370 */
371class ATL_NO_VTABLE USBDeviceFilter :
372 public VirtualBoxBase,
373 public VirtualBoxSupportErrorInfoImpl<USBDeviceFilter, IUSBDeviceFilter>,
374 public VirtualBoxSupportTranslation<USBDeviceFilter>,
375 public IUSBDeviceFilter
376{
377public:
378 DECLARE_NOT_AGGREGATABLE(USBDeviceFilter)
379 DECLARE_PROTECT_FINAL_CONSTRUCT()
380 BEGIN_COM_MAP(USBDeviceFilter)
381 COM_INTERFACE_ENTRY(ISupportErrorInfo)
382 COM_INTERFACE_ENTRY(IUSBDeviceFilter)
383 END_COM_MAP()
384
385 DECLARE_EMPTY_CTOR_DTOR (USBDeviceFilter)
386
387 // IUSBDeviceFilter properties
388 STDMETHOD(COMGETTER(Name)) (BSTR *aName);
389 STDMETHOD(COMSETTER(Name)) (IN_BSTR aName);
390 STDMETHOD(COMGETTER(Active)) (BOOL *aActive);
391 STDMETHOD(COMSETTER(Active)) (BOOL aActive);
392 STDMETHOD(COMGETTER(VendorId)) (BSTR *aVendorId);
393 STDMETHOD(COMSETTER(VendorId)) (IN_BSTR aVendorId);
394 STDMETHOD(COMGETTER(ProductId)) (BSTR *aProductId);
395 STDMETHOD(COMSETTER(ProductId)) (IN_BSTR aProductId);
396 STDMETHOD(COMGETTER(Revision)) (BSTR *aRevision);
397 STDMETHOD(COMSETTER(Revision)) (IN_BSTR aRevision);
398 STDMETHOD(COMGETTER(Manufacturer)) (BSTR *aManufacturer);
399 STDMETHOD(COMSETTER(Manufacturer)) (IN_BSTR aManufacturer);
400 STDMETHOD(COMGETTER(Product)) (BSTR *aProduct);
401 STDMETHOD(COMSETTER(Product)) (IN_BSTR aProduct);
402 STDMETHOD(COMGETTER(SerialNumber)) (BSTR *aSerialNumber);
403 STDMETHOD(COMSETTER(SerialNumber)) (IN_BSTR aSerialNumber);
404 STDMETHOD(COMGETTER(Port)) (BSTR *aPort);
405 STDMETHOD(COMSETTER(Port)) (IN_BSTR aPort);
406 STDMETHOD(COMGETTER(Remote)) (BSTR *aRemote);
407 STDMETHOD(COMSETTER(Remote)) (IN_BSTR aRemote);
408 STDMETHOD(COMGETTER(MaskedInterfaces)) (ULONG *aMaskedIfs);
409 STDMETHOD(COMSETTER(MaskedInterfaces)) (ULONG aMaskedIfs);
410};
411#endif /* !VBOX_WITH_USB */
412
413
414STDMETHODIMP USBController::COMGETTER(DeviceFilters) (ComSafeArrayOut(IUSBDeviceFilter *, aDevicesFilters))
415{
416#ifdef VBOX_WITH_USB
417 CheckComArgOutSafeArrayPointerValid(aDevicesFilters);
418
419 AutoCaller autoCaller(this);
420 if (FAILED(autoCaller.rc())) return autoCaller.rc();
421
422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
423
424 SafeIfaceArray<IUSBDeviceFilter> collection (*m->llDeviceFilters.data());
425 collection.detachTo(ComSafeArrayOutArg(aDevicesFilters));
426
427 return S_OK;
428#else
429 NOREF(aDevicesFilters);
430# ifndef RT_OS_WINDOWS
431 NOREF(aDevicesFiltersSize);
432# endif
433 ReturnComNotImplemented();
434#endif
435}
436
437// IUSBController methods
438/////////////////////////////////////////////////////////////////////////////
439
440STDMETHODIMP USBController::CreateDeviceFilter (IN_BSTR aName,
441 IUSBDeviceFilter **aFilter)
442{
443#ifdef VBOX_WITH_USB
444 CheckComArgOutPointerValid(aFilter);
445
446 CheckComArgStrNotEmptyOrNull(aName);
447
448 AutoCaller autoCaller(this);
449 if (FAILED(autoCaller.rc())) return autoCaller.rc();
450
451 /* the machine needs to be mutable */
452 AutoMutableStateDependency adep(m->pParent);
453 if (FAILED(adep.rc())) return adep.rc();
454
455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
456
457 ComObjPtr<USBDeviceFilter> filter;
458 filter.createObject();
459 HRESULT rc = filter->init (this, aName);
460 ComAssertComRCRetRC (rc);
461 rc = filter.queryInterfaceTo(aFilter);
462 AssertComRCReturnRC(rc);
463
464 return S_OK;
465#else
466 NOREF(aName);
467 NOREF(aFilter);
468 ReturnComNotImplemented();
469#endif
470}
471
472STDMETHODIMP USBController::InsertDeviceFilter (ULONG aPosition,
473 IUSBDeviceFilter *aFilter)
474{
475#ifdef VBOX_WITH_USB
476
477 CheckComArgNotNull(aFilter);
478
479 AutoCaller autoCaller(this);
480 if (FAILED(autoCaller.rc())) return autoCaller.rc();
481
482 /* the machine needs to be mutable */
483 AutoMutableStateDependency adep(m->pParent);
484 if (FAILED(adep.rc())) return adep.rc();
485
486 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
487
488 ComObjPtr<USBDeviceFilter> filter = static_cast<USBDeviceFilter*>(aFilter);
489 // @todo r=dj make sure the input object is actually from us
490// ComObjPtr<USBDeviceFilter> filter = getDependentChild(aFilter);
491// if (!filter)
492// return setError (E_INVALIDARG,
493// tr ("The given USB device filter is not created within "
494// "this VirtualBox instance"));
495
496 if (filter->mInList)
497 return setError (VBOX_E_INVALID_OBJECT_STATE,
498 tr ("The given USB device filter is already in the list"));
499
500 /* backup the list before modification */
501 m->llDeviceFilters.backup();
502
503 /* iterate to the position... */
504 DeviceFilterList::iterator it;
505 if (aPosition < m->llDeviceFilters->size())
506 {
507 it = m->llDeviceFilters->begin();
508 std::advance (it, aPosition);
509 }
510 else
511 it = m->llDeviceFilters->end();
512 /* ...and insert */
513 m->llDeviceFilters->insert (it, filter);
514 filter->mInList = true;
515
516 /* notify the proxy (only when it makes sense) */
517 if (filter->getData().mActive && Global::IsOnline(adep.machineState()))
518 {
519 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
520 ComAssertRet (service, E_FAIL);
521
522 ComAssertRet(filter->getId() == NULL, E_FAIL);
523 filter->getId() = service->insertFilter (&filter->getData().mUSBFilter);
524 }
525
526 return S_OK;
527
528#else /* VBOX_WITH_USB */
529
530 NOREF(aPosition);
531 NOREF(aFilter);
532 ReturnComNotImplemented();
533
534#endif /* VBOX_WITH_USB */
535}
536
537STDMETHODIMP USBController::RemoveDeviceFilter(ULONG aPosition,
538 IUSBDeviceFilter **aFilter)
539{
540#ifdef VBOX_WITH_USB
541
542 CheckComArgOutPointerValid(aFilter);
543
544 AutoCaller autoCaller(this);
545 if (FAILED(autoCaller.rc())) return autoCaller.rc();
546
547 /* the machine needs to be mutable */
548 AutoMutableStateDependency adep(m->pParent);
549 if (FAILED(adep.rc())) return adep.rc();
550
551 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
552
553 if (!m->llDeviceFilters->size())
554 return setError (E_INVALIDARG,
555 tr ("The USB device filter list is empty"));
556
557 if (aPosition >= m->llDeviceFilters->size())
558 return setError (E_INVALIDARG,
559 tr ("Invalid position: %lu (must be in range [0, %lu])"),
560 aPosition, m->llDeviceFilters->size() - 1);
561
562 /* backup the list before modification */
563 m->llDeviceFilters.backup();
564
565 ComObjPtr<USBDeviceFilter> filter;
566 {
567 /* iterate to the position... */
568 DeviceFilterList::iterator it = m->llDeviceFilters->begin();
569 std::advance (it, aPosition);
570 /* ...get an element from there... */
571 filter = *it;
572 /* ...and remove */
573 filter->mInList = false;
574 m->llDeviceFilters->erase (it);
575 }
576
577 /* cancel sharing (make an independent copy of data) */
578 filter->unshare();
579
580 filter.queryInterfaceTo(aFilter);
581
582 /* notify the proxy (only when it makes sense) */
583 if (filter->getData().mActive && Global::IsOnline(adep.machineState()))
584 {
585 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
586 ComAssertRet (service, E_FAIL);
587
588 ComAssertRet(filter->getId() != NULL, E_FAIL);
589 service->removeFilter(filter->getId());
590 filter->getId() = NULL;
591 }
592
593 return S_OK;
594
595#else /* VBOX_WITH_USB */
596
597 NOREF(aPosition);
598 NOREF(aFilter);
599 ReturnComNotImplemented();
600
601#endif /* VBOX_WITH_USB */
602}
603
604// public methods only for internal purposes
605/////////////////////////////////////////////////////////////////////////////
606
607/**
608 * Loads settings from the given machine node.
609 * May be called once right after this object creation.
610 *
611 * @param aMachineNode <Machine> node.
612 *
613 * @note Locks this object for writing.
614 */
615HRESULT USBController::loadSettings(const settings::USBController &data)
616{
617 AutoCaller autoCaller(this);
618 AssertComRCReturnRC(autoCaller.rc());
619
620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
621
622 /* Note: we assume that the default values for attributes of optional
623 * nodes are assigned in the Data::Data() constructor and don't do it
624 * here. It implies that this method may only be called after constructing
625 * a new BIOSSettings object while all its data fields are in the default
626 * values. Exceptions are fields whose creation time defaults don't match
627 * values that should be applied when these fields are not explicitly set
628 * in the settings file (for backwards compatibility reasons). This takes
629 * place when a setting of a newly created object must default to A while
630 * the same setting of an object loaded from the old settings file must
631 * default to B. */
632
633 m->bd->fEnabled = data.fEnabled;
634 m->bd->fEnabledEHCI = data.fEnabledEHCI;
635
636#ifdef VBOX_WITH_USB
637 for (settings::USBDeviceFiltersList::const_iterator it = data.llDeviceFilters.begin();
638 it != data.llDeviceFilters.end();
639 ++it)
640 {
641 const settings::USBDeviceFilter &f = *it;
642 ComObjPtr<USBDeviceFilter> pFilter;
643 pFilter.createObject();
644 HRESULT rc = pFilter->init(this, // parent
645 f);
646 if (FAILED(rc)) return rc;
647
648 m->llDeviceFilters->push_back(pFilter);
649 pFilter->mInList = true;
650 }
651#endif /* VBOX_WITH_USB */
652
653 return S_OK;
654}
655
656/**
657 * Saves settings to the given machine node.
658 *
659 * @param aMachineNode <Machine> node.
660 *
661 * @note Locks this object for reading.
662 */
663HRESULT USBController::saveSettings(settings::USBController &data)
664{
665 AutoCaller autoCaller(this);
666 if (FAILED(autoCaller.rc())) return autoCaller.rc();
667
668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
669
670 data.fEnabled = !!m->bd->fEnabled;
671 data.fEnabledEHCI = !!m->bd->fEnabledEHCI;
672
673#ifdef VBOX_WITH_USB
674 data.llDeviceFilters.clear();
675
676 for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
677 it != m->llDeviceFilters->end();
678 ++it)
679 {
680 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
681 const USBDeviceFilter::Data &filterData = (*it)->getData();
682
683 Bstr str;
684
685 settings::USBDeviceFilter f;
686 f.strName = filterData.mName;
687 f.fActive = !!filterData.mActive;
688 (*it)->COMGETTER(VendorId)(str.asOutParam());
689 f.strVendorId = str;
690 (*it)->COMGETTER(ProductId)(str.asOutParam());
691 f.strProductId = str;
692 (*it)->COMGETTER (Revision) (str.asOutParam());
693 f.strRevision = str;
694 (*it)->COMGETTER (Manufacturer) (str.asOutParam());
695 f.strManufacturer = str;
696 (*it)->COMGETTER (Product) (str.asOutParam());
697 f.strProduct = str;
698 (*it)->COMGETTER (SerialNumber) (str.asOutParam());
699 f.strSerialNumber = str;
700 (*it)->COMGETTER (Port) (str.asOutParam());
701 f.strPort = str;
702 f.strRemote = filterData.mRemote.string();
703 f.ulMaskedInterfaces = filterData.mMaskedIfs;
704
705 data.llDeviceFilters.push_back(f);
706 }
707#endif /* VBOX_WITH_USB */
708
709 return S_OK;
710}
711
712/** @note Locks objects for reading! */
713bool USBController::isModified()
714{
715 AutoCaller autoCaller(this);
716 AssertComRCReturn (autoCaller.rc(), false);
717
718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
719
720 if (m->bd.isBackedUp()
721#ifdef VBOX_WITH_USB
722 || m->llDeviceFilters.isBackedUp()
723#endif
724 )
725 return true;
726
727#ifdef VBOX_WITH_USB
728 /* see whether any of filters has changed its data */
729 for (DeviceFilterList::const_iterator
730 it = m->llDeviceFilters->begin();
731 it != m->llDeviceFilters->end();
732 ++ it)
733 {
734 if ((*it)->isModified())
735 return true;
736 }
737#endif /* VBOX_WITH_USB */
738
739 return false;
740}
741
742/** @note Locks objects for reading! */
743bool USBController::isReallyModified()
744{
745 AutoCaller autoCaller(this);
746 AssertComRCReturn (autoCaller.rc(), false);
747
748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
749
750 if (m->bd.hasActualChanges())
751 return true;
752
753#ifdef VBOX_WITH_USB
754 if (!m->llDeviceFilters.isBackedUp())
755 {
756 /* see whether any of filters has changed its data */
757 for (DeviceFilterList::const_iterator
758 it = m->llDeviceFilters->begin();
759 it != m->llDeviceFilters->end();
760 ++ it)
761 {
762 if ((*it)->isReallyModified())
763 return true;
764 }
765
766 return false;
767 }
768
769 if (m->llDeviceFilters->size() != m->llDeviceFilters.backedUpData()->size())
770 return true;
771
772 if (m->llDeviceFilters->size() == 0)
773 return false;
774
775 /* Make copies to speed up comparison */
776 DeviceFilterList devices = *m->llDeviceFilters.data();
777 DeviceFilterList backDevices = *m->llDeviceFilters.backedUpData();
778
779 DeviceFilterList::iterator it = devices.begin();
780 while (it != devices.end())
781 {
782 bool found = false;
783 DeviceFilterList::iterator thatIt = backDevices.begin();
784 while (thatIt != backDevices.end())
785 {
786 if ((*it)->getData() == (*thatIt)->getData())
787 {
788 backDevices.erase (thatIt);
789 found = true;
790 break;
791 }
792 else
793 ++ thatIt;
794 }
795 if (found)
796 it = devices.erase (it);
797 else
798 return false;
799 }
800
801 Assert (devices.size() == 0 && backDevices.size() == 0);
802#endif /* VBOX_WITH_USB */
803
804 return false;
805}
806
807/** @note Locks objects for writing! */
808bool USBController::rollback()
809{
810 AutoCaller autoCaller(this);
811 AssertComRCReturn (autoCaller.rc(), false);
812
813 /* we need the machine state */
814 AutoAnyStateDependency adep(m->pParent);
815 AssertComRCReturn (adep.rc(), false);
816
817 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
818
819 bool dataChanged = false;
820
821 if (m->bd.isBackedUp())
822 {
823 /* we need to check all data to see whether anything will be changed
824 * after rollback */
825 dataChanged = m->bd.hasActualChanges();
826 m->bd.rollback();
827 }
828
829#ifdef VBOX_WITH_USB
830
831 if (m->llDeviceFilters.isBackedUp())
832 {
833 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
834 ComAssertRet (service, false);
835
836 /* uninitialize all new filters (absent in the backed up list) */
837 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
838 DeviceFilterList *backedList = m->llDeviceFilters.backedUpData();
839 while (it != m->llDeviceFilters->end())
840 {
841 if (std::find (backedList->begin(), backedList->end(), *it) ==
842 backedList->end())
843 {
844 /* notify the proxy (only when it makes sense) */
845 if ((*it)->getData().mActive &&
846 Global::IsOnline (adep.machineState()))
847 {
848 USBDeviceFilter *filter = *it;
849 ComAssertRet(filter->getId() != NULL, false);
850 service->removeFilter(filter->getId());
851 filter->getId() = NULL;
852 }
853
854 (*it)->uninit();
855 }
856 ++ it;
857 }
858
859 if (Global::IsOnline (adep.machineState()))
860 {
861 /* find all removed old filters (absent in the new list)
862 * and insert them back to the USB proxy */
863 it = backedList->begin();
864 while (it != backedList->end())
865 {
866 if (std::find (m->llDeviceFilters->begin(), m->llDeviceFilters->end(), *it) ==
867 m->llDeviceFilters->end())
868 {
869 /* notify the proxy (only when necessary) */
870 if ((*it)->getData().mActive)
871 {
872 USBDeviceFilter *flt = *it; /* resolve ambiguity */
873 ComAssertRet(flt->getId() == NULL, false);
874 flt->getId() = service->insertFilter(&flt->getData().mUSBFilter);
875 }
876 }
877 ++ it;
878 }
879 }
880
881 /* restore the list */
882 m->llDeviceFilters.rollback();
883 }
884
885 /* here we don't depend on the machine state any more */
886 adep.release();
887
888 /* rollback any changes to filters after restoring the list */
889 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
890 while (it != m->llDeviceFilters->end())
891 {
892 if ((*it)->isModified())
893 {
894 (*it)->rollback();
895 /* call this to notify the USB proxy about changes */
896 onDeviceFilterChange (*it);
897 }
898 ++ it;
899 }
900
901#endif /* VBOX_WITH_USB */
902
903 return dataChanged;
904}
905
906/**
907 * @note Locks this object for writing, together with the peer object (also
908 * for writing) if there is one.
909 */
910void USBController::commit()
911{
912 /* sanity */
913 AutoCaller autoCaller(this);
914 AssertComRCReturnVoid (autoCaller.rc());
915
916 /* sanity too */
917 AutoCaller peerCaller(m->pPeer);
918 AssertComRCReturnVoid (peerCaller.rc());
919
920 /* lock both for writing since we modify both (mPeer is "master" so locked
921 * first) */
922 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
923
924 if (m->bd.isBackedUp())
925 {
926 m->bd.commit();
927 if (m->pPeer)
928 {
929 /* attach new data to the peer and reshare it */
930 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
931 m->pPeer->m->bd.attach(m->bd);
932 }
933 }
934
935#ifdef VBOX_WITH_USB
936 bool commitFilters = false;
937
938 if (m->llDeviceFilters.isBackedUp())
939 {
940 m->llDeviceFilters.commit();
941
942 /* apply changes to peer */
943 if (m->pPeer)
944 {
945 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
946
947 /* commit all changes to new filters (this will reshare data with
948 * peers for those who have peers) */
949 DeviceFilterList *newList = new DeviceFilterList();
950 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
951 while (it != m->llDeviceFilters->end())
952 {
953 (*it)->commit();
954
955 /* look if this filter has a peer filter */
956 ComObjPtr<USBDeviceFilter> peer = (*it)->peer();
957 if (!peer)
958 {
959 /* no peer means the filter is a newly created one;
960 * create a peer owning data this filter share it with */
961 peer.createObject();
962 peer->init(m->pPeer, *it, true /* aReshare */);
963 }
964 else
965 {
966 /* remove peer from the old list */
967 m->pPeer->m->llDeviceFilters->remove(peer);
968 }
969 /* and add it to the new list */
970 newList->push_back (peer);
971
972 ++ it;
973 }
974
975 /* uninit old peer's filters that are left */
976 it = m->pPeer->m->llDeviceFilters->begin();
977 while (it != m->pPeer->m->llDeviceFilters->end())
978 {
979 (*it)->uninit();
980 ++ it;
981 }
982
983 /* attach new list of filters to our peer */
984 m->pPeer->m->llDeviceFilters.attach(newList);
985 }
986 else
987 {
988 /* we have no peer (our parent is the newly created machine);
989 * just commit changes to filters */
990 commitFilters = true;
991 }
992 }
993 else
994 {
995 /* the list of filters itself is not changed,
996 * just commit changes to filters themselves */
997 commitFilters = true;
998 }
999
1000 if (commitFilters)
1001 {
1002 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
1003 while (it != m->llDeviceFilters->end())
1004 {
1005 (*it)->commit();
1006 ++ it;
1007 }
1008 }
1009#endif /* VBOX_WITH_USB */
1010}
1011
1012/**
1013 * @note Locks this object for writing, together with the peer object
1014 * represented by @a aThat (locked for reading).
1015 */
1016void USBController::copyFrom (USBController *aThat)
1017{
1018 AssertReturnVoid (aThat != NULL);
1019
1020 /* sanity */
1021 AutoCaller autoCaller(this);
1022 AssertComRCReturnVoid (autoCaller.rc());
1023
1024 /* sanity too */
1025 AutoCaller thatCaller (aThat);
1026 AssertComRCReturnVoid (thatCaller.rc());
1027
1028 /* even more sanity */
1029 AutoAnyStateDependency adep(m->pParent);
1030 AssertComRCReturnVoid (adep.rc());
1031 /* Machine::copyFrom() may not be called when the VM is running */
1032 AssertReturnVoid (!Global::IsOnline (adep.machineState()));
1033
1034 /* peer is not modified, lock it for reading (aThat is "master" so locked
1035 * first) */
1036 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
1037 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
1038
1039 /* this will back up current data */
1040 m->bd.assignCopy(aThat->m->bd);
1041
1042#ifdef VBOX_WITH_USB
1043
1044 /* Note that we won't inform the USB proxy about new filters since the VM is
1045 * not running when we are here and therefore no need to do so */
1046
1047 /* create private copies of all filters */
1048 m->llDeviceFilters.backup();
1049 m->llDeviceFilters->clear();
1050 for (DeviceFilterList::const_iterator it = aThat->m->llDeviceFilters->begin();
1051 it != aThat->m->llDeviceFilters->end();
1052 ++ it)
1053 {
1054 ComObjPtr<USBDeviceFilter> filter;
1055 filter.createObject();
1056 filter->initCopy (this, *it);
1057 m->llDeviceFilters->push_back (filter);
1058 }
1059
1060#endif /* VBOX_WITH_USB */
1061}
1062
1063#ifdef VBOX_WITH_USB
1064
1065/**
1066 * Called by setter methods of all USB device filters.
1067 *
1068 * @note Locks nothing.
1069 */
1070HRESULT USBController::onDeviceFilterChange (USBDeviceFilter *aFilter,
1071 BOOL aActiveChanged /* = FALSE */)
1072{
1073 AutoCaller autoCaller(this);
1074 AssertComRCReturnRC(autoCaller.rc());
1075
1076 /* we need the machine state */
1077 AutoAnyStateDependency adep(m->pParent);
1078 AssertComRCReturnRC(adep.rc());
1079
1080 /* nothing to do if the machine isn't running */
1081 if (!Global::IsOnline (adep.machineState()))
1082 return S_OK;
1083
1084 /* we don't modify our data fields -- no need to lock */
1085
1086 if (aFilter->mInList && m->pParent->isRegistered())
1087 {
1088 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
1089 ComAssertRet (service, E_FAIL);
1090
1091 if (aActiveChanged)
1092 {
1093 /* insert/remove the filter from the proxy */
1094 if (aFilter->getData().mActive)
1095 {
1096 ComAssertRet(aFilter->getId() == NULL, E_FAIL);
1097 aFilter->getId() = service->insertFilter(&aFilter->getData().mUSBFilter);
1098 }
1099 else
1100 {
1101 ComAssertRet(aFilter->getId() != NULL, E_FAIL);
1102 service->removeFilter(aFilter->getId());
1103 aFilter->getId() = NULL;
1104 }
1105 }
1106 else
1107 {
1108 if (aFilter->getData().mActive)
1109 {
1110 /* update the filter in the proxy */
1111 ComAssertRet(aFilter->getId() != NULL, E_FAIL);
1112 service->removeFilter(aFilter->getId());
1113 aFilter->getId() = service->insertFilter(&aFilter->getData().mUSBFilter);
1114 }
1115 }
1116 }
1117
1118 return S_OK;
1119}
1120
1121/**
1122 * Returns true if the given USB device matches to at least one of
1123 * this controller's USB device filters.
1124 *
1125 * A HostUSBDevice specific version.
1126 *
1127 * @note Locks this object for reading.
1128 */
1129bool USBController::hasMatchingFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
1130{
1131 AutoCaller autoCaller(this);
1132 AssertComRCReturn (autoCaller.rc(), false);
1133
1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 /* Disabled USB controllers cannot actually work with USB devices */
1137 if (!m->bd->fEnabled)
1138 return false;
1139
1140 /* apply self filters */
1141 for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
1142 it != m->llDeviceFilters->end();
1143 ++ it)
1144 {
1145 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
1146 if (aDevice->isMatch((*it)->getData()))
1147 {
1148 *aMaskedIfs = (*it)->getData().mMaskedIfs;
1149 return true;
1150 }
1151 }
1152
1153 return false;
1154}
1155
1156/**
1157 * Returns true if the given USB device matches to at least one of
1158 * this controller's USB device filters.
1159 *
1160 * A generic version that accepts any IUSBDevice on input.
1161 *
1162 * @note
1163 * This method MUST correlate with HostUSBDevice::isMatch()
1164 * in the sense of the device matching logic.
1165 *
1166 * @note Locks this object for reading.
1167 */
1168bool USBController::hasMatchingFilter (IUSBDevice *aUSBDevice, ULONG *aMaskedIfs)
1169{
1170 LogFlowThisFuncEnter();
1171
1172 AutoCaller autoCaller(this);
1173 AssertComRCReturn (autoCaller.rc(), false);
1174
1175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1176
1177 /* Disabled USB controllers cannot actually work with USB devices */
1178 if (!m->bd->fEnabled)
1179 return false;
1180
1181 HRESULT rc = S_OK;
1182
1183 /* query fields */
1184 USBFILTER dev;
1185 USBFilterInit (&dev, USBFILTERTYPE_CAPTURE);
1186
1187 USHORT vendorId = 0;
1188 rc = aUSBDevice->COMGETTER(VendorId) (&vendorId);
1189 ComAssertComRCRet (rc, false);
1190 ComAssertRet (vendorId, false);
1191 int vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_VENDOR_ID, vendorId, true); AssertRC(vrc);
1192
1193 USHORT productId = 0;
1194 rc = aUSBDevice->COMGETTER(ProductId) (&productId);
1195 ComAssertComRCRet (rc, false);
1196 vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_PRODUCT_ID, productId, true); AssertRC(vrc);
1197
1198 USHORT revision;
1199 rc = aUSBDevice->COMGETTER(Revision) (&revision);
1200 ComAssertComRCRet (rc, false);
1201 vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_DEVICE, revision, true); AssertRC(vrc);
1202
1203 Bstr manufacturer;
1204 rc = aUSBDevice->COMGETTER(Manufacturer) (manufacturer.asOutParam());
1205 ComAssertComRCRet (rc, false);
1206 if (!manufacturer.isNull())
1207 USBFilterSetStringExact (&dev, USBFILTERIDX_MANUFACTURER_STR, Utf8Str(manufacturer).c_str(), true);
1208
1209 Bstr product;
1210 rc = aUSBDevice->COMGETTER(Product) (product.asOutParam());
1211 ComAssertComRCRet (rc, false);
1212 if (!product.isNull())
1213 USBFilterSetStringExact (&dev, USBFILTERIDX_PRODUCT_STR, Utf8Str(product).c_str(), true);
1214
1215 Bstr serialNumber;
1216 rc = aUSBDevice->COMGETTER(SerialNumber) (serialNumber.asOutParam());
1217 ComAssertComRCRet (rc, false);
1218 if (!serialNumber.isNull())
1219 USBFilterSetStringExact (&dev, USBFILTERIDX_SERIAL_NUMBER_STR, Utf8Str(serialNumber).c_str(), true);
1220
1221 Bstr address;
1222 rc = aUSBDevice->COMGETTER(Address) (address.asOutParam());
1223 ComAssertComRCRet (rc, false);
1224
1225 USHORT port = 0;
1226 rc = aUSBDevice->COMGETTER(Port)(&port);
1227 ComAssertComRCRet (rc, false);
1228 USBFilterSetNumExact (&dev, USBFILTERIDX_PORT, port, true);
1229
1230 BOOL remote = FALSE;
1231 rc = aUSBDevice->COMGETTER(Remote)(&remote);
1232 ComAssertComRCRet (rc, false);
1233 ComAssertRet (remote == TRUE, false);
1234
1235 bool match = false;
1236
1237 /* apply self filters */
1238 for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
1239 it != m->llDeviceFilters->end();
1240 ++ it)
1241 {
1242 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
1243 const USBDeviceFilter::Data &aData = (*it)->getData();
1244
1245 if (!aData.mActive)
1246 continue;
1247 if (!aData.mRemote.isMatch (remote))
1248 continue;
1249 if (!USBFilterMatch (&aData.mUSBFilter, &dev))
1250 continue;
1251
1252 match = true;
1253 *aMaskedIfs = aData.mMaskedIfs;
1254 break;
1255 }
1256
1257 LogFlowThisFunc(("returns: %d\n", match));
1258 LogFlowThisFuncLeave();
1259
1260 return match;
1261}
1262
1263/**
1264 * Notifies the proxy service about all filters as requested by the
1265 * @a aInsertFilters argument.
1266 *
1267 * @param aInsertFilters @c true to insert filters, @c false to remove.
1268 *
1269 * @note Locks this object for reading.
1270 */
1271HRESULT USBController::notifyProxy (bool aInsertFilters)
1272{
1273 LogFlowThisFunc(("aInsertFilters=%RTbool\n", aInsertFilters));
1274
1275 AutoCaller autoCaller(this);
1276 AssertComRCReturn (autoCaller.rc(), false);
1277
1278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
1281 AssertReturn(service, E_FAIL);
1282
1283 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
1284 while (it != m->llDeviceFilters->end())
1285 {
1286 USBDeviceFilter *flt = *it; /* resolve ambiguity (for ComPtr below) */
1287
1288 /* notify the proxy (only if the filter is active) */
1289 if (flt->getData().mActive)
1290 {
1291 if (aInsertFilters)
1292 {
1293 AssertReturn(flt->getId() == NULL, E_FAIL);
1294 flt->getId() = service->insertFilter(&flt->getData().mUSBFilter);
1295 }
1296 else
1297 {
1298 /* It's possible that the given filter was not inserted the proxy
1299 * when this method gets called (as a result of an early VM
1300 * process crash for example. So, don't assert that ID != NULL. */
1301 if (flt->getId() != NULL)
1302 {
1303 service->removeFilter(flt->getId());
1304 flt->getId() = NULL;
1305 }
1306 }
1307 }
1308 ++ it;
1309 }
1310
1311 return S_OK;
1312}
1313
1314Machine* USBController::getMachine()
1315{
1316 return m->pParent;
1317}
1318
1319#endif /* VBOX_WITH_USB */
1320
1321// private methods
1322/////////////////////////////////////////////////////////////////////////////
1323/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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