/* $Id: HostUSBDeviceImpl.h $ */ /** @file * VirtualBox IHostUSBDevice COM interface implementation. */ /* * Copyright (C) 2006-2023 Oracle and/or its affiliates. * * This file is part of VirtualBox base platform packages, as * available from https://www.virtualbox.org. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation, in version 3 of the * License. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * SPDX-License-Identifier: GPL-3.0-only */ #ifndef MAIN_INCLUDED_HostUSBDeviceImpl_h #define MAIN_INCLUDED_HostUSBDeviceImpl_h #ifndef RT_WITHOUT_PRAGMA_ONCE # pragma once #endif #include "VirtualBoxBase.h" #include "USBDeviceFilterImpl.h" #include #include "HostUSBDeviceWrap.h" class SessionMachine; class USBProxyBackend; /** * 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_COMMON_CLASS_METHODS(HostUSBDevice) HRESULT FinalConstruct(); void FinalRelease(); // public initializer/uninitializer for internal purposes only HRESULT init(PUSBDEVICE aUsb, USBProxyBackend *aUSBProxyBackend); 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 &i_getMachine() { return mMachine; } /** @note Must be called from under the object read lock. */ PCUSBDEVICE i_getUsbData() const { return mUsb; } USBProxyBackend *i_getUsbProxyBackend() const { return mUSBProxyBackend; } 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::BackupableUSBDeviceFilterData &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); void *i_getBackendUserData() { return m_pvBackendUser; } void i_setBackendUserData(void *pvBackendUser) { m_pvBackendUser = pvBackendUser; } 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(HostUSBDeviceState a_enmStateHint); 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 getPortPath(com::Utf8Str &aPortPath); HRESULT getVersion(USHORT *aVersion); HRESULT getPortVersion(USHORT *aPortVersion); HRESULT getSpeed(USBConnectionSpeed_T *aSpeed); HRESULT getRemote(BOOL *aRemote); HRESULT getState(USBDeviceState_T *aState); HRESULT getBackend(com::Utf8Str &aBackend); HRESULT getDeviceInfo(std::vector &aInfo); 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 mMachine; /** Pointer to the USB Proxy Backend instance. */ USBProxyBackend *mUSBProxyBackend; /** 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; /** Optional opaque user data assigned by the USB proxy backend owning the device. */ void *m_pvBackendUser; }; #endif /* !MAIN_INCLUDED_HostUSBDeviceImpl_h */ /* vi: set tabstop=4 shiftwidth=4 expandtab: */