HostUSBDeviceImpl.h revision 2782c227354d0c29f08a7933a07f8aa9b30e010d
/* $Id$ */
/** @file
* VirtualBox IHostUSBDevice COM interface implementation.
*/
/*
* Copyright (C) 2006-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* 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.
*/
#ifndef ____H_HOSTUSBDEVICEIMPL
#define ____H_HOSTUSBDEVICEIMPL
#include "VirtualBoxBase.h"
#include "USBDeviceFilterImpl.h"
/* #include "USBProxyService.h" circular on Host/HostUSBDevice, the includer
* must include this. */
#include <VBox/usb.h>
#include "Logging.h"
#include "HostUSBDeviceWrap.h"
class SessionMachine;
class USBProxyService;
/**
* The unified state machine of HostUSBDevice.
*
* This is a super set of USBDEVICESTATE / USBDeviceState_T that
* includes additional states for tracking state transitions.
*
* @remarks
* The CapturingForVM and CapturingForProxy states have been merged
* into Capturing with a destination state (AttachingToVM or HeldByProxy).
*
* The DetachingFromVM state is a merge of DetachingFromVMToProxy and
* DetachingFromVMToHost and uses the destination state (HeldByProxy
* or ReleasingToHost) like Capturing.
*
* The *AwaitingDetach and *AwaitingReattach substates (optionally used
* in Capturing, AttachingToVM, DetachingFromVM and ReleasingToHost) are
* implemented via a substate kHostUSBDeviceSubState.
*/
typedef enum
{
/** The device is unsupported (HUB).
* Next Host: PhysDetached.
* Next VBox: No change permitted.
*/
kHostUSBDeviceState_Unsupported = USBDEVICESTATE_UNSUPPORTED,
/** The device is used exclusivly by the host or is inaccessible for some other reason.
* Next Host: Capturable, Unused, PhysDetached.
* Run filters.
* Next VBox: No change permitted.
*/
kHostUSBDeviceState_UsedByHost = USBDEVICESTATE_USED_BY_HOST,
/** The device is used by the host but can be captured.
* Next Host: Unsupported, UsedByHost, Unused, PhysDetached.
* Run filters if Unused (for wildcard filters).
* Next VBox: CapturingForVM, CapturingForProxy.
*/
kHostUSBDeviceState_Capturable = USBDEVICESTATE_USED_BY_HOST_CAPTURABLE,
/** The device is not used by the host and can be captured.
* Next Host: UsedByHost, Capturable, PhysDetached
* Don't run any filters (done on state entry).
* Next VBox: CapturingForVM, CapturingForProxy.
*/
kHostUSBDeviceState_Unused = USBDEVICESTATE_UNUSED,
/** The device is held captive by the proxy.
* Next Host: PhysDetached
* Next VBox: ReleasingHeld, AttachingToVM
*/
kHostUSBDeviceState_HeldByProxy = USBDEVICESTATE_HELD_BY_PROXY,
/** The device is in use by a VM.
* Next Host: PhysDetachingFromVM
* Next VBox: DetachingFromVM
*/
kHostUSBDeviceState_UsedByVM = USBDEVICESTATE_USED_BY_GUEST,
/** The device has been detach from both the host and VMs.
* This is the final state. */
kHostUSBDeviceState_PhysDetached = 9,
/** The start of the transitional states. */
kHostUSBDeviceState_FirstTransitional,
/** The device is being seized from the host, either for HeldByProxy or for AttachToVM.
*
* On some hosts we will need to re-enumerate the in which case the sub-state
* is employed to track this progress. On others, this is synchronous or faked, and
* will will then leave the device in this state and poke the service thread to do
* the completion state change.
*
* Next Host: PhysDetached.
* Next VBox: HeldByProxy or AttachingToVM on success,
* previous state (Unused or Capturable) or UsedByHost on failure.
*/
kHostUSBDeviceState_Capturing = kHostUSBDeviceState_FirstTransitional,
/** The device is being released back to the host, following VM or Proxy usage.
* Most hosts needs to re-enumerate the device and will therefore employ the
* sub-state as during capturing. On the others we'll just leave it to the usb
* service thread to advance the device state.
*
* Next Host: Unused, UsedByHost, Capturable.
* No filters.
* Next VBox: PhysDetached (timeout), HeldByProxy (failure).
*/
kHostUSBDeviceState_ReleasingToHost,
/** The device is being attached to a VM.
*
* This requires IPC to the VM and we will not advance the state until
* that completes.
*
* Next Host: PhysDetachingFromVM.
* Next VBox: UsedByGuest, HeldByProxy (failure).
*/
kHostUSBDeviceState_AttachingToVM,
/** The device is being detached from a VM and will be returned to the proxy or host.
*
* This involves IPC and may or may not also require re-enumeration of the
* device. Which means that it might transition directly into the ReleasingToHost state
* because the client (VM) will do the actual re-enumeration.
*
* Next Host: PhysDetachingFromVM (?) or just PhysDetached.
* Next VBox: ReleasingToHost, HeldByProxy.
*/
kHostUSBDeviceState_DetachingFromVM,
/** The device has been physically removed while a VM used it.
*
* This is the device state while VBoxSVC is doing IPC to the client (VM) telling it
* to detach it.
*
* Next Host: None.
* Next VBox: PhysDetached
*/
kHostUSBDeviceState_PhysDetachingFromVM,
/** Just an invalid state value for use as default for some methods. */
kHostUSBDeviceState_Invalid = 0x7fff
} HostUSBDeviceState;
/**
* Sub-state for dealing with device re-enumeration.
*/
typedef enum
{
/** Not in any sub-state. */
kHostUSBDeviceSubState_Default = 0,
/** Awaiting a logical device detach following a device re-enumeration. */
kHostUSBDeviceSubState_AwaitingDetach,
/** Awaiting a logical device re-attach following a device re-enumeration. */
kHostUSBDeviceSubState_AwaitingReAttach
} HostUSBDeviceSubState;
/**
* Object class used to hold Host USB Device properties.
*/
class ATL_NO_VTABLE HostUSBDevice :
public HostUSBDeviceWrap
{
public:
DECLARE_EMPTY_CTOR_DTOR(HostUSBDevice)
HRESULT FinalConstruct();
void FinalRelease();
// public initializer/uninitializer for internal purposes only
HRESULT init(PUSBDEVICE aUsb, USBProxyService *aUSBProxyService);
void uninit();
// public methods only for internal purposes
/** @note Must be called from under the object read lock. */
const Guid& i_getId() const { return mId; }
/** @note Must be called from under the object read lock. */
HostUSBDeviceState i_getUnistate() const { return mUniState; }
/** @note Must be called from under the object read lock. */
const char *i_getStateName() { return i_stateName (mUniState, mPendingUniState, mUniSubState); }
/** @note Must be called from under the object read lock. */
bool i_isCapturableOrHeld()
{
return mUniState == kHostUSBDeviceState_Unused
|| mUniState == kHostUSBDeviceState_Capturable
|| mUniState == kHostUSBDeviceState_HeldByProxy;
}
/** @note Must be called from under the object read lock. */
ComObjPtr<SessionMachine> &i_getMachine() { return mMachine; }
/** @note Must be called from under the object read lock. */
PCUSBDEVICE i_getUsbData() const { return mUsb; }
com::Utf8Str i_getName();
HRESULT i_requestCaptureForVM(SessionMachine *aMachine, bool aSetError,
const com::Utf8Str &aCaptureFilename, ULONG aMaskedIfs = 0);
HRESULT i_onDetachFromVM(SessionMachine *aMachine, bool aDone, bool *aRunFilters, bool aAbnormal = false);
HRESULT i_requestReleaseToHost();
HRESULT i_requestHold();
bool i_wasActuallyDetached();
void i_onPhysicalDetached();
bool i_isMatch(const USBDeviceFilter::Data &aData);
int i_compare(PCUSBDEVICE aDev2);
static int i_compare(PCUSBDEVICE aDev1, PCUSBDEVICE aDev2, bool aIsAwaitingReAttach = false);
bool i_updateState(PCUSBDEVICE aDev, bool *aRunFilters, SessionMachine **aIgnoreMachine);
bool i_updateStateFake(PCUSBDEVICE aDev, bool *aRunFilters, SessionMachine **aIgnoreMachine);
static const char *i_stateName(HostUSBDeviceState aState,
HostUSBDeviceState aPendingState = kHostUSBDeviceState_Invalid,
HostUSBDeviceSubState aSubState = kHostUSBDeviceSubState_Default);
protected:
HRESULT i_attachToVM(SessionMachine *aMachine, const com::Utf8Str &aCaptureFilename, ULONG aMaskedIfs = 0);
void i_detachFromVM(HostUSBDeviceState aFinalState);
void i_onPhysicalDetachedInternal();
bool i_hasAsyncOperationTimedOut() const;
bool i_setState (HostUSBDeviceState aNewState, HostUSBDeviceState aNewPendingState = kHostUSBDeviceState_Invalid,
HostUSBDeviceSubState aNewSubState = kHostUSBDeviceSubState_Default);
bool i_startTransition (HostUSBDeviceState aNewState, HostUSBDeviceState aFinalState,
HostUSBDeviceSubState aNewSubState = kHostUSBDeviceSubState_Default);
bool i_advanceTransition(bool aSkipReAttach = false);
bool i_failTransition();
USBDeviceState_T i_canonicalState() const;
private:
// wrapped IUSBDevice properties
HRESULT getId(com::Guid &aId);
HRESULT getVendorId(USHORT *aVendorId);
HRESULT getProductId(USHORT *aProductId);
HRESULT getRevision(USHORT *aRevision);
HRESULT getManufacturer(com::Utf8Str &aManufacturer);
HRESULT getProduct(com::Utf8Str &aProduct);
HRESULT getSerialNumber(com::Utf8Str &aSerialNumber);
HRESULT getAddress(com::Utf8Str &aAddress);
HRESULT getPort(USHORT *aPort);
HRESULT getVersion(USHORT *aVersion);
HRESULT getPortVersion(USHORT *aPortVersion);
HRESULT getSpeed(USBConnectionSpeed_T *aSpeed);
HRESULT getRemote(BOOL *aRemote);
HRESULT getName(com::Utf8Str &aName);
HRESULT getState(USBDeviceState_T *aState);
const Guid mId;
/** @name The state machine variables
* Only setState(), init() and uninit() will modify these members!
* @{ */
/** The RTTimeNanoTS() corresponding to the last state change.
*
* Old state machine: RTTimeNanoTS() of when mIsStatePending was set or mDetaching changed
* from kNotDetaching. For operations that cannot be canceled it's 0. */
uint64_t mLastStateChangeTS;
/** Current state. */
HostUSBDeviceState mUniState;
/** Sub-state for tracking re-enumeration. */
HostUSBDeviceSubState mUniSubState;
/** The final state of an pending transition.
* This is mainly a measure to reduce the number of HostUSBDeviceState values. */
HostUSBDeviceState mPendingUniState;
/** Previous state.
* This is used for bailing out when a transition like capture fails. */
HostUSBDeviceState mPrevUniState;
/** Indicator set by onDetachedPhys and check when advancing a transitional state. */
bool mIsPhysicallyDetached;
/** @} */
/** The machine the usb device is (being) attached to. */
ComObjPtr<SessionMachine> mMachine;
/** Pointer to the USB Proxy Service instance. */
USBProxyService *mUSBProxyService;
/** Pointer to the USB Device structure owned by this device.
* Only used for host devices. */
PUSBDEVICE mUsb;
/** The interface mask to be used in the pending capture.
* This is a filter property. */
ULONG mMaskedIfs;
/** The name of this device. */
Utf8Str mNameObj;
/** The name of this device (for logging purposes).
* This points to the string in mNameObj. */
const char *mName;
/** The filename to capture the USB traffic to. */
com::Utf8Str mCaptureFilename;
friend class USBProxyService;
#ifdef RT_OS_SOLARIS
friend class USBProxyServiceSolaris;
/** One-shot filter id only for new code */
void *mOneShotId;
#endif
#ifdef RT_OS_LINUX
friend class USBProxyServiceLinux;
#endif
#ifdef RT_OS_DARWIN
/** One-shot filter id. */
void *mOneShotId;
friend class USBProxyServiceDarwin;
#endif
#ifdef RT_OS_FreeBSD
friend class USBProxyServiceFreeBSD;
#endif
};
#endif // ____H_HOSTUSBDEVICEIMPL
/* vi: set tabstop=4 shiftwidth=4 expandtab: */