VirtualBox

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

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

Main: separate internal machine data structs into MachineImplPrivate.h to significantly speed up compilation and for better interface separation; remove obsolete ConsoleEvents.h file

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.9 KB
Line 
1/* $Id: USBControllerImpl.cpp 30760 2010-07-09 13:12:04Z vboxsync $ */
2/** @file
3 * Implementation of IUSBController.
4 */
5
6/*
7 * Copyright (C) 2006-2007 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include "USBControllerImpl.h"
19
20#include "Global.h"
21#include "MachineImpl.h"
22#include "VirtualBoxImpl.h"
23#include "HostImpl.h"
24#ifdef VBOX_WITH_USB
25# include "USBDeviceImpl.h"
26# include "HostUSBDeviceImpl.h"
27# include "USBProxyService.h"
28#endif
29#include "USBDeviceFilterImpl.h"
30
31#include <iprt/string.h>
32#include <iprt/cpp/utils.h>
33
34#include <VBox/err.h>
35#include <VBox/settings.h>
36#include <VBox/com/array.h>
37
38#include <algorithm>
39
40#include "AutoStateDep.h"
41#include "AutoCaller.h"
42#include "Logging.h"
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 fEnabled;
57 BOOL fEnabledEHCI;
58};
59
60struct USBController::Data
61{
62 Data()
63 : pParent(NULL)
64 {};
65
66 ~Data()
67 {};
68
69 /** Parent object. */
70 Machine * const 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) = NULL;
255 unconst(m->pParent) = NULL;
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 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.release();
300
301 AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
302 m->pParent->setModified(Machine::IsModified_USB);
303 mlock.release();
304
305 m->pParent->onUSBControllerChange();
306 }
307
308 return S_OK;
309}
310
311STDMETHODIMP USBController::COMGETTER(EnabledEhci) (BOOL *aEnabled)
312{
313 CheckComArgOutPointerValid(aEnabled);
314
315 AutoCaller autoCaller(this);
316 if (FAILED(autoCaller.rc())) return autoCaller.rc();
317
318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
319
320 *aEnabled = m->bd->fEnabledEHCI;
321
322 return S_OK;
323}
324
325STDMETHODIMP USBController::COMSETTER(EnabledEhci) (BOOL aEnabled)
326{
327 LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled));
328
329 AutoCaller autoCaller(this);
330 if (FAILED(autoCaller.rc())) return autoCaller.rc();
331
332 /* the machine needs to be mutable */
333 AutoMutableStateDependency adep(m->pParent);
334 if (FAILED(adep.rc())) return adep.rc();
335
336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
337
338 if (m->bd->fEnabledEHCI != aEnabled)
339 {
340 m->bd.backup();
341 m->bd->fEnabledEHCI = aEnabled;
342
343 // leave the lock for safety
344 alock.release();
345
346 AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
347 m->pParent->setModified(Machine::IsModified_USB);
348 mlock.release();
349
350 m->pParent->onUSBControllerChange();
351 }
352
353 return S_OK;
354}
355
356STDMETHODIMP USBController::COMGETTER(ProxyAvailable) (BOOL *aEnabled)
357{
358 CheckComArgOutPointerValid(aEnabled);
359
360 AutoCaller autoCaller(this);
361 if (FAILED(autoCaller.rc())) return autoCaller.rc();
362
363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
364
365#ifdef VBOX_WITH_USB
366 *aEnabled = true;
367#else
368 *aEnabled = false;
369#endif
370
371 return S_OK;
372}
373
374STDMETHODIMP USBController::COMGETTER(USBStandard) (USHORT *aUSBStandard)
375{
376 CheckComArgOutPointerValid(aUSBStandard);
377
378 AutoCaller autoCaller(this);
379 if (FAILED(autoCaller.rc())) return autoCaller.rc();
380
381 /* not accessing data -- no need to lock */
382
383 /** @todo This is no longer correct */
384 *aUSBStandard = 0x0101;
385
386 return S_OK;
387}
388
389#ifndef VBOX_WITH_USB
390/**
391 * Fake class for build without USB.
392 * We need an empty collection & enum for deviceFilters, that's all.
393 */
394class ATL_NO_VTABLE USBDeviceFilter :
395 public VirtualBoxBase,
396 public VirtualBoxSupportErrorInfoImpl<USBDeviceFilter, IUSBDeviceFilter>,
397 public VirtualBoxSupportTranslation<USBDeviceFilter>,
398 public IUSBDeviceFilter
399{
400public:
401 DECLARE_NOT_AGGREGATABLE(USBDeviceFilter)
402 DECLARE_PROTECT_FINAL_CONSTRUCT()
403 BEGIN_COM_MAP(USBDeviceFilter)
404 COM_INTERFACE_ENTRY(ISupportErrorInfo)
405 COM_INTERFACE_ENTRY(IUSBDeviceFilter)
406 END_COM_MAP()
407
408 DECLARE_EMPTY_CTOR_DTOR (USBDeviceFilter)
409
410 // IUSBDeviceFilter properties
411 STDMETHOD(COMGETTER(Name)) (BSTR *aName);
412 STDMETHOD(COMSETTER(Name)) (IN_BSTR aName);
413 STDMETHOD(COMGETTER(Active)) (BOOL *aActive);
414 STDMETHOD(COMSETTER(Active)) (BOOL aActive);
415 STDMETHOD(COMGETTER(VendorId)) (BSTR *aVendorId);
416 STDMETHOD(COMSETTER(VendorId)) (IN_BSTR aVendorId);
417 STDMETHOD(COMGETTER(ProductId)) (BSTR *aProductId);
418 STDMETHOD(COMSETTER(ProductId)) (IN_BSTR aProductId);
419 STDMETHOD(COMGETTER(Revision)) (BSTR *aRevision);
420 STDMETHOD(COMSETTER(Revision)) (IN_BSTR aRevision);
421 STDMETHOD(COMGETTER(Manufacturer)) (BSTR *aManufacturer);
422 STDMETHOD(COMSETTER(Manufacturer)) (IN_BSTR aManufacturer);
423 STDMETHOD(COMGETTER(Product)) (BSTR *aProduct);
424 STDMETHOD(COMSETTER(Product)) (IN_BSTR aProduct);
425 STDMETHOD(COMGETTER(SerialNumber)) (BSTR *aSerialNumber);
426 STDMETHOD(COMSETTER(SerialNumber)) (IN_BSTR aSerialNumber);
427 STDMETHOD(COMGETTER(Port)) (BSTR *aPort);
428 STDMETHOD(COMSETTER(Port)) (IN_BSTR aPort);
429 STDMETHOD(COMGETTER(Remote)) (BSTR *aRemote);
430 STDMETHOD(COMSETTER(Remote)) (IN_BSTR aRemote);
431 STDMETHOD(COMGETTER(MaskedInterfaces)) (ULONG *aMaskedIfs);
432 STDMETHOD(COMSETTER(MaskedInterfaces)) (ULONG aMaskedIfs);
433};
434#endif /* !VBOX_WITH_USB */
435
436
437STDMETHODIMP USBController::COMGETTER(DeviceFilters) (ComSafeArrayOut(IUSBDeviceFilter *, aDevicesFilters))
438{
439#ifdef VBOX_WITH_USB
440 CheckComArgOutSafeArrayPointerValid(aDevicesFilters);
441
442 AutoCaller autoCaller(this);
443 if (FAILED(autoCaller.rc())) return autoCaller.rc();
444
445 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
446
447 SafeIfaceArray<IUSBDeviceFilter> collection (*m->llDeviceFilters.data());
448 collection.detachTo(ComSafeArrayOutArg(aDevicesFilters));
449
450 return S_OK;
451#else
452 NOREF(aDevicesFilters);
453# ifndef RT_OS_WINDOWS
454 NOREF(aDevicesFiltersSize);
455# endif
456 ReturnComNotImplemented();
457#endif
458}
459
460// IUSBController methods
461/////////////////////////////////////////////////////////////////////////////
462
463STDMETHODIMP USBController::CreateDeviceFilter (IN_BSTR aName,
464 IUSBDeviceFilter **aFilter)
465{
466#ifdef VBOX_WITH_USB
467 CheckComArgOutPointerValid(aFilter);
468
469 CheckComArgStrNotEmptyOrNull(aName);
470
471 AutoCaller autoCaller(this);
472 if (FAILED(autoCaller.rc())) return autoCaller.rc();
473
474 /* the machine needs to be mutable */
475 AutoMutableStateDependency adep(m->pParent);
476 if (FAILED(adep.rc())) return adep.rc();
477
478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
479
480 ComObjPtr<USBDeviceFilter> filter;
481 filter.createObject();
482 HRESULT rc = filter->init (this, aName);
483 ComAssertComRCRetRC (rc);
484 rc = filter.queryInterfaceTo(aFilter);
485 AssertComRCReturnRC(rc);
486
487 return S_OK;
488#else
489 NOREF(aName);
490 NOREF(aFilter);
491 ReturnComNotImplemented();
492#endif
493}
494
495STDMETHODIMP USBController::InsertDeviceFilter(ULONG aPosition,
496 IUSBDeviceFilter *aFilter)
497{
498#ifdef VBOX_WITH_USB
499
500 CheckComArgNotNull(aFilter);
501
502 AutoCaller autoCaller(this);
503 if (FAILED(autoCaller.rc())) return autoCaller.rc();
504
505 /* the machine needs to be mutable */
506 AutoMutableStateDependency adep(m->pParent);
507 if (FAILED(adep.rc())) return adep.rc();
508
509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
510
511 ComObjPtr<USBDeviceFilter> filter = static_cast<USBDeviceFilter*>(aFilter);
512 // @todo r=dj make sure the input object is actually from us
513// ComObjPtr<USBDeviceFilter> filter = getDependentChild(aFilter);
514// if (!filter)
515// return setError (E_INVALIDARG,
516// tr ("The given USB device filter is not created within "
517// "this VirtualBox instance"));
518
519 if (filter->mInList)
520 return setError(VBOX_E_INVALID_OBJECT_STATE,
521 tr("The given USB device filter is already in the list"));
522
523 /* backup the list before modification */
524 m->llDeviceFilters.backup();
525
526 /* iterate to the position... */
527 DeviceFilterList::iterator it;
528 if (aPosition < m->llDeviceFilters->size())
529 {
530 it = m->llDeviceFilters->begin();
531 std::advance (it, aPosition);
532 }
533 else
534 it = m->llDeviceFilters->end();
535 /* ...and insert */
536 m->llDeviceFilters->insert (it, filter);
537 filter->mInList = true;
538
539 /* notify the proxy (only when it makes sense) */
540 if (filter->getData().mActive && Global::IsOnline(adep.machineState()))
541 {
542 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
543 ComAssertRet(service, E_FAIL);
544
545 ComAssertRet(filter->getId() == NULL, E_FAIL);
546 filter->getId() = service->insertFilter (&filter->getData().mUSBFilter);
547 }
548
549 alock.release();
550 AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
551 m->pParent->setModified(Machine::IsModified_USB);
552 mlock.release();
553
554 return S_OK;
555
556#else /* VBOX_WITH_USB */
557
558 NOREF(aPosition);
559 NOREF(aFilter);
560 ReturnComNotImplemented();
561
562#endif /* VBOX_WITH_USB */
563}
564
565STDMETHODIMP USBController::RemoveDeviceFilter(ULONG aPosition,
566 IUSBDeviceFilter **aFilter)
567{
568#ifdef VBOX_WITH_USB
569
570 CheckComArgOutPointerValid(aFilter);
571
572 AutoCaller autoCaller(this);
573 if (FAILED(autoCaller.rc())) return autoCaller.rc();
574
575 /* the machine needs to be mutable */
576 AutoMutableStateDependency adep(m->pParent);
577 if (FAILED(adep.rc())) return adep.rc();
578
579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
580
581 if (!m->llDeviceFilters->size())
582 return setError(E_INVALIDARG,
583 tr("The USB device filter list is empty"));
584
585 if (aPosition >= m->llDeviceFilters->size())
586 return setError(E_INVALIDARG,
587 tr("Invalid position: %lu (must be in range [0, %lu])"),
588 aPosition, m->llDeviceFilters->size() - 1);
589
590 /* backup the list before modification */
591 m->llDeviceFilters.backup();
592
593 ComObjPtr<USBDeviceFilter> filter;
594 {
595 /* iterate to the position... */
596 DeviceFilterList::iterator it = m->llDeviceFilters->begin();
597 std::advance (it, aPosition);
598 /* ...get an element from there... */
599 filter = *it;
600 /* ...and remove */
601 filter->mInList = false;
602 m->llDeviceFilters->erase (it);
603 }
604
605 /* cancel sharing (make an independent copy of data) */
606 filter->unshare();
607
608 filter.queryInterfaceTo(aFilter);
609
610 /* notify the proxy (only when it makes sense) */
611 if (filter->getData().mActive && Global::IsOnline(adep.machineState()))
612 {
613 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
614 ComAssertRet(service, E_FAIL);
615
616 ComAssertRet(filter->getId() != NULL, E_FAIL);
617 service->removeFilter(filter->getId());
618 filter->getId() = NULL;
619 }
620
621 alock.release();
622 AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS);
623 m->pParent->setModified(Machine::IsModified_USB);
624 mlock.release();
625
626 return S_OK;
627
628#else /* VBOX_WITH_USB */
629
630 NOREF(aPosition);
631 NOREF(aFilter);
632 ReturnComNotImplemented();
633
634#endif /* VBOX_WITH_USB */
635}
636
637// public methods only for internal purposes
638/////////////////////////////////////////////////////////////////////////////
639
640/**
641 * Loads settings from the given machine node.
642 * May be called once right after this object creation.
643 *
644 * @param aMachineNode <Machine> node.
645 *
646 * @note Does not lock "this" as Machine::loadHardware, which calls this, does not lock either.
647 */
648HRESULT USBController::loadSettings(const settings::USBController &data)
649{
650 AutoCaller autoCaller(this);
651 AssertComRCReturnRC(autoCaller.rc());
652
653 /* Note: we assume that the default values for attributes of optional
654 * nodes are assigned in the Data::Data() constructor and don't do it
655 * here. It implies that this method may only be called after constructing
656 * a new BIOSSettings object while all its data fields are in the default
657 * values. Exceptions are fields whose creation time defaults don't match
658 * values that should be applied when these fields are not explicitly set
659 * in the settings file (for backwards compatibility reasons). This takes
660 * place when a setting of a newly created object must default to A while
661 * the same setting of an object loaded from the old settings file must
662 * default to B. */
663
664 m->bd->fEnabled = data.fEnabled;
665 m->bd->fEnabledEHCI = data.fEnabledEHCI;
666
667#ifdef VBOX_WITH_USB
668 for (settings::USBDeviceFiltersList::const_iterator it = data.llDeviceFilters.begin();
669 it != data.llDeviceFilters.end();
670 ++it)
671 {
672 const settings::USBDeviceFilter &f = *it;
673 ComObjPtr<USBDeviceFilter> pFilter;
674 pFilter.createObject();
675 HRESULT rc = pFilter->init(this, // parent
676 f);
677 if (FAILED(rc)) return rc;
678
679 m->llDeviceFilters->push_back(pFilter);
680 pFilter->mInList = true;
681 }
682#endif /* VBOX_WITH_USB */
683
684 return S_OK;
685}
686
687/**
688 * Saves settings to the given machine node.
689 *
690 * @param aMachineNode <Machine> node.
691 *
692 * @note Locks this object for reading.
693 */
694HRESULT USBController::saveSettings(settings::USBController &data)
695{
696 AutoCaller autoCaller(this);
697 if (FAILED(autoCaller.rc())) return autoCaller.rc();
698
699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
700
701 data.fEnabled = !!m->bd->fEnabled;
702 data.fEnabledEHCI = !!m->bd->fEnabledEHCI;
703
704#ifdef VBOX_WITH_USB
705 data.llDeviceFilters.clear();
706
707 for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
708 it != m->llDeviceFilters->end();
709 ++it)
710 {
711 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
712 const USBDeviceFilter::Data &filterData = (*it)->getData();
713
714 Bstr str;
715
716 settings::USBDeviceFilter f;
717 f.strName = filterData.mName;
718 f.fActive = !!filterData.mActive;
719 (*it)->COMGETTER(VendorId)(str.asOutParam());
720 f.strVendorId = str;
721 (*it)->COMGETTER(ProductId)(str.asOutParam());
722 f.strProductId = str;
723 (*it)->COMGETTER (Revision) (str.asOutParam());
724 f.strRevision = str;
725 (*it)->COMGETTER (Manufacturer) (str.asOutParam());
726 f.strManufacturer = str;
727 (*it)->COMGETTER (Product) (str.asOutParam());
728 f.strProduct = str;
729 (*it)->COMGETTER (SerialNumber) (str.asOutParam());
730 f.strSerialNumber = str;
731 (*it)->COMGETTER (Port) (str.asOutParam());
732 f.strPort = str;
733 f.strRemote = filterData.mRemote.string();
734 f.ulMaskedInterfaces = filterData.mMaskedIfs;
735
736 data.llDeviceFilters.push_back(f);
737 }
738#endif /* VBOX_WITH_USB */
739
740 return S_OK;
741}
742
743/** @note Locks objects for writing! */
744void USBController::rollback()
745{
746 AutoCaller autoCaller(this);
747 AssertComRCReturnVoid(autoCaller.rc());
748
749 /* we need the machine state */
750 AutoAnyStateDependency adep(m->pParent);
751 AssertComRCReturnVoid(adep.rc());
752
753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
754
755 m->bd.rollback();
756
757#ifdef VBOX_WITH_USB
758
759 if (m->llDeviceFilters.isBackedUp())
760 {
761 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
762 Assert(service);
763
764 /* uninitialize all new filters (absent in the backed up list) */
765 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
766 DeviceFilterList *backedList = m->llDeviceFilters.backedUpData();
767 while (it != m->llDeviceFilters->end())
768 {
769 if (std::find (backedList->begin(), backedList->end(), *it) ==
770 backedList->end())
771 {
772 /* notify the proxy (only when it makes sense) */
773 if ((*it)->getData().mActive &&
774 Global::IsOnline (adep.machineState()))
775 {
776 USBDeviceFilter *filter = *it;
777 Assert(filter->getId() != NULL);
778 service->removeFilter(filter->getId());
779 filter->getId() = NULL;
780 }
781
782 (*it)->uninit();
783 }
784 ++ it;
785 }
786
787 if (Global::IsOnline (adep.machineState()))
788 {
789 /* find all removed old filters (absent in the new list)
790 * and insert them back to the USB proxy */
791 it = backedList->begin();
792 while (it != backedList->end())
793 {
794 if (std::find (m->llDeviceFilters->begin(), m->llDeviceFilters->end(), *it) ==
795 m->llDeviceFilters->end())
796 {
797 /* notify the proxy (only when necessary) */
798 if ((*it)->getData().mActive)
799 {
800 USBDeviceFilter *flt = *it; /* resolve ambiguity */
801 Assert(flt->getId() == NULL);
802 flt->getId() = service->insertFilter(&flt->getData().mUSBFilter);
803 }
804 }
805 ++ it;
806 }
807 }
808
809 /* restore the list */
810 m->llDeviceFilters.rollback();
811 }
812
813 /* here we don't depend on the machine state any more */
814 adep.release();
815
816 /* rollback any changes to filters after restoring the list */
817 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
818 while (it != m->llDeviceFilters->end())
819 {
820 if ((*it)->isModified())
821 {
822 (*it)->rollback();
823 /* call this to notify the USB proxy about changes */
824 onDeviceFilterChange(*it);
825 }
826 ++it;
827 }
828
829#endif /* VBOX_WITH_USB */
830}
831
832/**
833 * @note Locks this object for writing, together with the peer object (also
834 * for writing) if there is one.
835 */
836void USBController::commit()
837{
838 /* sanity */
839 AutoCaller autoCaller(this);
840 AssertComRCReturnVoid (autoCaller.rc());
841
842 /* sanity too */
843 AutoCaller peerCaller(m->pPeer);
844 AssertComRCReturnVoid (peerCaller.rc());
845
846 /* lock both for writing since we modify both (mPeer is "master" so locked
847 * first) */
848 AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS);
849
850 if (m->bd.isBackedUp())
851 {
852 m->bd.commit();
853 if (m->pPeer)
854 {
855 /* attach new data to the peer and reshare it */
856 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
857 m->pPeer->m->bd.attach(m->bd);
858 }
859 }
860
861#ifdef VBOX_WITH_USB
862 bool commitFilters = false;
863
864 if (m->llDeviceFilters.isBackedUp())
865 {
866 m->llDeviceFilters.commit();
867
868 /* apply changes to peer */
869 if (m->pPeer)
870 {
871 AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS);
872
873 /* commit all changes to new filters (this will reshare data with
874 * peers for those who have peers) */
875 DeviceFilterList *newList = new DeviceFilterList();
876 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
877 while (it != m->llDeviceFilters->end())
878 {
879 (*it)->commit();
880
881 /* look if this filter has a peer filter */
882 ComObjPtr<USBDeviceFilter> peer = (*it)->peer();
883 if (!peer)
884 {
885 /* no peer means the filter is a newly created one;
886 * create a peer owning data this filter share it with */
887 peer.createObject();
888 peer->init(m->pPeer, *it, true /* aReshare */);
889 }
890 else
891 {
892 /* remove peer from the old list */
893 m->pPeer->m->llDeviceFilters->remove(peer);
894 }
895 /* and add it to the new list */
896 newList->push_back (peer);
897
898 ++ it;
899 }
900
901 /* uninit old peer's filters that are left */
902 it = m->pPeer->m->llDeviceFilters->begin();
903 while (it != m->pPeer->m->llDeviceFilters->end())
904 {
905 (*it)->uninit();
906 ++ it;
907 }
908
909 /* attach new list of filters to our peer */
910 m->pPeer->m->llDeviceFilters.attach(newList);
911 }
912 else
913 {
914 /* we have no peer (our parent is the newly created machine);
915 * just commit changes to filters */
916 commitFilters = true;
917 }
918 }
919 else
920 {
921 /* the list of filters itself is not changed,
922 * just commit changes to filters themselves */
923 commitFilters = true;
924 }
925
926 if (commitFilters)
927 {
928 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
929 while (it != m->llDeviceFilters->end())
930 {
931 (*it)->commit();
932 ++ it;
933 }
934 }
935#endif /* VBOX_WITH_USB */
936}
937
938/**
939 * @note Locks this object for writing, together with the peer object
940 * represented by @a aThat (locked for reading).
941 */
942void USBController::copyFrom (USBController *aThat)
943{
944 AssertReturnVoid (aThat != NULL);
945
946 /* sanity */
947 AutoCaller autoCaller(this);
948 AssertComRCReturnVoid (autoCaller.rc());
949
950 /* sanity too */
951 AutoCaller thatCaller (aThat);
952 AssertComRCReturnVoid (thatCaller.rc());
953
954 /* even more sanity */
955 AutoAnyStateDependency adep(m->pParent);
956 AssertComRCReturnVoid (adep.rc());
957 /* Machine::copyFrom() may not be called when the VM is running */
958 AssertReturnVoid (!Global::IsOnline (adep.machineState()));
959
960 /* peer is not modified, lock it for reading (aThat is "master" so locked
961 * first) */
962 AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS);
963 AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS);
964
965 /* this will back up current data */
966 m->bd.assignCopy(aThat->m->bd);
967
968#ifdef VBOX_WITH_USB
969
970 /* Note that we won't inform the USB proxy about new filters since the VM is
971 * not running when we are here and therefore no need to do so */
972
973 /* create private copies of all filters */
974 m->llDeviceFilters.backup();
975 m->llDeviceFilters->clear();
976 for (DeviceFilterList::const_iterator it = aThat->m->llDeviceFilters->begin();
977 it != aThat->m->llDeviceFilters->end();
978 ++ it)
979 {
980 ComObjPtr<USBDeviceFilter> filter;
981 filter.createObject();
982 filter->initCopy (this, *it);
983 m->llDeviceFilters->push_back (filter);
984 }
985
986#endif /* VBOX_WITH_USB */
987}
988
989#ifdef VBOX_WITH_USB
990
991/**
992 * Called by setter methods of all USB device filters.
993 *
994 * @note Locks nothing.
995 */
996HRESULT USBController::onDeviceFilterChange (USBDeviceFilter *aFilter,
997 BOOL aActiveChanged /* = FALSE */)
998{
999 AutoCaller autoCaller(this);
1000 AssertComRCReturnRC(autoCaller.rc());
1001
1002 /* we need the machine state */
1003 AutoAnyStateDependency adep(m->pParent);
1004 AssertComRCReturnRC(adep.rc());
1005
1006 /* nothing to do if the machine isn't running */
1007 if (!Global::IsOnline (adep.machineState()))
1008 return S_OK;
1009
1010 /* we don't modify our data fields -- no need to lock */
1011
1012 if (aFilter->mInList && m->pParent->isRegistered())
1013 {
1014 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
1015 ComAssertRet(service, E_FAIL);
1016
1017 if (aActiveChanged)
1018 {
1019 /* insert/remove the filter from the proxy */
1020 if (aFilter->getData().mActive)
1021 {
1022 ComAssertRet(aFilter->getId() == NULL, E_FAIL);
1023 aFilter->getId() = service->insertFilter(&aFilter->getData().mUSBFilter);
1024 }
1025 else
1026 {
1027 ComAssertRet(aFilter->getId() != NULL, E_FAIL);
1028 service->removeFilter(aFilter->getId());
1029 aFilter->getId() = NULL;
1030 }
1031 }
1032 else
1033 {
1034 if (aFilter->getData().mActive)
1035 {
1036 /* update the filter in the proxy */
1037 ComAssertRet(aFilter->getId() != NULL, E_FAIL);
1038 service->removeFilter(aFilter->getId());
1039 aFilter->getId() = service->insertFilter(&aFilter->getData().mUSBFilter);
1040 }
1041 }
1042 }
1043
1044 return S_OK;
1045}
1046
1047/**
1048 * Returns true if the given USB device matches to at least one of
1049 * this controller's USB device filters.
1050 *
1051 * A HostUSBDevice specific version.
1052 *
1053 * @note Locks this object for reading.
1054 */
1055bool USBController::hasMatchingFilter (const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
1056{
1057 AutoCaller autoCaller(this);
1058 AssertComRCReturn (autoCaller.rc(), false);
1059
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 /* Disabled USB controllers cannot actually work with USB devices */
1063 if (!m->bd->fEnabled)
1064 return false;
1065
1066 /* apply self filters */
1067 for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
1068 it != m->llDeviceFilters->end();
1069 ++ it)
1070 {
1071 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
1072 if (aDevice->isMatch((*it)->getData()))
1073 {
1074 *aMaskedIfs = (*it)->getData().mMaskedIfs;
1075 return true;
1076 }
1077 }
1078
1079 return false;
1080}
1081
1082/**
1083 * Returns true if the given USB device matches to at least one of
1084 * this controller's USB device filters.
1085 *
1086 * A generic version that accepts any IUSBDevice on input.
1087 *
1088 * @note
1089 * This method MUST correlate with HostUSBDevice::isMatch()
1090 * in the sense of the device matching logic.
1091 *
1092 * @note Locks this object for reading.
1093 */
1094bool USBController::hasMatchingFilter (IUSBDevice *aUSBDevice, ULONG *aMaskedIfs)
1095{
1096 LogFlowThisFuncEnter();
1097
1098 AutoCaller autoCaller(this);
1099 AssertComRCReturn (autoCaller.rc(), false);
1100
1101 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1102
1103 /* Disabled USB controllers cannot actually work with USB devices */
1104 if (!m->bd->fEnabled)
1105 return false;
1106
1107 HRESULT rc = S_OK;
1108
1109 /* query fields */
1110 USBFILTER dev;
1111 USBFilterInit (&dev, USBFILTERTYPE_CAPTURE);
1112
1113 USHORT vendorId = 0;
1114 rc = aUSBDevice->COMGETTER(VendorId) (&vendorId);
1115 ComAssertComRCRet(rc, false);
1116 ComAssertRet(vendorId, false);
1117 int vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_VENDOR_ID, vendorId, true); AssertRC(vrc);
1118
1119 USHORT productId = 0;
1120 rc = aUSBDevice->COMGETTER(ProductId) (&productId);
1121 ComAssertComRCRet(rc, false);
1122 vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_PRODUCT_ID, productId, true); AssertRC(vrc);
1123
1124 USHORT revision;
1125 rc = aUSBDevice->COMGETTER(Revision) (&revision);
1126 ComAssertComRCRet(rc, false);
1127 vrc = USBFilterSetNumExact (&dev, USBFILTERIDX_DEVICE, revision, true); AssertRC(vrc);
1128
1129 Bstr manufacturer;
1130 rc = aUSBDevice->COMGETTER(Manufacturer) (manufacturer.asOutParam());
1131 ComAssertComRCRet(rc, false);
1132 if (!manufacturer.isEmpty())
1133 USBFilterSetStringExact (&dev, USBFILTERIDX_MANUFACTURER_STR, Utf8Str(manufacturer).c_str(), true);
1134
1135 Bstr product;
1136 rc = aUSBDevice->COMGETTER(Product) (product.asOutParam());
1137 ComAssertComRCRet(rc, false);
1138 if (!product.isEmpty())
1139 USBFilterSetStringExact (&dev, USBFILTERIDX_PRODUCT_STR, Utf8Str(product).c_str(), true);
1140
1141 Bstr serialNumber;
1142 rc = aUSBDevice->COMGETTER(SerialNumber) (serialNumber.asOutParam());
1143 ComAssertComRCRet(rc, false);
1144 if (!serialNumber.isEmpty())
1145 USBFilterSetStringExact (&dev, USBFILTERIDX_SERIAL_NUMBER_STR, Utf8Str(serialNumber).c_str(), true);
1146
1147 Bstr address;
1148 rc = aUSBDevice->COMGETTER(Address) (address.asOutParam());
1149 ComAssertComRCRet(rc, false);
1150
1151 USHORT port = 0;
1152 rc = aUSBDevice->COMGETTER(Port)(&port);
1153 ComAssertComRCRet(rc, false);
1154 USBFilterSetNumExact (&dev, USBFILTERIDX_PORT, port, true);
1155
1156 BOOL remote = FALSE;
1157 rc = aUSBDevice->COMGETTER(Remote)(&remote);
1158 ComAssertComRCRet(rc, false);
1159 ComAssertRet(remote == TRUE, false);
1160
1161 bool match = false;
1162
1163 /* apply self filters */
1164 for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
1165 it != m->llDeviceFilters->end();
1166 ++ it)
1167 {
1168 AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS);
1169 const USBDeviceFilter::Data &aData = (*it)->getData();
1170
1171 if (!aData.mActive)
1172 continue;
1173 if (!aData.mRemote.isMatch (remote))
1174 continue;
1175 if (!USBFilterMatch (&aData.mUSBFilter, &dev))
1176 continue;
1177
1178 match = true;
1179 *aMaskedIfs = aData.mMaskedIfs;
1180 break;
1181 }
1182
1183 LogFlowThisFunc(("returns: %d\n", match));
1184 LogFlowThisFuncLeave();
1185
1186 return match;
1187}
1188
1189/**
1190 * Notifies the proxy service about all filters as requested by the
1191 * @a aInsertFilters argument.
1192 *
1193 * @param aInsertFilters @c true to insert filters, @c false to remove.
1194 *
1195 * @note Locks this object for reading.
1196 */
1197HRESULT USBController::notifyProxy (bool aInsertFilters)
1198{
1199 LogFlowThisFunc(("aInsertFilters=%RTbool\n", aInsertFilters));
1200
1201 AutoCaller autoCaller(this);
1202 AssertComRCReturn (autoCaller.rc(), false);
1203
1204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1205
1206 USBProxyService *service = m->pParent->getVirtualBox()->host()->usbProxyService();
1207 AssertReturn(service, E_FAIL);
1208
1209 DeviceFilterList::const_iterator it = m->llDeviceFilters->begin();
1210 while (it != m->llDeviceFilters->end())
1211 {
1212 USBDeviceFilter *flt = *it; /* resolve ambiguity (for ComPtr below) */
1213
1214 /* notify the proxy (only if the filter is active) */
1215 if (flt->getData().mActive)
1216 {
1217 if (aInsertFilters)
1218 {
1219 AssertReturn(flt->getId() == NULL, E_FAIL);
1220 flt->getId() = service->insertFilter(&flt->getData().mUSBFilter);
1221 }
1222 else
1223 {
1224 /* It's possible that the given filter was not inserted the proxy
1225 * when this method gets called (as a result of an early VM
1226 * process crash for example. So, don't assert that ID != NULL. */
1227 if (flt->getId() != NULL)
1228 {
1229 service->removeFilter(flt->getId());
1230 flt->getId() = NULL;
1231 }
1232 }
1233 }
1234 ++ it;
1235 }
1236
1237 return S_OK;
1238}
1239
1240Machine* USBController::getMachine()
1241{
1242 return m->pParent;
1243}
1244
1245#endif /* VBOX_WITH_USB */
1246
1247// private methods
1248/////////////////////////////////////////////////////////////////////////////
1249/* 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