HostUSBDeviceImpl.cpp revision 730b109e93240f48a2fc4ca97c05e5f347f71ec9
/* $Id$ */
/** @file
* VirtualBox IHostUSBDevice COM interface implementation.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* Oracle Corporation confidential
* All rights reserved
*/
#include "HostUSBDeviceImpl.h"
#include "MachineImpl.h"
#include "HostImpl.h"
#include "VirtualBoxErrorInfoImpl.h"
#include "USBProxyService.h"
#include "AutoCaller.h"
#include "Logging.h"
// constructor / destructor
/////////////////////////////////////////////////////////////////////////////
{
return S_OK;
}
void HostUSBDevice::FinalRelease()
{
uninit();
}
// public initializer/uninitializer for internal purposes only
/////////////////////////////////////////////////////////////////////////////
/**
* Initializes the USB device object.
*
* @returns COM result indicator
* @param aUsb Pointer to the usb device structure for which the object is to be a wrapper.
* This structure is now fully owned by the HostUSBDevice object and will be
* freed when it is destructed.
* @param aUSBProxyService Pointer to the USB Proxy Service object.
*/
{
/* Enclose the state transition NotReady->InInit->Ready */
AutoInitSpan autoInitSpan(this);
/*
* We need a unique ID for this VBoxSVC session.
* The UUID isn't stored anywhere.
*/
/*
* Set the initial device state.
*/
mIsPhysicallyDetached = false;
/* Other data members */
/* Set the name. */
/* Confirm the 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 HostUSBDevice::uninit()
{
/* Enclose the state transition Ready->InUninit->NotReady */
AutoUninitSpan autoUninitSpan(this);
if (autoUninitSpan.uninitDone())
return;
{
}
}
// IUSBDevice properties
/////////////////////////////////////////////////////////////////////////////
{
AutoCaller autoCaller(this);
/* mId is constant during life time, no need to lock */
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
#if !defined(RT_OS_WINDOWS) /// @todo check up the bPort value on Windows before enabling this.
#else
*aPort = 0;
#endif
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
{
AutoCaller autoCaller(this);
/* Port version is 2 (EHCI) if and only if the device runs at high speed;
* if speed is unknown, fall back to the old and innacurate method.
*/
else
return S_OK;
}
{
AutoCaller autoCaller(this);
return S_OK;
}
// IHostUSBDevice properties
/////////////////////////////////////////////////////////////////////////////
{
AutoCaller autoCaller(this);
*aState = canonicalState();
return S_OK;
}
// public methods only for internal purposes
////////////////////////////////////////////////////////////////////////////////
/**
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
if (haveManufacturer && haveProduct)
else if (haveManufacturer)
else if (haveProduct)
else
name = "<unknown>";
return name;
}
/**
* Requests the USB proxy service capture the device (from the host)
* and attach it to a VM.
*
* As a convenience, this method will operate like attachToVM() if the device
* is already held by the proxy. Note that it will then perform IPC to the VM
* process, which means it will temporarily leave all locks. (Is this a good idea?)
*
* @param aMachine Machine this device should be attach to.
* @param aSetError Whether to set full error message or not to bother.
* @param aMaskedIfs The interfaces to hide from the guest.
*
* @retval S_OK on success.
* @retval E_UNEXPECTED if the device state doesn't permit for any attaching.
* @retval E_* as appropriate.
*
* @note Must be called from under the object write lock.
* @note May lock the given machine object for reading.
*/
HRESULT HostUSBDevice::requestCaptureForVM(SessionMachine *aMachine, bool aSetError, ULONG aMaskedIfs /* = 0*/)
{
/*
* Validate preconditions and input.
*/
if (aSetError)
{
return setError(E_INVALIDARG,
tr("USB device '%s' with UUID {%RTuuid} cannot be accessed by guest computers"),
return setError(E_INVALIDARG,
tr("USB device '%s' with UUID {%RTuuid} is being exclusively used by the host computer"),
{
/* Machine::name() requires a read lock */
return setError(E_INVALIDARG,
tr("USB device '%s' with UUID {%RTuuid} is already captured by the virtual machine '%ls'"),
}
return setError(E_INVALIDARG,
tr("USB device '%s' with UUID {%RTuuid} is busy with a previous request. Please try again later"),
if ( mUniState != kHostUSBDeviceState_Unused
return setError(E_INVALIDARG,
tr("USB device '%s' with UUID {%RTuuid} is not in the right state for capturing (%s)"),
}
/*
* If it's already held by the proxy, we'll simply call
* attachToVM synchronously.
*/
{
}
/*
* Need to capture the device before it can be used.
*
* The device will be attached to the VM by the USB proxy service thread
* when the request succeeds (i.e. asynchronously).
*/
|| defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS)
setState(kHostUSBDeviceState_Capturing, kHostUSBDeviceState_UsedByVM, kHostUSBDeviceSubState_AwaitingDetach);
#else
#endif
if (RT_FAILURE(rc))
{
if (rc == VERR_SHARING_VIOLATION)
tr("USB device '%s' with UUID {%RTuuid} is in use by someone else"),
return E_FAIL;
}
return S_OK;
}
/**
* Attempts to attach the USB device to a VM.
*
* The device must be in the HeldByProxy state or about to exit the
* Capturing state.
*
* This method will make an IPC to the VM process and do the actual
* attaching. While in the IPC locks will be abandond.
*
* @returns Status indicating whether it was successfully attached or not.
* @retval S_OK on success.
* @retval E_UNEXPECTED if the device state doesn't permit for any attaching.
* @retval E_* as appropriate.
*
* @param aMachine Machine this device should be attach to.
* @param aMaskedIfs The interfaces to hide from the guest.
*/
{
/*
* Validate and update the state.
*/
/*
* The VM process will query the object, so grab a refernce to ourselves and leave the locks.
*/
ComPtr<IUSBDevice> d = this;
alockProxy.leave();
/*
* Call the VM process (IPC) and request it to attach the device.
*
* There are many reasonas for this to fail, so, as a consequence we don't
* assert the return code as it will crash the daemon and annoy the heck
* out of people.
*/
/*
* As we re-enter the lock, we'll have to check if the device was
* physically detached while we were busy.
*/
alockProxy.enter();
{
if (!mIsPhysicallyDetached)
else
{
hrc = E_UNEXPECTED;
}
}
else
{
if (!mIsPhysicallyDetached)
{
if (hrc == E_UNEXPECTED)
}
else
{
hrc = E_UNEXPECTED;
}
}
return hrc;
}
/**
* Detaches the device from the VM.
*
* This is used for a special scenario in attachToVM() and from
* onPhysicalDetachedInternal().
*
* @param aFinalState The final state (PhysDetached).
*/
{
/*
* Assert preconditions.
*/
/*
* Change the state and abandond the locks. The VM may query
* data and we don't want to deadlock - the state protects us,
* so, it's not a bit issue here.
*/
alockProxy.leave();
/*
* Call the VM process (IPC) and request it to detach the device.
*
* There are many reasonas for this to fail, so, as a consequence we don't
* assert the return code as it will crash the daemon and annoy the heck
* out of people.
*/
/*
* Re-enter the locks and complete the transition.
*/
alockProxy.enter();
}
/**
* Called when the VM process to inform us about the device being
* detached from it.
*
* This is NOT called when we detach the device via onUSBDeviceDetach.
*
*
* @param[in] aMachine The machine making the request.
* This must be the machine this device is currently attached to.
* @param[in] aDone When set to false, the VM just informs us that it's about
* to detach this device but hasn't done it just yet.
* When set to true, the VM informs us that it has completed
* the detaching of this device.
* @param[out] aRunFilters Whether to run filters.
* @param[in] aAbnormal Set if we're cleaning up after a crashed VM.
*
* @returns S_OK on success, and E_UNEXPECTED if the device isn't in the right state.
*
* @note Must be called from under the object write lock.
*/
HRESULT HostUSBDevice::onDetachFromVM(SessionMachine *aMachine, bool aDone, bool *aRunFilters, bool aAbnormal /*= true*/)
{
LogFlowThisFunc(("{%s} state=%s aDone=%RTbool aAbnormal=%RTbool\n", mName, getStateName(), aDone, aAbnormal));
/*
* Validate preconditions.
*/
if (!aDone)
{
return setError(E_INVALIDARG,
tr("USB device '%s' with UUID {%RTuuid} is busy (state '%s'). Please try again later"),
}
else
AssertMsgReturn( mUniState == kHostUSBDeviceState_DetachingFromVM /** @todo capturing for VM ends up here on termination. */
/*
* Change the state.
*/
if (!aDone)
{
*aRunFilters = startTransition(kHostUSBDeviceState_DetachingFromVM, kHostUSBDeviceState_HeldByProxy);
/* PORTME: This might require host specific changes if you re-enumerate the device. */
}
{
/* Fast forward thru the DetachingFromVM state and on to HeldByProxy. */
/** @todo need to update the state machine to handle crashed VMs. */
*aRunFilters = advanceTransition();
/* PORTME: ditto / trouble if you depend on the VM process to do anything. */
}
else
{
/* normal completion. */
*aRunFilters = advanceTransition();
}
return S_OK;
}
/**
* Requests the USB proxy service to release the device back to the host.
*
* This method will ignore (not assert) calls for devices that already
* belong to the host because it simplifies the usage a bit.
*
* @returns COM status code.
* @retval S_OK on success.
* @retval E_UNEXPECTED on bad state.
* @retval E_* as appropriate.
*
* @note Must be called from under the object write lock.
*/
{
/*
* Validate preconditions.
*/
if ( mUniState == kHostUSBDeviceState_Unused
return S_OK;
AssertMsgReturn(mUniState == kHostUSBDeviceState_HeldByProxy, ("{%s} %s\n", mName, getStateName()), E_UNEXPECTED);
/*
* Try release it.
*/
|| defined(RT_OS_WINDOWS)
startTransition(kHostUSBDeviceState_ReleasingToHost, kHostUSBDeviceState_Unused, kHostUSBDeviceSubState_AwaitingDetach);
#else
#endif
if (RT_FAILURE(rc))
{
return E_FAIL;
}
return S_OK;
}
/**
* Requests the USB proxy service to capture and hold the device.
*
* The device must be owned by the host at the time of the call. But for
* the callers convenience, calling this method on a device that is already
* being held will success without any assertions.
*
* @returns COM status code.
* @retval S_OK on success.
* @retval E_UNEXPECTED on bad state.
* @retval E_* as appropriate.
*
* @note Must be called from under the object write lock.
*/
{
/*
* Validate preconditions.
*/
return S_OK;
/*
* Do the job.
*/
|| defined(RT_OS_WINDOWS)
startTransition(kHostUSBDeviceState_Capturing, kHostUSBDeviceState_HeldByProxy, kHostUSBDeviceSubState_AwaitingDetach);
#else
#endif
if (RT_FAILURE(rc))
{
return E_FAIL;
}
return S_OK;
}
/**
* Check a detach detected by the USB Proxy Service to see if
* it's a real one or just a logical following a re-enumeration.
*
* This will work the internal sub state of the device and do time
* outs, so it does more than just querying data!
*
* @returns true if it was actually detached, false if it's just a re-enumeration.
*/
bool HostUSBDevice::wasActuallyDetached()
{
/*
* This only applies to the detach and re-attach states.
*/
switch (mUniState)
{
switch (mUniSubState)
{
/*
* If we're awaiting a detach, the this has now occurred
* and the state should be advanced.
*/
return false; /* not physically detached. */
/*
* Check for timeouts.
*/
{
#ifndef RT_OS_WINDOWS /* check the implementation details here. */
{
}
#endif
return false; /* not physically detached. */
}
/* not applicable.*/
break;
}
break;
/* not applicable. */
break;
default:
break;
}
/* It was detached. */
return true;
}
/**
* Notification from the USB Proxy that the device was physically detached.
*
* If a transition is pending, mIsPhysicallyDetached will be set and
* handled when the transition advances forward.
*
* Otherwsie the device will be detached from any VM currently using it - this
* involves IPC and will temporarily abandond locks - and all the device data
* reset.
*
* @note Must be called from under the object write lock.
*/
void HostUSBDevice::onPhysicalDetached()
{
mIsPhysicallyDetached = true;
}
/**
* Do the physical detach work for a device in a stable state or
* at a transition state change.
*
* See onPhysicalDetach() for details.
*
* @note Must be called from under the object write lock.
*/
{
/*
* Do we need to detach it from the VM first?
*/
else
/*
* Reset the data and enter the final state.
*/
}
/**
* Returns true if this device matches the given filter data.
*
* @note It is assumed, that the filter data owner is appropriately
* locked before calling this method.
*
* @note
* This method MUST correlate with
* USBController::hasMatchingFilter (IUSBDevice *)
* in the sense of the device matching logic.
*
* @note Locks this object for reading.
*/
{
AutoCaller autoCaller(this);
return false;
return false;
return false;
/* Don't match busy devices with a 100% wildcard filter - this will
later become a filter prop (ring-3 only). */
return false;
LogFlowThisFunc(("returns true\n"));
return true;
}
/**
* Compares this device with a USBDEVICE and decides if the match or which comes first.
*
* This will take into account device re-attaching and omit the bits
* that may change during a device re-enumeration.
*
* @param aDev2 Device 2.
*
* @returns < 0 if this should come before aDev2.
* @returns 0 if this and aDev2 are equal.
* @returns > 0 if this should come after aDev2.
*
* @note Must be called from under the object write lock.
*/
{
//Log3(("%Rfn: %p {%s}\n", __PRETTY_FUNCTION__, this, mName));
mUniSubState == kHostUSBDeviceSubState_AwaitingDetach /* (In case we don't get the detach notice.) */
}
/**
* Compares two USBDEVICE structures and decides if the match or which comes first.
*
* @param aDev1 Device 1.
* @param aDev2 Device 2.
* @param aIsAwaitingReAttach Whether to omit bits that will change in a device
* re-enumeration (true) or not (false).
*
* @returns < 0 if aDev1 should come before aDev2.
* @returns 0 if aDev1 and aDev2 are equal.
* @returns > 0 if aDev1 should come after aDev2.
*/
/*static*/
int HostUSBDevice::compare(PCUSBDEVICE aDev1, PCUSBDEVICE aDev2, bool aIsAwaitingReAttach /*= false */)
{
/*
* Things that stays the same everywhere.
*
* The more uniquely these properties identifies a device the less the chance
* that we mix similar devices during re-enumeration. Bus+port would help
* provide ~99.8% accuracy if the host can provide those attributes.
*/
if (iDiff)
return iDiff;
if (iDiff)
return iDiff;
if (iDiff)
{
//Log3(("compare: bcdDevice: %#x != %#x\n", aDev1->bcdDevice, aDev2->bcdDevice));
return iDiff;
}
#ifdef RT_OS_WINDOWS /* the string query may fail on windows during replugging, ignore serial mismatch if this is the case. */
&& ( !aIsAwaitingReAttach
)
#else
#endif
{
//Log3(("compare: u64SerialHash: %#llx != %#llx\n", aDev1->u64SerialHash, aDev2->u64SerialHash));
}
#ifdef RT_OS_WINDOWS
if (iDiff)
{
//Log3(("compare: HubName: %s != %s\n", aDev1->pszHubName, aDev2->pszHubName));
return iDiff;
}
#else
if (iDiff)
{
//Log3(("compare: bBus: %#x != %#x\n", aDev1->bBus, aDev2->bBus));
return iDiff;
}
#endif
iDiff = aDev1->bPort - aDev2->bPort; /* shouldn't change anywhere and help pinpoint it very accurately. */
if (iDiff)
{
//Log3(("compare: bPort: %#x != %#x\n", aDev1->bPort, aDev2->bPort));
return iDiff;
}
/*
* Things that usually doesn't stay the same when re-enumerating
* a device. The fewer things in the category the better chance
* that we avoid messing up when more than one device of the same
* kind is attached.
*/
if (aIsAwaitingReAttach)
{
//Log3(("aDev1=%p == aDev2=%p\n", aDev1, aDev2));
return 0;
}
/* device number always changes. */
}
/**
* Updates the state of the device.
*
* If this method returns @c true, Host::onUSBDeviceStateChanged() will be
* called to process the state change (complete the state change request,
* inform the VM process etc.).
*
* If this method returns @c false, it is assumed that the given state change
* is "minor": it doesn't require any further action other than update the
* mState field with the actual state value.
*
* Regardless of the return value, this method always takes ownership of the
* new USBDEVICE structure passed in and updates the pNext and pPrev fiends in
* it using the values of the old structure.
*
* @param[in] aDev The current device state as seen by the proxy backend.
* @param[out] aRunFilters Whether the state change should be accompanied by
* running filters on the device.
* @param[out] aIgnoreMachine Machine to ignore when running filters.
*
* @returns Whether the Host object should be bothered with this state change.
*
* @note Locks this object for writing.
*
* @todo Just do everything here, that is, call filter runners and everything that
* works by state change. Using 3 return codes/parameters is just plain ugly.
*/
bool HostUSBDevice::updateState(PCUSBDEVICE aDev, bool *aRunFilters, SessionMachine **aIgnoreMachine)
{
*aRunFilters = false;
*aIgnoreMachine = NULL;
/*
* Locking.
*/
AssertReturn(isWriteLockOnCurrentThread(), false);
AutoCaller autoCaller(this);
/*
* Replace the existing structure by the new one.
*/
{
}
/** @def HOSTUSBDEVICE_FUZZY_STATE
* Defined on hosts where we have a driver that keeps proper device states.
*/
# if defined(RT_OS_LINUX) || defined(DOXYGEN_RUNNING)
# define HOSTUSBDEVICE_FUZZY_STATE 1
# else
# endif
/*
* For some hosts we'll have to be pretty carefule here because
* they don't always have a clue what is going on. This is
* particularly true on linux and solaris, while windows and
* darwin generally knows a bit more.
*/
bool fIsImportant = false;
{
{
/*
* Little fuzzyness here, except where we fake capture.
*/
switch (mUniState)
{
/* Host drivers installed, that's fine. */
break;
break;
/* Can only mean that we've failed capturing it. */
*aRunFilters = failTransition();
break;
/* Guess we've successfully released it. */
break;
/* These are IPC states and should be left alone. */
break;
#ifdef HOSTUSBDEVICE_FUZZY_STATE
/* Fake: We can't prevent anyone from grabbing it. */
LogThisFunc(("{%s} %s -> %s!\n", mName, getStateName(), stateName(kHostUSBDeviceState_UsedByHost)));
break;
//case kHostUSBDeviceState_UsedByVM:
// /** @todo needs to be detached from the VM. */
// break;
#endif
/* Not supposed to happen... */
#ifndef HOSTUSBDEVICE_FUZZY_STATE
#endif
default:
break;
}
break;
/*
* It changed to capturable. Fuzzy hosts might easily
* confuse UsedByVM with this one.
*/
switch (mUniState)
{
/* No change. */
#ifdef HOSTUSBDEVICE_FUZZY_STATE
#endif
break;
/* Changed! */
fIsImportant = true;
break;
/* Can only mean that we've failed capturing it. */
*aRunFilters = failTransition();
break;
/* Guess we've successfully released it. */
break;
/* These are IPC states and should be left alone. */
break;
/* Not supposed to happen*/
#ifndef HOSTUSBDEVICE_FUZZY_STATE
#endif
default:
break;
}
break;
/*
* It changed to capturable. Fuzzy hosts might easily
* confuse UsedByVM and HeldByProxy with this one.
*/
case USBDEVICESTATE_UNUSED:
switch (mUniState)
{
/* No change. */
#ifdef HOSTUSBDEVICE_FUZZY_STATE
#endif
break;
/* Changed! */
fIsImportant = true;
break;
/* Can mean that we've failed capturing it, but on windows it is the detach signal. */
#if defined(RT_OS_WINDOWS)
{
*aRunFilters = advanceTransition();
}
else
#endif
{
*aRunFilters = failTransition();
}
break;
/* Guess we've successfully released it. */
break;
/* These are IPC states and should be left alone. */
break;
/* Not supposed to happen*/
#ifndef HOSTUSBDEVICE_FUZZY_STATE
#endif
default:
break;
}
break;
/*
* This is pretty straight forward, except that everyone
* might sometimes confuse this and the UsedByVM state.
*/
switch (mUniState)
{
/* No change. */
break;
break;
/* Guess we've successfully captured it. */
/* Take action if we're supposed to attach it to a VM. */
break;
/* Can only mean that we've failed capturing it. */
break;
/* These are IPC states and should be left alone. */
break;
/* Not supposed to happen. */
default:
break;
}
break;
/*
* This is very straight forward and only Darwin implements it.
*/
switch (mUniState)
{
/* No change. */
break;
break;
/* These are IPC states and should be left alone. */
break;
/* Not supposed to happen. */
default:
break;
}
break;
/*
* This is not supposed to happen and indicates a bug in the backend!
*/
break;
default:
break;
}
}
else if ( mUniSubState == kHostUSBDeviceSubState_AwaitingDetach
{
LogRel(("USB: timeout in %s for {%RTuuid} / {%s}\n",
*aRunFilters = failTransition();
fIsImportant = true;
}
else
{
/** @todo might have to handle some stuff here too if we cannot make the release/capture handling deal with that above ... */
}
return fIsImportant;
}
/**
* Updates the state of the device, checking for cases which we fake.
*
* See HostUSBDevice::updateState() for details.
*
* @param[in] aDev See HostUSBDevice::updateState().
* @param[out] aRunFilters See HostUSBDevice::updateState()
* @param[out] aIgnoreMachine See HostUSBDevice::updateState()
*
* @returns See HostUSBDevice::updateState()
*
* @note Caller must write lock the object.
*/
bool HostUSBDevice::updateStateFake(PCUSBDEVICE aDev, bool *aRunFilters, SessionMachine **aIgnoreMachine)
{
switch (enmState)
{
{
*aRunFilters = advanceTransition();
{
}
/* call the completion method */
else
/* Take action if we're supposed to attach it to a VM. */
return true;
}
default:
}
}
/**
* Checks if there is a pending asynchronous operation and whether
* it has timed out or not.
*
* @returns true on timeout, false if not.
*
* @note Caller must have read or write locked the object before calling.
*/
bool HostUSBDevice::hasAsyncOperationTimedOut() const
{
switch (mUniSubState)
{
#ifndef RT_OS_WINDOWS /* no timeouts on windows yet since I don't have all the details here... */
{
}
#endif
default:
return false;
}
}
/**
* Translate the state into
*
* @returns
* @param aState
* @param aSubState
* @param aPendingState
*/
{
switch (aState)
{
return "Unsupported";
return "UsedByHost";
return "Capturable";
return "Unused";
return "HeldByProxy";
return "UsedByVM";
return "PhysDetached";
switch (aPendingState)
{
switch (aSubState)
{
return "CapturingForVM";
return "CapturingForVM[Detach]";
return "CapturingForVM[Attach]";
default:
AssertFailedReturn("CapturingForVM[bad]");
}
break;
switch (aSubState)
{
return "CapturingForProxy";
return "CapturingForProxy[Detach]";
return "CapturingForProxy[Attach]";
default:
AssertFailedReturn("CapturingForProxy[bad]");
}
break;
default:
AssertFailedReturn("Capturing{bad}");
}
break;
switch (aPendingState)
{
switch (aSubState)
{
return "ReleasingToHost";
return "ReleasingToHost[Detach]";
return "ReleasingToHost[Attach]";
default:
AssertFailedReturn("ReleasingToHost[bad]");
}
break;
default:
AssertFailedReturn("ReleasingToHost{bad}");
}
break;
switch (aPendingState)
{
switch (aSubState)
{
return "DetatchingFromVM>Proxy";
return "DetatchingFromVM>Proxy[Detach]";
return "DetatchingFromVM>Proxy[Attach]";
default:
AssertFailedReturn("DetatchingFromVM>Proxy[bad]");
}
break;
switch (aSubState)
{
return "DetachingFromVM>Host";
return "DetachingFromVM>Host[Detach]";
return "DetachingFromVM>Host[Attach]";
default:
AssertFailedReturn("DetachingFromVM>Host[bad]");
}
break;
default:
AssertFailedReturn("DetachingFromVM{bad}");
}
break;
switch (aPendingState)
{
switch (aSubState)
{
return "AttachingToVM";
return "AttachingToVM[Detach]";
return "AttachingToVM[Attach]";
default:
AssertFailedReturn("AttachingToVM[bad]");
}
break;
default:
AssertFailedReturn("AttachingToVM{bad}");
}
break;
switch (aPendingState)
{
switch (aSubState)
{
return "PhysDetachingFromVM";
default:
AssertFailedReturn("AttachingToVM[bad]");
}
break;
default:
AssertFailedReturn("AttachingToVM{bad}");
}
break;
default:
AssertFailedReturn("BadState");
}
AssertFailedReturn("shouldn't get here");
}
/**
* Set the device state.
*
* This method will verify that the state transition is a legal one
* according to the statemachine. It will also take care of the
* associated house keeping and determin if filters needs to be applied.
*
* @param aNewState The new state.
* @param aNewPendingState The final state of a transition when applicable.
* @param aNewSubState The new sub-state when applicable.
*
* @returns true if filters should be applied to the device, false if not.
*
* @note The caller must own the write lock for this object.
*/
bool HostUSBDevice::setState(HostUSBDeviceState aNewState, HostUSBDeviceState aNewPendingState /*= kHostUSBDeviceState_Invalid*/,
{
/*
* If the state is unchanged, then don't bother going
* thru the validation and setting. This saves a bit of code.
*/
&& aNewSubState == mUniSubState)
return false;
/*
* Welcome to the switch orgies!
* You're welcome to check out the ones in startTransition(),
* advanceTransition(), failTransition() and getStateName() too. Enjoy!
*/
bool fFilters = false;
switch (mUniState)
{
/*
* Not much can be done with a device in this state.
*/
switch (aNewState)
{
break;
default:
}
break;
/*
* Only the host OS (or the user) can make changes
* that'll make a device get out of this state.
*/
switch (aNewState)
{
fFilters = true;
break;
default:
}
break;
/*
* Now it gets interesting.
*/
switch (aNewState)
{
/* Host changes. */
fFilters = true; /* Wildcard only... */
break;
/* VBox actions */
switch (aNewPendingState)
{
break;
default:
}
break;
default:
}
break;
switch (aNewState)
{
/* Host changes. */
break;
/* VBox actions */
switch (aNewPendingState)
{
break;
default:
}
break;
default:
}
break;
/*
* VBox owns this device now, what's next...
*/
switch (aNewState)
{
/* Host changes. */
break;
/* VBox actions */
switch (aNewPendingState)
{
break;
default:
}
break;
switch (aNewPendingState)
{
case kHostUSBDeviceState_Unused: /* Only this! */
break;
default:
}
break;
default:
}
break;
switch (aNewState)
{
/* Host changes. */
break;
/* VBox actions */
switch (aNewPendingState)
{
case kHostUSBDeviceState_Unused: /* Only this! */
break;
default:
}
break;
default:
}
break;
/*
* The final state.
*/
switch (mUniState)
{
case kHostUSBDeviceState_DetachingFromVM: // ??
break;
case kHostUSBDeviceState_AttachingToVM: // ??
default:
}
break;
/*
* The transitional states.
*/
switch (aNewState)
{
/* Sub state advance. */
switch (aNewSubState)
{
break;
default:
}
break;
break;
break;
/* VBox */
break;
break;
default:
}
break;
switch (aNewState)
{
/* Sub state advance. */
switch (aNewSubState)
{
break;
default:
}
break;
break;
break;
/* Success */
break;
default:
}
break;
switch (aNewState)
{
break;
break;
/* Success */
break;
default:
}
break;
switch (aNewState)
{
case kHostUSBDeviceState_PhysDetached: //??
break;
break;
/* Success */
fFilters = true;
break;
break;
default:
}
break;
switch (aNewState)
{
break;
default:
}
break;
default:
}
/*
* Make the state change.
*/
if (NewPrevState != mPrevUniState)
LogFlowThisFunc(("%s -> %s (prev: %s -> %s) [%s]\n",
else
LogFlowThisFunc(("%s -> %s (prev: %s) [%s]\n",
getStateName(), stateName(aNewState, aNewPendingState, aNewSubState), stateName(NewPrevState), mName));
return fFilters;
}
/**
* A convenience for entering a transitional state.
* @param aNewState The new state (transitional).
* @param aFinalSubState The final state of the transition (non-transitional).
* @param aNewSubState The new sub-state when applicable.
*
* @returns Always false because filters are never applied for the start of a transition.
*
* @note The caller must own the write lock for this object.
*/
{
/*
* A quick prevalidation thing. Not really necessary since setState
* verifies this too, but it's very easy here.
*/
switch (mUniState)
{
break;
default:
}
}
/**
* A convenience for advancing a transitional state forward.
*
* @param aSkipReAttach Fast forwards thru the re-attach substate if
* applicable.
*
* @returns true if filters should be applied to the device, false if not.
*
* @note The caller must own the write lock for this object.
*/
{
switch (enmState)
{
switch (enmSub)
{
break;
/* fall thru */
switch (enmPending)
{
break;
break;
default:
AssertMsgFailedReturn(("this=%p invalid pending state %d: %s\n", this, enmPending, getStateName()), false);
}
break;
default:
}
break;
switch (enmSub)
{
break;
/* fall thru */
switch (enmPending)
{
/* Use Unused here since it implies that filters has been applied
and will make sure they aren't applied if the final state really
is Capturable. */
break;
default:
AssertMsgFailedReturn(("this=%p invalid pending state %d: %s\n", this, enmPending, getStateName()), false);
}
break;
default:
}
break;
switch (enmSub)
{
break;
/* fall thru */
switch (enmPending)
{
break;
default:
AssertMsgFailedReturn(("this=%p invalid pending state %d: %s\n", this, enmPending, getStateName()), false);
}
break;
default:
}
break;
switch (enmSub)
{
break;
/* fall thru */
switch (enmPending)
{
break;
break;
default:
AssertMsgFailedReturn(("this=%p invalid pending state %d: %s\n", this, enmPending, getStateName()), false);
}
break;
default:
}
break;
switch (enmSub)
{
switch (enmPending)
{
break;
default:
AssertMsgFailedReturn(("this=%p invalid pending state %d: %s\n", this, enmPending, getStateName()), false);
}
break;
default:
}
break;
default:
}
return fRc;
}
/**
* A convenience for failing a transitional state.
*
* @return true if filters should be applied to the device, false if not.
*
* @note The caller must own the write lock for this object.
*/
bool HostUSBDevice::failTransition()
{
switch (enmState)
{
/*
* There are just two cases, either we got back to the
* previous state (assumes Capture+Attach-To-VM updates it)
* or we assume the device has been unplugged (physically).
*/
switch (enmSub)
{
/* fall thru */
break;
break;
default:
}
break;
default:
}
}
/**
* Determins the canonical state of the device.
*
* @returns canonical state.
*
* @note The caller must own the read (or write) lock for this object.
*/
{
switch (mUniState)
{
/*
* Straight forward.
*/
return USBDeviceState_NotSupported;
return USBDeviceState_Unavailable;
return USBDeviceState_Busy;
return USBDeviceState_Available;
return USBDeviceState_Held;
return USBDeviceState_Captured;
/*
* Pretend we've reached the final state.
*/
return mPendingUniState == kHostUSBDeviceState_UsedByVM
/* The cast ^^^^ is because xidl is using different enums for
each of the values. *Very* nice idea... :-) */
return USBDeviceState_Captured;
/*
* Return the previous state.
*/
return mPrevUniState == kHostUSBDeviceState_UsedByVM
/* The cast ^^^^ is because xidl is using different enums for
each of the values. *Very* nice idea... :-) */
return USBDeviceState_Captured;
return USBDeviceState_Captured;
default:
AssertReleaseMsgFailedReturn (("this=%p mUniState=%d\n", this, mUniState), USBDeviceState_NotSupported);
}
/* won't ever get here. */
}
/* vi: set tabstop=4 shiftwidth=4 expandtab: */