VirtualBox

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

Last change on this file since 25881 was 25860, checked in by vboxsync, 15 years ago

Main: cleanup: get rid of VirtualBoxBaseProto, move AutoCaller*/*Span* classes out of VirtualBoxBaseProto class scope and into separate header; move CombinedProgress into separate header (it's only used by Console any more)

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