VirtualBox

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

Last change on this file since 25414 was 25346, checked in by vboxsync, 14 years ago

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

© 2023 Oracle
ContactPrivacy policyTerms of Use