/* $Id: USBProxyServiceDarwin.cpp 31892 2010-08-24 08:00:51Z vboxsync $ */ /** @file * VirtualBox USB Proxy Service (in VBoxSVC), Darwin Specialization. */ /* * Copyright (C) 2006-2010 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. */ /******************************************************************************* * Header Files * *******************************************************************************/ #include "USBProxyService.h" #include "Logging.h" #include "iokit.h" #include #include #include #include #include #include #include #include #include /** * Initialize data members. */ USBProxyServiceDarwin::USBProxyServiceDarwin (Host *aHost) : USBProxyService (aHost), mServiceRunLoopRef (NULL), mNotifyOpaque (NULL), mWaitABitNextTime (false), mUSBLibInitialized (false) { LogFlowThisFunc(("aHost=%p\n", aHost)); } /** * Initializes the object (called right after construction). * * @returns S_OK on success and non-fatal failures, some COM error otherwise. */ HRESULT USBProxyServiceDarwin::init(void) { /* * Call the superclass method first. */ HRESULT hrc = USBProxyService::init(); AssertComRCReturn(hrc, hrc); #ifdef VBOX_WITH_NEW_USB_CODE_ON_DARWIN /* * Initialize the USB library. */ int rc = USBLibInit(); if (RT_FAILURE(rc)) { mLastError = rc; return S_OK; } mUSBLibInitialized = true; #endif /* * Start the poller thread. */ start(); return S_OK; } /** * Stop all service threads and free the device chain. */ USBProxyServiceDarwin::~USBProxyServiceDarwin() { LogFlowThisFunc(("\n")); /* * Stop the service. */ if (isActive()) stop(); #ifdef VBOX_WITH_NEW_USB_CODE_ON_DARWIN /* * Terminate the USB library - it'll */ if (mUSBLibInitialized) { USBLibTerm(); mUSBLibInitialized = false; } #endif } #ifdef VBOX_WITH_NEW_USB_CODE_ON_DARWIN void *USBProxyServiceDarwin::insertFilter (PCUSBFILTER aFilter) { return USBLibAddFilter (aFilter); } void USBProxyServiceDarwin::removeFilter (void *aId) { USBLibRemoveFilter (aId); } #endif /* VBOX_WITH_NEW_USB_CODE_ON_DARWIN */ int USBProxyServiceDarwin::captureDevice(HostUSBDevice *aDevice) { /* * Check preconditions. */ AssertReturn(aDevice, VERR_GENERAL_FAILURE); LogFlowThisFunc(("aDevice=%s\n", aDevice->getName().c_str())); AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); Assert(aDevice->getUnistate() == kHostUSBDeviceState_Capturing); #ifndef VBOX_WITH_NEW_USB_CODE_ON_DARWIN /* * Fake it. */ ASMAtomicWriteBool(&mFakeAsync, true); interruptWait(); return VINF_SUCCESS; #else /* * Create a one-shot capture filter for the device (don't * match on port) and trigger a re-enumeration of it. */ USBFILTER Filter; USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_CAPTURE); initFilterFromDevice(&Filter, aDevice); void *pvId = USBLibAddFilter(&Filter); if (!pvId) return VERR_GENERAL_FAILURE; int rc = DarwinReEnumerateUSBDevice(aDevice->mUsb); if (RT_SUCCESS(rc)) aDevice->mOneShotId = pvId; else { USBLibRemoveFilter(pvId); pvId = NULL; } LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId)); return rc; #endif } void USBProxyServiceDarwin::captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess) { #ifdef VBOX_WITH_NEW_USB_CODE_ON_DARWIN /* * Remove the one-shot filter if necessary. */ LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->getName().c_str(), aSuccess, aDevice->mOneShotId)); if (!aSuccess && aDevice->mOneShotId) USBLibRemoveFilter(aDevice->mOneShotId); aDevice->mOneShotId = NULL; #endif } int USBProxyServiceDarwin::releaseDevice(HostUSBDevice *aDevice) { /* * Check preconditions. */ AssertReturn(aDevice, VERR_GENERAL_FAILURE); LogFlowThisFunc(("aDevice=%s\n", aDevice->getName().c_str())); AssertReturn(aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE); Assert(aDevice->getUnistate() == kHostUSBDeviceState_ReleasingToHost); #ifndef VBOX_WITH_NEW_USB_CODE_ON_DARWIN /* * Fake it. */ ASMAtomicWriteBool(&mFakeAsync, true); interruptWait(); return VINF_SUCCESS; #else /* * Create a one-shot ignore filter for the device * and trigger a re-enumeration of it. */ USBFILTER Filter; USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE); initFilterFromDevice(&Filter, aDevice); Log(("USBFILTERIDX_PORT=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_PORT))); Log(("USBFILTERIDX_BUS=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_BUS))); void *pvId = USBLibAddFilter(&Filter); if (!pvId) return VERR_GENERAL_FAILURE; int rc = DarwinReEnumerateUSBDevice(aDevice->mUsb); if (RT_SUCCESS(rc)) aDevice->mOneShotId = pvId; else { USBLibRemoveFilter(pvId); pvId = NULL; } LogFlowThisFunc(("returns %Rrc pvId=%p\n", rc, pvId)); return rc; #endif } void USBProxyServiceDarwin::releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess) { #ifdef VBOX_WITH_NEW_USB_CODE_ON_DARWIN /* * Remove the one-shot filter if necessary. */ LogFlowThisFunc(("aDevice=%s aSuccess=%RTbool mOneShotId=%p\n", aDevice->getName().c_str(), aSuccess, aDevice->mOneShotId)); if (!aSuccess && aDevice->mOneShotId) USBLibRemoveFilter(aDevice->mOneShotId); aDevice->mOneShotId = NULL; #endif } void USBProxyServiceDarwin::detachingDevice(HostUSBDevice *aDevice) { #ifndef VBOX_WITH_NEW_USB_CODE_ON_DARWIN aDevice->setLogicalReconnect (HostUSBDevice::kDetachingPendingDetach); #else NOREF(aDevice); #endif } bool USBProxyServiceDarwin::updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine) { #ifndef VBOX_WITH_NEW_USB_CODE_ON_DARWIN /* We're faking async state stuff. */ return updateDeviceStateFake(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine); #else /* Nothing special here so far, so fall back on parent */ return USBProxyService::updateDeviceState(aDevice, aUSBDevice, aRunFilters, aIgnoreMachine); #endif } int USBProxyServiceDarwin::wait(RTMSINTERVAL aMillies) { #ifndef VBOX_WITH_NEW_USB_CODE_ON_DARWIN if ( mFakeAsync && ASMAtomicXchgBool(&mFakeAsync, false)) return VINF_SUCCESS; #endif SInt32 rc = CFRunLoopRunInMode(CFSTR (VBOX_IOKIT_MODE_STRING), mWaitABitNextTime && aMillies >= 1000 ? 1.0 /* seconds */ : aMillies >= 5000 /* Temporary measure to poll for status changes (MSD). */ ? 5.0 /* seconds */ : aMillies / 1000.0, true); mWaitABitNextTime = rc != kCFRunLoopRunTimedOut; return VINF_SUCCESS; } int USBProxyServiceDarwin::interruptWait (void) { if (mServiceRunLoopRef) CFRunLoopStop (mServiceRunLoopRef); return 0; } PUSBDEVICE USBProxyServiceDarwin::getDevices (void) { /* call iokit.cpp */ return DarwinGetUSBDevices(); } void USBProxyServiceDarwin::serviceThreadInit (void) { mServiceRunLoopRef = CFRunLoopGetCurrent(); mNotifyOpaque = DarwinSubscribeUSBNotifications(); } void USBProxyServiceDarwin::serviceThreadTerm (void) { DarwinUnsubscribeUSBNotifications (mNotifyOpaque); mServiceRunLoopRef = NULL; } /** * Wrapper called from iokit.cpp. * * @param pCur The USB device to free. */ void DarwinFreeUSBDeviceFromIOKit (PUSBDEVICE pCur) { USBProxyService::freeDevice (pCur); }