HostUSBDeviceImpl.cpp revision 3ae1af412a33395845eaefd272ec486551a78a25
fa9e4066f08beec538e775443c5be79dd423fcabahrens/** @file
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * VirtualBox IHostUSBDevice COM interface implementation
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
72fc53bc90bd3b199d29d03ee68adb4a5a17d35bmarks
72fc53bc90bd3b199d29d03ee68adb4a5a17d35bmarks/*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Copyright (C) 2006-2007 innotek GmbH
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * This file is part of VirtualBox Open Source Edition (OSE), as
fa9e4066f08beec538e775443c5be79dd423fcabahrens * available from http://www.virtualbox.org. This file is free software;
fa9e4066f08beec538e775443c5be79dd423fcabahrens * you can redistribute it and/or modify it under the terms of the GNU
fa9e4066f08beec538e775443c5be79dd423fcabahrens * General Public License as published by the Free Software Foundation,
fa9e4066f08beec538e775443c5be79dd423fcabahrens * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
fa9e4066f08beec538e775443c5be79dd423fcabahrens * distribution. VirtualBox OSE is distributed in the hope that it will
fa9e4066f08beec538e775443c5be79dd423fcabahrens * be useful, but WITHOUT ANY WARRANTY of any kind.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/types.h> /* for UINT64_C */
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include "HostUSBDeviceImpl.h"
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include "MachineImpl.h"
72fc53bc90bd3b199d29d03ee68adb4a5a17d35bmarks#include "VirtualBoxErrorInfoImpl.h"
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include "USBProxyService.h"
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include "Logging.h"
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <VBox/err.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens#include <iprt/cpputils.h>
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens// constructor / destructor
fa9e4066f08beec538e775443c5be79dd423fcabahrens/////////////////////////////////////////////////////////////////////////////
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensDEFINE_EMPTY_CTOR_DTOR (HostUSBDevice)
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensHRESULT HostUSBDevice::FinalConstruct()
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens mUSBProxyService = NULL;
fa9e4066f08beec538e775443c5be79dd423fcabahrens mUsb = NULL;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensvoid HostUSBDevice::FinalRelease()
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens uninit();
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens// public initializer/uninitializer for internal purposes only
fa9e4066f08beec538e775443c5be79dd423fcabahrens/////////////////////////////////////////////////////////////////////////////
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Initializes the USB device object.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @returns COM result indicator
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param aUsb Pointer to the usb device structure for which the object is to be a wrapper.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * This structure is now fully owned by the HostUSBDevice object and will be
fa9e4066f08beec538e775443c5be79dd423fcabahrens * freed when it is destructed.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @param aUSBProxyService Pointer to the USB Proxy Service object.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensHRESULT HostUSBDevice::init(PUSBDEVICE aUsb, USBProxyService *aUSBProxyService)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens ComAssertRet (aUsb, E_INVALIDARG);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Enclose the state transition NotReady->InInit->Ready */
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoInitSpan autoInitSpan (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertReturn (autoInitSpan.isOk(), E_UNEXPECTED);
72fc53bc90bd3b199d29d03ee68adb4a5a17d35bmarks
fa9e4066f08beec538e775443c5be79dd423fcabahrens /*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * We need a unique ID for this VBoxSVC session.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * The UUID isn't stored anywhere.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens unconst (mId).create();
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /*
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Convert from USBDEVICESTATE to USBDeviceState.
fa9e4066f08beec538e775443c5be79dd423fcabahrens *
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Note that not all proxy backend can detect the HELD_BY_PROXY
fa9e4066f08beec538e775443c5be79dd423fcabahrens * and USED_BY_GUEST states. But that shouldn't matter much.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrens switch (aUsb->enmState)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens default:
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertMsgFailed(("aUsb->enmState=%d\n", aUsb->enmState));
fa9e4066f08beec538e775443c5be79dd423fcabahrens case USBDEVICESTATE_UNSUPPORTED:
fa9e4066f08beec538e775443c5be79dd423fcabahrens mState = USBDeviceState_USBDeviceNotSupported;
fa9e4066f08beec538e775443c5be79dd423fcabahrens break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens case USBDEVICESTATE_USED_BY_HOST:
fa9e4066f08beec538e775443c5be79dd423fcabahrens mState = USBDeviceState_USBDeviceUnavailable;
fa9e4066f08beec538e775443c5be79dd423fcabahrens break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens case USBDEVICESTATE_USED_BY_HOST_CAPTURABLE:
fa9e4066f08beec538e775443c5be79dd423fcabahrens mState = USBDeviceState_USBDeviceBusy;
fa9e4066f08beec538e775443c5be79dd423fcabahrens break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens case USBDEVICESTATE_UNUSED:
fa9e4066f08beec538e775443c5be79dd423fcabahrens mState = USBDeviceState_USBDeviceAvailable;
fa9e4066f08beec538e775443c5be79dd423fcabahrens break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens case USBDEVICESTATE_HELD_BY_PROXY:
fa9e4066f08beec538e775443c5be79dd423fcabahrens mState = USBDeviceState_USBDeviceHeld;
fa9e4066f08beec538e775443c5be79dd423fcabahrens break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens case USBDEVICESTATE_USED_BY_GUEST:
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used
fa9e4066f08beec538e775443c5be79dd423fcabahrens * anywhere in the proxy code; it's quite logical because the
fa9e4066f08beec538e775443c5be79dd423fcabahrens * proxy doesn't know anything about guest VMs. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertFailedReturn (E_FAIL);
fa9e4066f08beec538e775443c5be79dd423fcabahrens break;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens mPendingState = mState;
fa9e4066f08beec538e775443c5be79dd423fcabahrens mPendingStateEx = kNothingPending;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Other data members */
fa9e4066f08beec538e775443c5be79dd423fcabahrens mIsStatePending = false;
fa9e4066f08beec538e775443c5be79dd423fcabahrens mUSBProxyService = aUSBProxyService;
fa9e4066f08beec538e775443c5be79dd423fcabahrens mUsb = aUsb;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Confirm the successful initialization */
fa9e4066f08beec538e775443c5be79dd423fcabahrens autoInitSpan.setSucceeded();
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Uninitializes the instance and sets the ready flag to FALSE.
fa9e4066f08beec538e775443c5be79dd423fcabahrens * Called either from FinalRelease() or by the parent when it gets destroyed.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensvoid HostUSBDevice::uninit()
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* Enclose the state transition Ready->InUninit->NotReady */
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoUninitSpan autoUninitSpan (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (autoUninitSpan.uninitDone())
fa9e4066f08beec538e775443c5be79dd423fcabahrens return;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (mUsb != NULL)
fa9e4066f08beec538e775443c5be79dd423fcabahrens {
fa9e4066f08beec538e775443c5be79dd423fcabahrens USBProxyService::freeDevice (mUsb);
fa9e4066f08beec538e775443c5be79dd423fcabahrens mUsb = NULL;
fa9e4066f08beec538e775443c5be79dd423fcabahrens }
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens mUSBProxyService = NULL;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens// IUSBDevice properties
fa9e4066f08beec538e775443c5be79dd423fcabahrens/////////////////////////////////////////////////////////////////////////////
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Id)(GUIDPARAMOUT aId)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aId)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /* mId is constant during life time, no need to lock */
fa9e4066f08beec538e775443c5be79dd423fcabahrens mId.cloneTo (aId);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(VendorId)(USHORT *aVendorId)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aVendorId)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens *aVendorId = mUsb->idVendor;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(ProductId)(USHORT *aProductId)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aProductId)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens *aProductId = mUsb->idProduct;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Revision)(USHORT *aRevision)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aRevision)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens *aRevision = mUsb->bcdDevice;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Manufacturer)(BSTR *aManufacturer)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aManufacturer)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens Bstr (mUsb->pszManufacturer).cloneTo (aManufacturer);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Product)(BSTR *aProduct)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aProduct)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin AutoReaderLock alock (this);
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin Bstr (mUsb->pszProduct).cloneTo (aProduct);
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin return S_OK;
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin}
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrinSTDMETHODIMP HostUSBDevice::COMGETTER(SerialNumber)(BSTR *aSerialNumber)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin if (!aSerialNumber)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens Bstr (mUsb->pszSerialNumber).cloneTo (aSerialNumber);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Address)(BSTR *aAddress)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aAddress)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin AutoReaderLock alock (this);
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin Bstr (mUsb->pszAddress).cloneTo (aAddress);
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin return S_OK;
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin}
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Port)(USHORT *aPort)
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aPort)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens ///@todo implement
fa9e4066f08beec538e775443c5be79dd423fcabahrens aPort = 0;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Version)(USHORT *aVersion)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aVersion)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens *aVersion = mUsb->bcdUSB >> 8;
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin return S_OK;
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin}
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrinSTDMETHODIMP HostUSBDevice::COMGETTER(PortVersion)(USHORT *aPortVersion)
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin{
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin if (!aPortVersion)
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens /** @todo implement this correctly or things just won't work right, see the vista defect. */
fa9e4066f08beec538e775443c5be79dd423fcabahrens *aPortVersion = mUsb->bcdUSB >> 8;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrensSTDMETHODIMP HostUSBDevice::COMGETTER(Remote)(BOOL *aRemote)
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (!aRemote)
fa9e4066f08beec538e775443c5be79dd423fcabahrens return E_INVALIDARG;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens CheckComRCReturnRC (autoCaller.rc());
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens *aRemote = FALSE;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens// IHostUSBDevice properties
fa9e4066f08beec538e775443c5be79dd423fcabahrens/////////////////////////////////////////////////////////////////////////////
fa9e4066f08beec538e775443c5be79dd423fcabahrens
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrinSTDMETHODIMP HostUSBDevice::COMGETTER(State) (USBDeviceState_T *aState)
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin{
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin if (!aState)
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin return E_POINTER;
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin AutoCaller autoCaller (this);
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin CheckComRCReturnRC (autoCaller.rc());
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
b19a79ec1a527828a60c4d325ccd8dcbeb2b2e8bperrin
fa9e4066f08beec538e775443c5be79dd423fcabahrens *aState = mState;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens return S_OK;
fa9e4066f08beec538e775443c5be79dd423fcabahrens}
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens// public methods only for internal purposes
fa9e4066f08beec538e775443c5be79dd423fcabahrens////////////////////////////////////////////////////////////////////////////////
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens/**
fa9e4066f08beec538e775443c5be79dd423fcabahrens * @note Locks this object for reading.
fa9e4066f08beec538e775443c5be79dd423fcabahrens */
fa9e4066f08beec538e775443c5be79dd423fcabahrensUtf8Str HostUSBDevice::name()
fa9e4066f08beec538e775443c5be79dd423fcabahrens{
fa9e4066f08beec538e775443c5be79dd423fcabahrens Utf8Str name;
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoCaller autoCaller (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens AssertComRCReturn (autoCaller.rc(), name);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens AutoReaderLock alock (this);
fa9e4066f08beec538e775443c5be79dd423fcabahrens
fa9e4066f08beec538e775443c5be79dd423fcabahrens bool haveManufacturer = mUsb->pszManufacturer && *mUsb->pszManufacturer;
fa9e4066f08beec538e775443c5be79dd423fcabahrens bool haveProduct = mUsb->pszProduct && *mUsb->pszProduct;
fa9e4066f08beec538e775443c5be79dd423fcabahrens if (haveManufacturer && haveProduct)
fa9e4066f08beec538e775443c5be79dd423fcabahrens name = Utf8StrFmt ("%s %s", mUsb->pszManufacturer,
fa9e4066f08beec538e775443c5be79dd423fcabahrens mUsb->pszProduct);
fa9e4066f08beec538e775443c5be79dd423fcabahrens else if (haveManufacturer)
fa9e4066f08beec538e775443c5be79dd423fcabahrens name = Utf8StrFmt ("%s", mUsb->pszManufacturer);
fa9e4066f08beec538e775443c5be79dd423fcabahrens else if (haveProduct)
fa9e4066f08beec538e775443c5be79dd423fcabahrens name = Utf8StrFmt ("%s", mUsb->pszProduct);
fa9e4066f08beec538e775443c5be79dd423fcabahrens else
name = "<unknown>";
return name;
}
/**
* Requests the USB proxy service to capture the device and sets the pending
* state to Captured.
*
* If the state change may be performed immediately (for example, Hold ->
* Captured), then the machine is informed before this method returns.
*
* @param aMachine Machine that will capture this device on success.
* @param aMaskedIfs The interfaces to hide from the guest.
* @return @c false if the device could be immediately captured
* but the VM process refused to grab it;
* @c true otherwise.
*
* @note Must be called from under the object write lock.
*
* @note May lock the given machine object for reading.
*/
bool HostUSBDevice::requestCapture (SessionMachine *aMachine, ULONG aMaskedIfs /* = 0*/)
{
LogFlowThisFunc (("\n"));
AssertReturn (aMachine, false);
AssertReturn (isLockedOnCurrentThread(), false);
AssertReturn (mIsStatePending == false, false);
AssertReturn (
mState == USBDeviceState_USBDeviceBusy ||
mState == USBDeviceState_USBDeviceAvailable ||
mState == USBDeviceState_USBDeviceHeld,
false);
if (mState == USBDeviceState_USBDeviceHeld)
{
/* can perform immediate capture, inform the VM process */
ComPtr <IUSBDevice> d = this;
mIsStatePending = true;
mPendingSince = 0;
/* the VM process will query the object, so leave the lock */
AutoLock alock (this);
alock.leave();
LogFlowThisFunc (("Calling machine->onUSBDeviceAttach()...\n"));
HRESULT rc = aMachine->onUSBDeviceAttach (d, NULL, aMaskedIfs);
/* The VM process has a legal reason to fail (for example, incorrect
* usbfs permissions or out of virtual USB ports). More over, the VM
* process might have been accidentially crashed and not accessible
* any more (so that calling an uninitialized SessionMachine returns a
* failure). So don't assert. */
LogFlowThisFunc (("Done machine->onUSBDeviceAttach()=%08X\n", rc));
alock.enter();
mIsStatePending = false;
mPendingStateEx = kNothingPending;
if (SUCCEEDED (rc))
{
mState = mPendingState = USBDeviceState_USBDeviceCaptured;
mMachine = aMachine;
return true;
}
return false;
}
mIsStatePending = true;
mPendingState = USBDeviceState_USBDeviceCaptured;
mPendingStateEx = kNothingPending;
mPendingSince = RTTimeNanoTS();
mMachine = aMachine;
mMaskedIfs = aMaskedIfs;
mUSBProxyService->captureDevice (this);
return true;
}
/**
* Requests the USB proxy service to release the device and sets the pending
* state to Available.
*
* If the state change may be performed immediately (for example, the current
* state is Busy), this method does nothing.
*
* @note Must be called from under the object write lock.
*/
void HostUSBDevice::requestRelease()
{
LogFlowThisFunc (("\n"));
AssertReturnVoid (isLockedOnCurrentThread());
AssertReturnVoid (mIsStatePending == false);
AssertReturnVoid (
mState == USBDeviceState_USBDeviceBusy ||
mState == USBDeviceState_USBDeviceAvailable ||
mState == USBDeviceState_USBDeviceHeld);
if (mState != USBDeviceState_USBDeviceHeld)
return;
mIsStatePending = true;
mPendingState = USBDeviceState_USBDeviceAvailable;
mPendingStateEx = kNothingPending;
mPendingSince = RTTimeNanoTS();
mUSBProxyService->releaseDevice (this);
}
/**
* Requests the USB proxy service to release the device, sets the pending
* state to Held and removes the machine association if any.
*
* If the state change may be performed immediately (for example, the current
* state is already Held), this method does nothing but removes the machine
* association.
*
* @note Must be called from under the object write lock.
*/
void HostUSBDevice::requestHold()
{
LogFlowThisFunc (("\n"));
AssertReturnVoid (isLockedOnCurrentThread());
AssertReturnVoid (mIsStatePending == false);
AssertReturnVoid (
mState == USBDeviceState_USBDeviceBusy ||
mState == USBDeviceState_USBDeviceAvailable ||
mState == USBDeviceState_USBDeviceHeld);
mMachine.setNull();
if (mState == USBDeviceState_USBDeviceHeld)
return;
mIsStatePending = true;
mPendingState = USBDeviceState_USBDeviceHeld;
mPendingStateEx = kNothingPending;
mPendingSince = RTTimeNanoTS();
mUSBProxyService->captureDevice (this);
}
/**
* Sets the device state from Captured to Held and resets the machine
* association (if any). Usually called before applying filters.
*
* @note Must be called from under the object write lock.
*/
void HostUSBDevice::setHeld()
{
LogFlowThisFunc (("\n"));
AssertReturnVoid (isLockedOnCurrentThread());
AssertReturnVoid (mState == USBDeviceState_USBDeviceCaptured);
AssertReturnVoid (mPendingState == USBDeviceState_USBDeviceCaptured);
AssertReturnVoid (mIsStatePending == false);
mState = USBDeviceState_USBDeviceHeld;
mMachine.setNull();
}
/**
* Resets all device data and informs the machine (if any) about the
* detachment. Must be called when this device is physically detached from
* the host.
*
* @note Must be called from under the object write lock.
*/
void HostUSBDevice::onDetachedPhys()
{
LogFlowThisFunc (("\n"));
AssertReturnVoid (isLockedOnCurrentThread());
if (!mMachine.isNull() && mState == USBDeviceState_USBDeviceCaptured)
{
/* the device is captured by a machine, instruct it to release */
mIsStatePending = true;
mPendingSince = 0;
/* the VM process will query the object, so leave the lock */
AutoLock alock (this);
alock.leave();
LogFlowThisFunc (("Calling machine->onUSBDeviceDetach()...\n"));
HRESULT rc = mMachine->onUSBDeviceDetach (mId, NULL);
NOREF(rc);
/* This call may expectedly fail with rc = NS_ERROR_FAILURE (on XPCOM)
* if the VM process requests device release right before termination
* and then terminates before onUSBDeviceDetach() reached
* it. Therefore, we don't assert here. On MS COM, there should be
* something similar (with the different error code). More over, the
* VM process might have been accidentially crashed and not accessible
* any more (so that calling an uninitialized SessionMachine returns a
* failure). So don't assert. */
LogFlowThisFunc (("Done machine->onUSBDeviceDetach()=%08X\n", rc));
alock.enter();
/* Reset all fields. The object should have been
* uninitialized after this method returns, so it doesn't really
* matter what state we put it in. */
mIsStatePending = false;
mState = mPendingState = USBDeviceState_USBDeviceNotSupported;
mPendingStateEx = kNothingPending;
mMachine.setNull();
}
}
/**
* Handles the finished pending state change and informs the VM process if
* necessary.
*
* @note Must be called from under the object write lock.
*/
void HostUSBDevice::handlePendingStateChange()
{
LogFlowThisFunc (("\n"));
AssertReturnVoid (isLockedOnCurrentThread());
AssertReturnVoid (mIsStatePending == true);
AssertReturnVoid (mState != USBDeviceState_USBDeviceCaptured || mPendingStateEx != kNothingPending);
bool wasCapture = false;
HRESULT requestRC = S_OK;
Bstr errorText;
switch (mPendingStateEx)
{
case kNothingPending:
switch (mPendingState)
{
case USBDeviceState_USBDeviceCaptured:
{
if (mState == USBDeviceState_USBDeviceHeld)
{
if (!mMachine.isNull())
wasCapture = true;
else
{
/* it is a canceled capture request. Give the device back
* to the host. */
mPendingState = USBDeviceState_USBDeviceAvailable;
mUSBProxyService->releaseDevice (this);
}
}
else
{
/* couldn't capture the device, will report an error */
wasCapture = true;
Assert (!mMachine.isNull());
/// @todo more detailed error message depending on the state?
// probably need some error code/string from the USB proxy itself
requestRC = E_FAIL;
errorText = Utf8StrFmt (
tr ("USB device '%s' with UUID {%Vuuid} is being accessed by the host "
"computer and cannot be attached to the virtual machine."
"Please try later"),
name().raw(), id().raw());
}
break;
}
case USBDeviceState_USBDeviceAvailable:
{
Assert (mMachine.isNull());
if (mState == USBDeviceState_USBDeviceHeld)
{
/* couldn't release the device (give it back to the host).
* there is nobody to report an error to (the machine has
* already been deassociated because VMM has already detached
* the device before requesting a release). */
}
else
{
/* it is a canceled release request. Leave at the host */
/// @todo we may want to re-run all filters in this case
}
break;
}
case USBDeviceState_USBDeviceHeld:
{
if (mState == USBDeviceState_USBDeviceHeld)
{
/* All right, the device is now held (due to some global
* filter). */
break;
}
else
{
/* couldn't capture the device requested by the global
* filter, there is nobody to report an error to. */
}
break;
}
default:
AssertFailed();
}
break;
/*
* The device has reappeared, the caller (Host) will (maybe) reapply filters,
* since we don't quite know we set the machine to NULL.
*/
case kDetachingPendingAttachFilters:
mMachine.setNull();
break;
/*
* The device has reappeared while the detach operation is still in
* progress, just clear the pending operation and leave the machine as is.
*/
case kDetachingPendingAttach:
break;
case kDetachingPendingDetach:
case kDetachingPendingDetachFilters:
default:
AssertMsgFailed(("%d\n", mPendingStateEx));
return;
}
ComObjPtr <VirtualBoxErrorInfo> error;
if (FAILED (requestRC))
{
LogFlowThisFunc (("Request failed, requestRC=%08X, text='%ls'\n",
requestRC, errorText.raw()));
error.createObject();
error->init (requestRC, COM_IIDOF (IHostUSBDevice),
Bstr (HostUSBDevice::getComponentName()),
errorText.raw());
}
if (wasCapture)
{
/* inform the VM process */
ComPtr <IUSBDevice> d = this;
/* the VM process will query the object, so leave the lock */
AutoLock alock (this);
alock.leave();
LogFlowThisFunc (("Calling machine->onUSBDeviceAttach()...\n"));
HRESULT rc = mMachine->onUSBDeviceAttach (d, error, mMaskedIfs);
/* The VM process has a legal reason to fail (for example, incorrect
* usbfs permissions or out of virtual USB ports). More over, the VM
* process might have been accidentially crashed and not accessible
* any more (so that calling an uninitialized SessionMachine returns a
* failure). So don't assert. */
/// @todo we will probably want to re-run all filters on failure
LogFlowThisFunc (("Done machine->onUSBDeviceAttach()=%08X\n", rc));
alock.enter();
if (SUCCEEDED (requestRC) && SUCCEEDED (rc))
{
mIsStatePending = false;
mState = mPendingState = USBDeviceState_USBDeviceCaptured;
mPendingStateEx = kNothingPending;
return;
}
/* on failure, either from the proxy or from the VM process,
* deassociate from the machine */
mMachine.setNull();
}
mIsStatePending = false;
mPendingState = mState;
mPendingStateEx = kNothingPending;
}
/**
* Cancels pending state change due to machine termination or timeout.
*
* @note Must be called from under the object write lock.
* @param aTimeout Whether this is a timeout or not.
*/
void HostUSBDevice::cancelPendingState(bool aTimeout /*= false*/)
{
LogFlowThisFunc (("\n"));
AssertReturnVoid (isLockedOnCurrentThread());
AssertReturnVoid (mIsStatePending == true);
AssertReturnVoid (aTimeout || !mMachine.isNull());
switch (mPendingStateEx)
{
case kNothingPending:
switch (mPendingState)
{
case USBDeviceState_USBDeviceCaptured:
/* reset mMachine to deassociate it from the filter and tell
* handlePendingStateChange() what to do */
mMachine.setNull();
if (!aTimeout)
break;
case USBDeviceState_USBDeviceAvailable:
case USBDeviceState_USBDeviceHeld:
if (aTimeout)
{
mPendingStateEx = kNothingPending;
mIsStatePending = false;
break;
}
/* fall thru */
default:
AssertFailed();
}
break;
case kDetachingPendingDetach:
case kDetachingPendingDetachFilters:
case kDetachingPendingAttach:
case kDetachingPendingAttachFilters:
LogFlowThisFunc (("cancelling reattach in state %d\n", mPendingStateEx));
mMachine.setNull();
mPendingStateEx = kNothingPending;
mIsStatePending = false;
break;
default:
AssertMsgFailed(("%d\n", mPendingStateEx));
break;
}
}
/**
* 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.
*/
bool HostUSBDevice::isMatch (const USBDeviceFilter::Data &aData)
{
AutoCaller autoCaller (this);
AssertComRCReturn (autoCaller.rc(), false);
AutoReaderLock alock (this);
if (!aData.mActive)
return false;
#ifndef VBOX_WITH_USBFILTER
if (!aData.mVendorId.isMatch (mUsb->idVendor))
{
LogFlowThisFunc (("vendor not match %04X\n",
mUsb->idVendor));
return false;
}
if (!aData.mProductId.isMatch (mUsb->idProduct))
{
LogFlowThisFunc (("product id not match %04X\n",
mUsb->idProduct));
return false;
}
if (!aData.mRevision.isMatch (mUsb->bcdDevice))
{
LogFlowThisFunc (("rev not match %04X\n",
mUsb->bcdDevice));
return false;
}
#if !defined (RT_OS_WINDOWS)
// these filters are temporarily ignored on Win32
if (!aData.mManufacturer.isMatch (Bstr (mUsb->pszManufacturer)))
return false;
if (!aData.mProduct.isMatch (Bstr (mUsb->pszProduct)))
return false;
if (!aData.mSerialNumber.isMatch (Bstr (mUsb->pszSerialNumber)))
return false;
/// @todo (dmik) pusPort is yet absent
// if (!aData.mPort.isMatch (Bstr (mUsb->pusPort)))
// return false;
#endif
// Host USB devices are local, so remote is always FALSE
if (!aData.mRemote.isMatch (FALSE))
{
LogFlowMember (("Host::HostUSBDevice: remote not match FALSE\n"));
return false;
}
/// @todo (dmik): bird, I assumed isMatch() is called only for devices
// that are suitable for holding/capturing (also assuming that when the device
// is just attached it first goes to our filter driver, and only after applying
// filters goes back to the system when appropriate). So the below
// doesn't look too correct; moreover, currently there is no determinable
// "any match" state for intervalic filters, and it will be not so easy
// to determine this state for an arbitrary regexp expression...
// For now, I just check that the string filter is empty (which doesn't
// actually reflect all possible "any match" filters).
//
// bird: This method was called for any device some weeks back, and it most certainly
// should be called for 'busy' devices still. However, we do *not* want 'busy' devices
// to match empty filters (because that will for instance capture all USB keyboards & mice).
// You assumption about a filter driver is not correct on linux. We're racing with
// everyone else in the system there - see your problem with usbfs access.
//
// The customer *requires* a way of matching all devices which the host isn't using,
// if that is now difficult or the below method opens holes in the matching, this *must*
// be addresses immediately.
/*
* If all the criteria is empty, devices which are used by the host will not match.
*/
if ( mUsb->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
&& aData.mVendorId.string().isEmpty()
&& aData.mProductId.string().isEmpty()
&& aData.mRevision.string().isEmpty()
&& aData.mManufacturer.string().isEmpty()
&& aData.mProduct.string().isEmpty()
&& aData.mSerialNumber.string().isEmpty())
return false;
#else /* VBOX_WITH_USBFILTER */
if (USBFilterMatchDevice (&aData.mUSBFilter, mUsb))
{
/* Don't match busy devices with a 100% wildcard filter - this will
later become a filter prop (ring-3 only). */
if ( mUsb->enmState == USBDEVICESTATE_USED_BY_HOST_CAPTURABLE
&& !USBFilterHasAnySubstatialCriteria (&aData.mUSBFilter))
return false;
}
#endif /* VBOX_WITH_USBFILTER */
LogFlowThisFunc (("returns true\n"));
return true;
}
/**
* Compares this device with a USBDEVICE and decides which comes first.
*
* If the device has a pending state request, a non-strict comparison is made
* (to properly detect a re-attached device). Otherwise, a strict comparison
* is performed.
*
* @param aDev2 Device 2.
*
* @return < 0 if this should come before aDev2.
* @return 0 if this and aDev2 are equal.
* @return > 0 if this should come after aDev2.
*
* @note Must be called from under the object write lock.
*/
int HostUSBDevice::compare (PCUSBDEVICE aDev2)
{
AssertReturn (isLockedOnCurrentThread(), -1);
return compare (mUsb, aDev2, !isStatePending());
}
/**
* Compares two USB devices and decides which comes first.
*
* If @a aIsStrict is @c true then the comparison will indicate a difference
* even if the same physical device (represented by @a aDev1) has been just
* re-attached to the host computer (represented by @a aDev2) and got a
* different address from the host OS, etc.
*
* If @a aIsStrict is @c false, then such a re-attached device will be
* considered equal to the previous device definition and this function will
* retrun 0.
*
* @param aDev1 Device 1.
* @param aDev2 Device 2.
* @param aIsStrict @c true to do a strict check and @c false otherwise.
* @return < 0 if aDev1 should come before aDev2.
* @return 0 if aDev1 and aDev2 are equal.
* @return > 0 if aDev1 should come after aDev2.
*/
/*static*/
int HostUSBDevice::compare (PCUSBDEVICE aDev1, PCUSBDEVICE aDev2,
bool aIsStrict /* = true */)
{
/* The non-strict checks tries as best as it can to distiguish between
different physical devices of the same product. Unfortunately this
isn't always possible and we might end up a bit confused in rare cases... */
int iDiff = aDev1->idVendor - aDev2->idVendor;
if (iDiff)
return iDiff;
iDiff = aDev1->idProduct - aDev2->idProduct;
if (iDiff)
return iDiff;
iDiff = aDev1->bcdDevice - aDev2->bcdDevice;
if (iDiff)
return iDiff;
if (aDev1->u64SerialHash != aDev2->u64SerialHash)
return aDev1->u64SerialHash < aDev2->u64SerialHash ? -1 : 1;
if (!aIsStrict)
return 0;
/* The rest is considered as a strict check since it includes bits that
may vary on logical reconnects (or whatever you wish to call it). */
return strcmp (aDev1->pszAddress, aDev2->pszAddress);
}
/**
* 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 aDev The current device state as seen by the proxy backend.
*
* @return Whether the Host object should be bothered with this state change.
*
* @note Locks this object for writing.
*/
bool HostUSBDevice::updateState (PCUSBDEVICE aDev)
{
LogFlowThisFunc (("\n"));
AssertReturn (isLockedOnCurrentThread(), false);
AutoCaller autoCaller (this);
AssertComRCReturn (autoCaller.rc(), false);
AutoLock alock (this);
/* Replace the existing structure by the new one */
if (mUsb != aDev)
{
aDev->pNext = mUsb->pNext;
aDev->pPrev = mUsb->pPrev;
USBProxyService::freeDevice (mUsb);
mUsb = aDev;
}
bool isImportant = false;
/*
* We have to be pretty conservative here because the proxy backend
* doesn't necessarily know everything that's going on. So, rather
* be overly careful than changing the state once when we shouldn't!
*
* In particular, we treat changing between three states Unavailable, Busy
* and Available as non-important (because they all mean that the device
* is owned by the host) and return false in this case. We may want to
* change it later and, e.g. re-run all USB filters when the device goes from
* from Busy to Available).
*
* 2007-07-04: State transitions from Unavailable to Busy or Available
* are now considered important and will cause filters to
* be rerun on the device. (See #2030 and #1870.)
*/
LogFlowThisFunc (("aDev->enmState=%d mState=%d mPendingState=%d mPendingStateEx=%d\n",
aDev->enmState, mState, mPendingState, mPendingStateEx));
switch (aDev->enmState)
{
default:
AssertMsgFailed (("aDev->enmState=%d\n", aDev->enmState));
case USBDEVICESTATE_UNSUPPORTED:
Assert (mState == USBDeviceState_USBDeviceNotSupported);
switch (mState)
{
case USBDeviceState_USBDeviceCaptured:
isImportant = mIsStatePending;
break;
}
return isImportant;
case USBDEVICESTATE_USED_BY_HOST:
switch (mState)
{
case USBDeviceState_USBDeviceUnavailable:
return false;
/* the following state changes don't require any action for now */
case USBDeviceState_USBDeviceBusy:
case USBDeviceState_USBDeviceAvailable:
isImportant = false;
break;
#ifndef RT_OS_WINDOWS /* Only windows really knows whether the device is unavailable or captured. */
case USBDeviceState_USBDeviceCaptured:
if (!mIsStatePending)
return false;
/* fall thru */
#endif
default:
isImportant = true;
}
LogFlowThisFunc (("%d -> %d\n",
mState, USBDeviceState_USBDeviceUnavailable));
mState = USBDeviceState_USBDeviceUnavailable;
return isImportant;
case USBDEVICESTATE_USED_BY_HOST_CAPTURABLE:
switch (mState)
{
case USBDeviceState_USBDeviceBusy:
return false;
case USBDeviceState_USBDeviceAvailable:
isImportant = false;
break;
case USBDeviceState_USBDeviceCaptured:
#ifndef RT_OS_WINDOWS /* Only Windows really knows whether the device is busy or captured. */
if (!mIsStatePending)
return false;
#endif
/* Remain in the captured state if it's an async detach. */
if (mPendingStateEx != kNothingPending)
{
LogFlowThisFunc (("USBDeviceCaptured - async detach completed (%d)\n", mPendingStateEx));
return true;
}
/* fall thru */
default:
/* USBDeviceState_USBDeviceUnavailable: The device has become capturable, re-run filters. */
/* USBDeviceState_USBDeviceHeld: Pending request. */
/* USBDeviceState_USBDeviceCaptured: Pending request. */
/* USBDeviceState_USBDeviceNotSupported: Something is broken. */
isImportant = true;
}
LogFlowThisFunc (("%d -> %d\n",
mState, USBDeviceState_USBDeviceBusy));
mState = USBDeviceState_USBDeviceBusy;
return isImportant;
case USBDEVICESTATE_UNUSED:
switch (mState)
{
case USBDeviceState_USBDeviceAvailable:
return false;
#if defined(RT_OS_LINUX) /* Hack for /proc/bus/usb/devices not necessarily putting up a driver. */ \
|| defined(RT_OS_DARWIN) /* We're a bit clueless as to the exact device state, just like linux. */
case USBDeviceState_USBDeviceCaptured:
if ( !mIsStatePending
|| mPendingStateEx != kNothingPending)
return false;
isImportant = true;
break;
#endif
/* the following state changes don't require any action for now */
case USBDeviceState_USBDeviceBusy:
isImportant = false;
break;
default:
/* USBDeviceState_USBDeviceUnavailable: The device has become available, re-run filters. */
/* USBDeviceState_USBDeviceHeld: Pending request. */
/* USBDeviceState_USBDeviceNotSupported: Something is broken. */
isImportant = true;
}
LogFlowThisFunc (("%d -> %d\n",
mState, USBDeviceState_USBDeviceAvailable));
mState = USBDeviceState_USBDeviceAvailable;
return isImportant;
case USBDEVICESTATE_HELD_BY_PROXY:
switch (mState)
{
case USBDeviceState_USBDeviceHeld:
return false;
case USBDeviceState_USBDeviceCaptured:
if (!mIsStatePending)
return false;
/* no break */
default:
LogFlowThisFunc (("%d -> %d\n",
mState, USBDeviceState_USBDeviceHeld));
mState = USBDeviceState_USBDeviceHeld;
return true;
}
break;
case USBDEVICESTATE_USED_BY_GUEST:
/** @todo USBDEVICESTATE_USED_BY_GUEST seems not to be used
* anywhere in the proxy code; it's quite logical because the
* proxy doesn't know anything about guest VMs. */
AssertFailed();
#if 0
switch (mState)
{
case USBDeviceState_USBDeviceCaptured:
/* the proxy may confuse following state(s) with captured */
case USBDeviceState_USBDeviceHeld:
case USBDeviceState_USBDeviceAvailable:
case USBDeviceState_USBDeviceBusy:
return false;
default:
LogFlowThisFunc (("%d -> %d\n",
mState, USBDeviceState_USBDeviceHeld));
mState = USBDeviceState_USBDeviceHeld;
return true;
}
#endif
break;
}
return false;
}
/**
* Checks for timeout of any pending async operation.
*
* The caller must write lock the object prior to calling
* this method.
*/
void HostUSBDevice::checkForAsyncTimeout()
{
AssertReturnVoid (isLockedOnCurrentThread());
#ifndef RT_OS_WINDOWS /* no timeouts on windows yet since I don't have all the details here... */
if (isStatePending() && mPendingSince)
{
uint64_t elapsedNanoseconds = RTTimeNanoTS() - mPendingSince;
if (elapsedNanoseconds > UINT64_C (60000000000) ) /* 60 seconds */
{
LogRel (("USB: Async operation timed out; mPendingState=%d mPendingStateEx=%d idVendor=%04x (%s) idProduct=%04x (%s) bcdDevice=%04x\n",
mPendingState, mPendingStateEx, mUsb->idVendor, mUsb->pszManufacturer, mUsb->idProduct, mUsb->pszProduct, mUsb->bcdDevice));
cancelPendingState (true);
}
}
#endif
}
/**
* This method is called by the USB proxy and Host to work the
* logical reconnection operation.
*
* @param aStage kDeatchingPendingDetach, kDeatchingPendingDetachFilters,
* kDetachingPendingAttach or kDetachingPendingAttachFilters.
*
* @returns Success indicator.
*/
bool HostUSBDevice::setLogicalReconnect (InternalState aStage)
{
AssertReturn (isLockedOnCurrentThread(), false);
switch (aStage)
{
case kDetachingPendingDetach:
AssertReturn (!mIsStatePending, false);
mPendingState = mState;
mIsStatePending = true;
mPendingSince = RTTimeNanoTS();
LogFlowThisFunc (("pending detach\n"));
break;
case kDetachingPendingDetachFilters:
AssertReturn (mIsStatePending, false);
AssertReturn (mPendingStateEx == kDetachingPendingDetach, false);
LogFlowThisFunc (("pending detach+filters\n"));
break;
case kDetachingPendingAttach:
AssertReturn (mIsStatePending, false);
AssertReturn (mPendingStateEx == kDetachingPendingDetach, false);
LogFlowThisFunc (("pending attach\n"));
break;
case kDetachingPendingAttachFilters:
AssertReturn (mIsStatePending, false);
AssertReturn ( mPendingStateEx == kDetachingPendingAttach
|| mPendingStateEx == kDetachingPendingDetachFilters, false);
LogFlowThisFunc (("pending attach+filters\n"));
break;
default:
AssertFailedReturn (false);
}
mPendingStateEx = aStage;
return true;
}