USBControllerImpl.cpp revision 2cd927e7e64403e8a118228ee371a4081087cc05
/* $Id$ */
/** @file
* Implementation of IUSBController.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*/
#include "USBControllerImpl.h"
#include "Global.h"
#include "MachineImpl.h"
#include "VirtualBoxImpl.h"
#include "HostImpl.h"
#ifdef VBOX_WITH_USB
# include "USBDeviceImpl.h"
# include "HostUSBDeviceImpl.h"
# include "USBProxyService.h"
# include "USBDeviceFilterImpl.h"
#endif
#include <VBox/settings.h>
#include <algorithm>
#include "AutoStateDep.h"
#include "AutoCaller.h"
#include "Logging.h"
// defines
/////////////////////////////////////////////////////////////////////////////
struct BackupableUSBData
{
: fEnabled(false),
fEnabledEHCI(false)
{ }
};
struct USBController::Data
{
Data()
{};
~Data()
{};
/** Parent object. */
/** Peer object. */
#ifdef VBOX_WITH_USB
// so they cannot be a part of BackupableData
#endif
};
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
{
return S_OK;
}
void USBController::FinalRelease()
{
uninit();
}
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the USB controller object.
*
* @returns COM result indicator.
* @param aParent Pointer to our parent object.
*/
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
m = new Data();
/* mPeer is left null */
#ifdef VBOX_WITH_USB
m->llDeviceFilters.allocate();
#endif
/* Confirm a successful initialization */
return S_OK;
}
/**
* Initializes the USB controller object given another USB controller object
* (a kind of copy constructor). This object shares data with
* the object passed as an argument.
*
* @returns COM result indicator.
* @param aParent Pointer to our parent object.
* @param aPeer The object to share.
*
* @note This object must be destroyed before the original object
* it shares data with is destroyed.
*/
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
m = new Data();
#ifdef VBOX_WITH_USB
/* create copies of all filters */
m->llDeviceFilters.allocate();
{
++ it;
}
#endif /* VBOX_WITH_USB */
/* Confirm a successful initialization */
return S_OK;
}
/**
* Initializes the USB controller object given another guest object
* (a kind of copy constructor). This object makes a private copy of data
* of the original object passed as an argument.
*/
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
m = new Data();
/* mPeer is left null */
#ifdef VBOX_WITH_USB
/* create private copies of all filters */
m->llDeviceFilters.allocate();
{
++ it;
}
#endif /* VBOX_WITH_USB */
/* Confirm a successful initialization */
return S_OK;
}
/**
* Uninitializes the instance and sets the ready flag to FALSE.
* Called either from FinalRelease() or by the parent when it gets destroyed.
*/
void USBController::uninit()
{
LogFlowThisFunc(("\n"));
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
#ifdef VBOX_WITH_USB
// uninit all device filters on the list (it's a standard std::list not an ObjectsList
// so we must uninit() manually)
++it)
m->llDeviceFilters.free();
#endif
delete m;
m = NULL;
}
// IUSBController properties
/////////////////////////////////////////////////////////////////////////////
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
/* the machine needs to be mutable */
{
// leave the lock for safety
m->pParent->onUSBControllerChange();
}
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
/* the machine needs to be mutable */
{
// leave the lock for safety
m->pParent->onUSBControllerChange();
}
return S_OK;
}
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_USB
*aEnabled = true;
#else
*aEnabled = false;
#endif
return S_OK;
}
{
AutoCaller autoCaller(this);
/* not accessing data -- no need to lock */
/** @todo This is no longer correct */
*aUSBStandard = 0x0101;
return S_OK;
}
#ifndef VBOX_WITH_USB
/**
* Fake class for build without USB.
* We need an empty collection & enum for deviceFilters, that's all.
*/
class ATL_NO_VTABLE USBDeviceFilter :
public VirtualBoxBase,
public IUSBDeviceFilter
{
public:
// IUSBDeviceFilter properties
};
#endif /* !VBOX_WITH_USB */
STDMETHODIMP USBController::COMGETTER(DeviceFilters) (ComSafeArrayOut(IUSBDeviceFilter *, aDevicesFilters))
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
return S_OK;
#else
# ifndef RT_OS_WINDOWS
# endif
#endif
}
// IUSBController methods
/////////////////////////////////////////////////////////////////////////////
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
/* the machine needs to be mutable */
return S_OK;
#else
#endif
}
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
/* the machine needs to be mutable */
// @todo r=dj make sure the input object is actually from us
// ComObjPtr<USBDeviceFilter> filter = getDependentChild(aFilter);
// if (!filter)
// return setError (E_INVALIDARG,
// tr ("The given USB device filter is not created within "
// "this VirtualBox instance"));
return setError(VBOX_E_INVALID_OBJECT_STATE,
tr("The given USB device filter is already in the list"));
/* backup the list before modification */
m->llDeviceFilters.backup();
/* iterate to the position... */
{
}
else
/* ...and insert */
/* notify the proxy (only when it makes sense) */
{
}
return S_OK;
#else /* VBOX_WITH_USB */
#endif /* VBOX_WITH_USB */
}
{
#ifdef VBOX_WITH_USB
AutoCaller autoCaller(this);
/* the machine needs to be mutable */
if (!m->llDeviceFilters->size())
return setError(E_INVALIDARG,
tr("The USB device filter list is empty"));
return setError(E_INVALIDARG,
tr("Invalid position: %lu (must be in range [0, %lu])"),
/* backup the list before modification */
m->llDeviceFilters.backup();
{
/* iterate to the position... */
/* ...get an element from there... */
/* ...and remove */
}
/* cancel sharing (make an independent copy of data) */
/* notify the proxy (only when it makes sense) */
{
}
return S_OK;
#else /* VBOX_WITH_USB */
#endif /* VBOX_WITH_USB */
}
// public methods only for internal purposes
/////////////////////////////////////////////////////////////////////////////
/**
* Loads settings from the given machine node.
* May be called once right after this object creation.
*
* @param aMachineNode <Machine> node.
*
* @note Does not lock "this" as Machine::loadHardware, which calls this, does not lock either.
*/
{
AutoCaller autoCaller(this);
/* Note: we assume that the default values for attributes of optional
* nodes are assigned in the Data::Data() constructor and don't do it
* here. It implies that this method may only be called after constructing
* a new BIOSSettings object while all its data fields are in the default
* values. Exceptions are fields whose creation time defaults don't match
* values that should be applied when these fields are not explicitly set
* in the settings file (for backwards compatibility reasons). This takes
* place when a setting of a newly created object must default to A while
* the same setting of an object loaded from the old settings file must
* default to B. */
#ifdef VBOX_WITH_USB
++it)
{
f);
}
#endif /* VBOX_WITH_USB */
return S_OK;
}
/**
* Saves settings to the given machine node.
*
* @param aMachineNode <Machine> node.
*
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
#ifdef VBOX_WITH_USB
++it)
{
f.strVendorId = str;
f.strProductId = str;
f.strRevision = str;
f.strManufacturer = str;
f.strProduct = str;
f.strSerialNumber = str;
}
#endif /* VBOX_WITH_USB */
return S_OK;
}
/** @note Locks objects for writing! */
void USBController::rollback()
{
AutoCaller autoCaller(this);
/* we need the machine state */
#ifdef VBOX_WITH_USB
if (m->llDeviceFilters.isBackedUp())
{
/* uninitialize all new filters (absent in the backed up list) */
{
backedList->end())
{
/* notify the proxy (only when it makes sense) */
{
}
}
++ it;
}
{
/* find all removed old filters (absent in the new list)
* and insert them back to the USB proxy */
{
m->llDeviceFilters->end())
{
/* notify the proxy (only when necessary) */
{
}
}
++ it;
}
}
/* restore the list */
m->llDeviceFilters.rollback();
}
/* here we don't depend on the machine state any more */
/* rollback any changes to filters after restoring the list */
{
if ((*it)->isModified())
{
/* call this to notify the USB proxy about changes */
}
++it;
}
#endif /* VBOX_WITH_USB */
}
/**
* @note Locks this object for writing, together with the peer object (also
* for writing) if there is one.
*/
void USBController::commit()
{
/* sanity */
AutoCaller autoCaller(this);
/* sanity too */
/* lock both for writing since we modify both (mPeer is "master" so locked
* first) */
if (m->bd.isBackedUp())
{
if (m->pPeer)
{
/* attach new data to the peer and reshare it */
}
}
#ifdef VBOX_WITH_USB
bool commitFilters = false;
if (m->llDeviceFilters.isBackedUp())
{
m->llDeviceFilters.commit();
/* apply changes to peer */
if (m->pPeer)
{
/* commit all changes to new filters (this will reshare data with
* peers for those who have peers) */
{
/* look if this filter has a peer filter */
if (!peer)
{
/* no peer means the filter is a newly created one;
* create a peer owning data this filter share it with */
peer.createObject();
}
else
{
/* remove peer from the old list */
}
/* and add it to the new list */
++ it;
}
/* uninit old peer's filters that are left */
{
++ it;
}
/* attach new list of filters to our peer */
}
else
{
/* we have no peer (our parent is the newly created machine);
* just commit changes to filters */
commitFilters = true;
}
}
else
{
/* the list of filters itself is not changed,
* just commit changes to filters themselves */
commitFilters = true;
}
if (commitFilters)
{
{
++ it;
}
}
#endif /* VBOX_WITH_USB */
}
/**
* @note Locks this object for writing, together with the peer object
* represented by @a aThat (locked for reading).
*/
{
/* sanity */
AutoCaller autoCaller(this);
/* sanity too */
/* even more sanity */
/* Machine::copyFrom() may not be called when the VM is running */
/* peer is not modified, lock it for reading (aThat is "master" so locked
* first) */
/* this will back up current data */
#ifdef VBOX_WITH_USB
/* Note that we won't inform the USB proxy about new filters since the VM is
* not running when we are here and therefore no need to do so */
/* create private copies of all filters */
m->llDeviceFilters.backup();
m->llDeviceFilters->clear();
++ it)
{
}
#endif /* VBOX_WITH_USB */
}
#ifdef VBOX_WITH_USB
/**
* Called by setter methods of all USB device filters.
*
* @note Locks nothing.
*/
{
AutoCaller autoCaller(this);
/* we need the machine state */
/* nothing to do if the machine isn't running */
return S_OK;
/* we don't modify our data fields -- no need to lock */
{
if (aActiveChanged)
{
{
}
else
{
}
}
else
{
{
/* update the filter in the proxy */
}
}
}
return S_OK;
}
/**
* Returns true if the given USB device matches to at least one of
* this controller's USB device filters.
*
* A HostUSBDevice specific version.
*
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
/* Disabled USB controllers cannot actually work with USB devices */
return false;
/* apply self filters */
++ it)
{
{
return true;
}
}
return false;
}
/**
* Returns true if the given USB device matches to at least one of
* this controller's USB device filters.
*
* A generic version that accepts any IUSBDevice on input.
*
* @note
* This method MUST correlate with HostUSBDevice::isMatch()
* in the sense of the device matching logic.
*
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
/* Disabled USB controllers cannot actually work with USB devices */
return false;
/* query fields */
ComAssertComRCRet(rc, false);
ComAssertRet(vendorId, false);
ComAssertComRCRet(rc, false);
ComAssertComRCRet(rc, false);
ComAssertComRCRet(rc, false);
if (!manufacturer.isEmpty())
ComAssertComRCRet(rc, false);
ComAssertComRCRet(rc, false);
if (!serialNumber.isEmpty())
USBFilterSetStringExact (&dev, USBFILTERIDX_SERIAL_NUMBER_STR, Utf8Str(serialNumber).c_str(), true);
ComAssertComRCRet(rc, false);
ComAssertComRCRet(rc, false);
ComAssertComRCRet(rc, false);
bool match = false;
/* apply self filters */
++ it)
{
continue;
continue;
continue;
match = true;
break;
}
return match;
}
/**
* Notifies the proxy service about all filters as requested by the
* @a aInsertFilters argument.
*
* @param aInsertFilters @c true to insert filters, @c false to remove.
*
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
{
/* notify the proxy (only if the filter is active) */
{
if (aInsertFilters)
{
}
else
{
/* It's possible that the given filter was not inserted the proxy
* when this method gets called (as a result of an early VM
* process crash for example. So, don't assert that ID != NULL. */
{
}
}
}
++ it;
}
return S_OK;
}
{
return m->pParent;
}
#endif /* VBOX_WITH_USB */
// private methods
/////////////////////////////////////////////////////////////////////////////
/* vi: set tabstop=4 shiftwidth=4 expandtab: */