summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostDrivers/VBoxUSB/darwin/VBoxUSB.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/HostDrivers/VBoxUSB/darwin/VBoxUSB.cpp
parentInitial commit. (diff)
downloadvirtualbox-upstream.tar.xz
virtualbox-upstream.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/HostDrivers/VBoxUSB/darwin/VBoxUSB.cpp')
-rw-r--r--src/VBox/HostDrivers/VBoxUSB/darwin/VBoxUSB.cpp1890
1 files changed, 1890 insertions, 0 deletions
diff --git a/src/VBox/HostDrivers/VBoxUSB/darwin/VBoxUSB.cpp b/src/VBox/HostDrivers/VBoxUSB/darwin/VBoxUSB.cpp
new file mode 100644
index 00000000..4b7868f1
--- /dev/null
+++ b/src/VBox/HostDrivers/VBoxUSB/darwin/VBoxUSB.cpp
@@ -0,0 +1,1890 @@
+/* $Id: VBoxUSB.cpp $ */
+/** @file
+ * VirtualBox USB driver for Darwin.
+ *
+ * This driver is responsible for hijacking USB devices when any of the
+ * VBoxSVC daemons requests it. It is also responsible for arbitrating
+ * access to hijacked USB devices.
+ */
+
+/*
+ * Copyright (C) 2006-2019 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.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_USB_DRV
+/* Deal with conflicts first.
+ * (This is mess inherited from BSD. The *BSDs has clean this up long ago.) */
+#include <sys/param.h>
+#undef PVM
+#include <IOKit/IOLib.h> /* Assert as function */
+
+#include "VBoxUSBInterface.h"
+#include "VBoxUSBFilterMgr.h"
+#include <VBox/version.h>
+#include <VBox/usblib-darwin.h>
+#include <VBox/log.h>
+#include <iprt/types.h>
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/semaphore.h>
+#include <iprt/process.h>
+#include <iprt/alloc.h>
+#include <iprt/errcore.h>
+#include <iprt/asm.h>
+
+#include <mach/kmod.h>
+#include <miscfs/devfs/devfs.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/ioccom.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <kern/task.h>
+#include <IOKit/IOService.h>
+#include <IOKit/IOUserClient.h>
+#include <IOKit/IOKitKeys.h>
+#include <IOKit/usb/USB.h>
+#include <IOKit/usb/IOUSBDevice.h>
+#include <IOKit/usb/IOUSBInterface.h>
+#include <IOKit/usb/IOUSBUserClient.h>
+
+/* private: */
+RT_C_DECLS_BEGIN
+extern void *get_bsdtask_info(task_t);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Locks the lists. */
+#define VBOXUSB_LOCK() do { int rc = RTSemFastMutexRequest(g_Mtx); AssertRC(rc); } while (0)
+/** Unlocks the lists. */
+#define VBOXUSB_UNLOCK() do { int rc = RTSemFastMutexRelease(g_Mtx); AssertRC(rc); } while (0)
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+RT_C_DECLS_BEGIN
+static kern_return_t VBoxUSBStart(struct kmod_info *pKModInfo, void *pvData);
+static kern_return_t VBoxUSBStop(struct kmod_info *pKModInfo, void *pvData);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The service class.
+ *
+ * This is the management service that VBoxSVC and the VMs speak to.
+ *
+ * @remark The method prototypes are ordered somewhat after their order of
+ * invocation, while the implementation is ordered by pair.
+ */
+class org_virtualbox_VBoxUSB : public IOService
+{
+ OSDeclareDefaultStructors(org_virtualbox_VBoxUSB);
+
+public:
+ RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT();
+
+ /** @name IOService
+ * @{ */
+ virtual bool init(OSDictionary *pDictionary = 0);
+ virtual bool start(IOService *pProvider);
+ virtual bool open(IOService *pForClient, IOOptionBits fOptions = 0, void *pvArg = 0);
+ virtual bool terminate(IOOptionBits fOptions);
+ virtual void close(IOService *pForClient, IOOptionBits fOptions = 0);
+ virtual void stop(IOService *pProvider);
+ virtual void free();
+ /** @} */
+
+private:
+ /** Guard against the parent class growing and us using outdated headers. */
+ uint8_t m_abSafetyPadding[256];
+};
+OSDefineMetaClassAndStructors(org_virtualbox_VBoxUSB, IOService);
+
+
+/**
+ * The user client class that pairs up with org_virtualbox_VBoxUSB.
+ */
+class org_virtualbox_VBoxUSBClient : public IOUserClient
+{
+ OSDeclareDefaultStructors(org_virtualbox_VBoxUSBClient);
+
+public:
+ RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT();
+
+ /** @name IOService & IOUserClient
+ * @{ */
+ virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type);
+ virtual bool start(IOService *pProvider);
+ virtual IOReturn clientClose(void);
+ virtual IOReturn clientDied(void);
+ virtual bool terminate(IOOptionBits fOptions = 0);
+ virtual bool finalize(IOOptionBits fOptions);
+ virtual void stop(IOService *pProvider);
+ virtual void free();
+ virtual IOExternalMethod *getTargetAndMethodForIndex(IOService **ppService, UInt32 iMethod);
+ /** @} */
+
+ /** @name User client methods
+ * @{ */
+ IOReturn addFilter(PUSBFILTER pFilter, PVBOXUSBADDFILTEROUT pOut, IOByteCount cbFilter, IOByteCount *pcbOut);
+ IOReturn removeFilter(uintptr_t *puId, int *prc, IOByteCount cbIn, IOByteCount *pcbOut);
+ /** @} */
+
+ static bool isClientTask(task_t ClientTask);
+
+private:
+ /** Guard against the parent class growing and us using outdated headers. */
+ uint8_t m_abSafetyPadding[256];
+ /** The service provider. */
+ org_virtualbox_VBoxUSB *m_pProvider;
+ /** The client task. */
+ task_t m_Task;
+ /** The client process. */
+ RTPROCESS m_Process;
+ /** Pointer to the next user client. */
+ org_virtualbox_VBoxUSBClient * volatile m_pNext;
+ /** List of user clients. Protected by g_Mtx. */
+ static org_virtualbox_VBoxUSBClient * volatile s_pHead;
+};
+OSDefineMetaClassAndStructors(org_virtualbox_VBoxUSBClient, IOUserClient);
+
+
+/**
+ * The IOUSBDevice driver class.
+ *
+ * The main purpose of this is hijack devices matching current filters.
+ *
+ * @remarks This is derived from IOUSBUserClientInit instead of IOService because we must make
+ * sure IOUSBUserClientInit::start() gets invoked for this provider. The problem is that
+ * there is some kind of magic that prevents this from happening if we boost the probe
+ * score to high. With the result that we don't have the required plugin entry for
+ * user land and consequently cannot open it.
+ *
+ * So, to avoid having to write a lot of code we just inherit from IOUSBUserClientInit
+ * and make some possibly bold assumptions about it not changing. This just means
+ * we'll have to keep an eye on the source apple releases or only call
+ * IOUSBUserClientInit::start() and hand the rest of the super calls to IOService. For
+ * now we're doing it by the C++ book.
+ */
+class org_virtualbox_VBoxUSBDevice : public IOUSBUserClientInit
+{
+ OSDeclareDefaultStructors(org_virtualbox_VBoxUSBDevice);
+
+public:
+ RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT();
+
+ /** @name IOService
+ * @{ */
+ virtual bool init(OSDictionary *pDictionary = 0);
+ virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score);
+ virtual bool start(IOService *pProvider);
+ virtual bool terminate(IOOptionBits fOptions = 0);
+ virtual void stop(IOService *pProvider);
+ virtual void free();
+ virtual IOReturn message(UInt32 enmMsg, IOService *pProvider, void *pvArg = 0);
+ /** @} */
+
+ static void scheduleReleaseByOwner(RTPROCESS Owner);
+private:
+ /** Padding to guard against parent class expanding (see class remarks). */
+ uint8_t m_abSafetyPadding[256];
+ /** The interface we're driving (aka. the provider). */
+ IOUSBDevice *m_pDevice;
+ /** The owner process, meaning the VBoxSVC process. */
+ RTPROCESS volatile m_Owner;
+ /** The client process, meaning the VM process. */
+ RTPROCESS volatile m_Client;
+ /** The ID of the matching filter. */
+ uintptr_t m_uId;
+ /** Have we opened the device or not? */
+ bool volatile m_fOpen;
+ /** Should be open the device on the next close notification message? */
+ bool volatile m_fOpenOnWasClosed;
+ /** Whether to re-enumerate this device when the client closes it.
+ * This is something we'll do when the filter owner dies. */
+ bool volatile m_fReleaseOnClose;
+ /** Whether we're being unloaded or not.
+ * Only valid in stop(). */
+ bool m_fBeingUnloaded;
+ /** Pointer to the next device in the list. */
+ org_virtualbox_VBoxUSBDevice * volatile m_pNext;
+ /** Pointer to the list head. Protected by g_Mtx. */
+ static org_virtualbox_VBoxUSBDevice * volatile s_pHead;
+
+#ifdef DEBUG
+ /** The interest notifier. */
+ IONotifier *m_pNotifier;
+
+ static IOReturn MyInterestHandler(void *pvTarget, void *pvRefCon, UInt32 enmMsgType,
+ IOService *pProvider, void * pvMsgArg, vm_size_t cbMsgArg);
+#endif
+};
+OSDefineMetaClassAndStructors(org_virtualbox_VBoxUSBDevice, IOUSBUserClientInit);
+
+
+/**
+ * The IOUSBInterface driver class.
+ *
+ * The main purpose of this is hijack interfaces which device is driven
+ * by org_virtualbox_VBoxUSBDevice.
+ *
+ * @remarks See org_virtualbox_VBoxUSBDevice for why we use IOUSBUserClientInit.
+ */
+class org_virtualbox_VBoxUSBInterface : public IOUSBUserClientInit
+{
+ OSDeclareDefaultStructors(org_virtualbox_VBoxUSBInterface);
+
+public:
+ RTR0MEMEF_NEW_AND_DELETE_OPERATORS_IOKIT();
+
+ /** @name IOService
+ * @{ */
+ virtual bool init(OSDictionary *pDictionary = 0);
+ virtual IOService *probe(IOService *pProvider, SInt32 *pi32Score);
+ virtual bool start(IOService *pProvider);
+ virtual bool terminate(IOOptionBits fOptions = 0);
+ virtual void stop(IOService *pProvider);
+ virtual void free();
+ virtual IOReturn message(UInt32 enmMsg, IOService *pProvider, void *pvArg = 0);
+ /** @} */
+
+private:
+ /** Padding to guard against parent class expanding (see class remarks). */
+ uint8_t m_abSafetyPadding[256];
+ /** The interface we're driving (aka. the provider). */
+ IOUSBInterface *m_pInterface;
+ /** Have we opened the device or not? */
+ bool volatile m_fOpen;
+ /** Should be open the device on the next close notification message? */
+ bool volatile m_fOpenOnWasClosed;
+};
+OSDefineMetaClassAndStructors(org_virtualbox_VBoxUSBInterface, IOUSBUserClientInit);
+
+
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/*
+ * Declare the module stuff.
+ */
+RT_C_DECLS_BEGIN
+extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData);
+extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData);
+
+KMOD_EXPLICIT_DECL(VBoxDrv, VBOX_VERSION_STRING, _start, _stop)
+DECLHIDDEN(kmod_start_func_t *) _realmain = VBoxUSBStart;
+DECLHIDDEN(kmod_stop_func_t *) _antimain = VBoxUSBStop;
+DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__;
+RT_C_DECLS_END
+
+/** Mutex protecting the lists. */
+static RTSEMFASTMUTEX g_Mtx = NIL_RTSEMFASTMUTEX;
+org_virtualbox_VBoxUSBClient * volatile org_virtualbox_VBoxUSBClient::s_pHead = NULL;
+org_virtualbox_VBoxUSBDevice * volatile org_virtualbox_VBoxUSBDevice::s_pHead = NULL;
+
+/** Global instance count - just for checking proving that everything is destroyed correctly. */
+static volatile uint32_t g_cInstances = 0;
+
+
+/**
+ * Start the kernel module.
+ */
+static kern_return_t VBoxUSBStart(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ int rc;
+ Log(("VBoxUSBStart\n"));
+
+ /*
+ * Initialize IPRT.
+ */
+ rc = RTR0Init(0);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the spinlock.
+ */
+ rc = RTSemFastMutexCreate(&g_Mtx);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VBoxUSBFilterInit();
+ if (RT_SUCCESS(rc))
+ {
+#if 0 /* testing */
+ USBFILTER Flt;
+ USBFilterInit(&Flt, USBFILTERTYPE_CAPTURE);
+ USBFilterSetNumExact(&Flt, USBFILTERIDX_VENDOR_ID, 0x096e, true);
+ uintptr_t uId;
+ rc = VBoxUSBFilterAdd(&Flt, 1, &uId);
+ printf("VBoxUSB: VBoxUSBFilterAdd #1 -> %d + %p\n", rc, uId);
+
+ USBFilterInit(&Flt, USBFILTERTYPE_CAPTURE);
+ USBFilterSetStringPattern(&Flt, USBFILTERIDX_PRODUCT_STR, "*DISK*", true);
+ rc = VBoxUSBFilterAdd(&Flt, 2, &uId);
+ printf("VBoxUSB: VBoxUSBFilterAdd #2 -> %d + %p\n", rc, uId);
+#endif
+ return KMOD_RETURN_SUCCESS;
+ }
+ printf("VBoxUSB: VBoxUSBFilterInit failed (rc=%d)\n", rc);
+ RTSemFastMutexDestroy(g_Mtx);
+ g_Mtx = NIL_RTSEMFASTMUTEX;
+ }
+ else
+ printf("VBoxUSB: RTSemFastMutexCreate failed (rc=%d)\n", rc);
+ RTR0Term();
+ }
+ else
+ printf("VBoxUSB: failed to initialize IPRT (rc=%d)\n", rc);
+
+ return KMOD_RETURN_FAILURE;
+}
+
+
+/**
+ * Stop the kernel module.
+ */
+static kern_return_t VBoxUSBStop(struct kmod_info *pKModInfo, void *pvData)
+{
+ RT_NOREF(pKModInfo, pvData);
+ Log(("VBoxUSBStop: g_cInstances=%d\n", g_cInstances));
+
+ /** @todo Fix problem with crashing when unloading a driver that's in use. */
+
+ /*
+ * Undo the work done during start (in reverse order).
+ */
+ VBoxUSBFilterTerm();
+
+ int rc = RTSemFastMutexDestroy(g_Mtx);
+ AssertRC(rc);
+ g_Mtx = NIL_RTSEMFASTMUTEX;
+
+ RTR0Term();
+
+ Log(("VBoxUSBStop - done\n"));
+ return KMOD_RETURN_SUCCESS;
+}
+
+
+
+
+
+#ifdef LOG_ENABLED
+/**
+ * Gets the name of a IOKit message.
+ *
+ * @returns Message name (read only).
+ * @param enmMsg The message.
+ */
+DECLINLINE(const char *) DbgGetIOKitMessageName(UInt32 enmMsg)
+{
+ switch (enmMsg)
+ {
+# define MY_CASE(enm) case enm: return #enm; break
+ MY_CASE(kIOMessageServiceIsTerminated);
+ MY_CASE(kIOMessageServiceIsSuspended);
+ MY_CASE(kIOMessageServiceIsResumed);
+ MY_CASE(kIOMessageServiceIsRequestingClose);
+ MY_CASE(kIOMessageServiceIsAttemptingOpen);
+ MY_CASE(kIOMessageServiceWasClosed);
+ MY_CASE(kIOMessageServiceBusyStateChange);
+ MY_CASE(kIOMessageServicePropertyChange);
+ MY_CASE(kIOMessageCanDevicePowerOff);
+ MY_CASE(kIOMessageDeviceWillPowerOff);
+ MY_CASE(kIOMessageDeviceWillNotPowerOff);
+ MY_CASE(kIOMessageDeviceHasPoweredOn);
+ MY_CASE(kIOMessageCanSystemPowerOff);
+ MY_CASE(kIOMessageSystemWillPowerOff);
+ MY_CASE(kIOMessageSystemWillNotPowerOff);
+ MY_CASE(kIOMessageCanSystemSleep);
+ MY_CASE(kIOMessageSystemWillSleep);
+ MY_CASE(kIOMessageSystemWillNotSleep);
+ MY_CASE(kIOMessageSystemHasPoweredOn);
+ MY_CASE(kIOMessageSystemWillRestart);
+ MY_CASE(kIOMessageSystemWillPowerOn);
+ MY_CASE(kIOUSBMessageHubResetPort);
+ MY_CASE(kIOUSBMessageHubSuspendPort);
+ MY_CASE(kIOUSBMessageHubResumePort);
+ MY_CASE(kIOUSBMessageHubIsDeviceConnected);
+ MY_CASE(kIOUSBMessageHubIsPortEnabled);
+ MY_CASE(kIOUSBMessageHubReEnumeratePort);
+ MY_CASE(kIOUSBMessagePortHasBeenReset);
+ MY_CASE(kIOUSBMessagePortHasBeenResumed);
+ MY_CASE(kIOUSBMessageHubPortClearTT);
+ MY_CASE(kIOUSBMessagePortHasBeenSuspended);
+ MY_CASE(kIOUSBMessageFromThirdParty);
+ MY_CASE(kIOUSBMessagePortWasNotSuspended);
+ MY_CASE(kIOUSBMessageExpressCardCantWake);
+// MY_CASE(kIOUSBMessageCompositeDriverReconfigured);
+# undef MY_CASE
+ }
+ return "unknown";
+}
+#endif /* LOG_ENABLED */
+
+
+
+
+
+/*
+ *
+ * org_virtualbox_VBoxUSB
+ *
+ */
+
+
+/**
+ * Initialize the object.
+ * @remark Only for logging.
+ */
+bool
+org_virtualbox_VBoxUSB::init(OSDictionary *pDictionary)
+{
+ uint32_t cInstances = ASMAtomicIncU32(&g_cInstances);
+ Log(("VBoxUSB::init([%p], %p) new g_cInstances=%d\n", this, pDictionary, cInstances));
+ RT_NOREF_PV(cInstances);
+
+ if (IOService::init(pDictionary))
+ {
+ /* init members. */
+ return true;
+ }
+ ASMAtomicDecU32(&g_cInstances);
+ return false;
+}
+
+
+/**
+ * Free the object.
+ * @remark Only for logging.
+ */
+void
+org_virtualbox_VBoxUSB::free()
+{
+ uint32_t cInstances = ASMAtomicDecU32(&g_cInstances); NOREF(cInstances);
+ Log(("VBoxUSB::free([%p]) new g_cInstances=%d\n", this, cInstances));
+ IOService::free();
+}
+
+
+/**
+ * Start this service.
+ */
+bool
+org_virtualbox_VBoxUSB::start(IOService *pProvider)
+{
+ Log(("VBoxUSB::start([%p], %p {%s})\n", this, pProvider, pProvider->getName()));
+
+ if (IOService::start(pProvider))
+ {
+ /* register the service. */
+ registerService();
+ return true;
+ }
+ return false;
+}
+
+
+/**
+ * Stop this service.
+ * @remark Only for logging.
+ */
+void
+org_virtualbox_VBoxUSB::stop(IOService *pProvider)
+{
+ Log(("VBoxUSB::stop([%p], %p (%s))\n", this, pProvider, pProvider->getName()));
+ IOService::stop(pProvider);
+}
+
+
+/**
+ * Stop this service.
+ * @remark Only for logging.
+ */
+bool
+org_virtualbox_VBoxUSB::open(IOService *pForClient, IOOptionBits fOptions/* = 0*/, void *pvArg/* = 0*/)
+{
+ Log(("VBoxUSB::open([%p], %p, %#x, %p)\n", this, pForClient, fOptions, pvArg));
+ bool fRc = IOService::open(pForClient, fOptions, pvArg);
+ Log(("VBoxUSB::open([%p], %p, %#x, %p) -> %d\n", this, pForClient, fOptions, pvArg, fRc));
+ return fRc;
+}
+
+
+/**
+ * Stop this service.
+ * @remark Only for logging.
+ */
+void
+org_virtualbox_VBoxUSB::close(IOService *pForClient, IOOptionBits fOptions/* = 0*/)
+{
+ Log(("VBoxUSB::close([%p], %p, %#x)\n", this, pForClient, fOptions));
+ IOService::close(pForClient, fOptions);
+}
+
+
+/**
+ * Terminate request.
+ * @remark Only for logging.
+ */
+bool
+org_virtualbox_VBoxUSB::terminate(IOOptionBits fOptions)
+{
+ Log(("VBoxUSB::terminate([%p], %#x): g_cInstances=%d\n", this, fOptions, g_cInstances));
+ bool fRc = IOService::terminate(fOptions);
+ Log(("VBoxUSB::terminate([%p], %#x): returns %d\n", this, fOptions, fRc));
+ return fRc;
+}
+
+
+
+
+
+
+
+
+
+
+
+/*
+ *
+ * org_virtualbox_VBoxUSBClient
+ *
+ */
+
+
+/**
+ * Initializer called when the client opens the service.
+ */
+bool
+org_virtualbox_VBoxUSBClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type)
+{
+ if (!OwningTask)
+ {
+ Log(("VBoxUSBClient::initWithTask([%p], %p, %p, %#x) -> false (no task)\n", this, OwningTask, pvSecurityId, u32Type));
+ return false;
+ }
+ if (u32Type != VBOXUSB_DARWIN_IOSERVICE_COOKIE)
+ {
+ Log(("VBoxUSBClient::initWithTask: Bade cookie %#x\n", u32Type));
+ return false;
+ }
+
+ proc_t pProc = (proc_t)get_bsdtask_info(OwningTask); /* we need the pid */
+ Log(("VBoxUSBClient::initWithTask([%p], %p(->%p:{.pid=%d}, %p, %#x)\n",
+ this, OwningTask, pProc, pProc ? proc_pid(pProc) : -1, pvSecurityId, u32Type));
+
+ if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type))
+ {
+ /*
+ * In theory we have to call task_reference() to make sure that the task is
+ * valid during the lifetime of this object. The pointer is only used to check
+ * for the context this object is called in though and never dereferenced
+ * or passed to anything which might, so we just skip this step.
+ */
+ m_pProvider = NULL;
+ m_Task = OwningTask;
+ m_Process = pProc ? proc_pid(pProc) : NIL_RTPROCESS;
+ m_pNext = NULL;
+
+ uint32_t cInstances = ASMAtomicIncU32(&g_cInstances);
+ Log(("VBoxUSBClient::initWithTask([%p], %p(->%p:{.pid=%d}, %p, %#x) -> true; new g_cInstances=%d\n",
+ this, OwningTask, pProc, pProc ? proc_pid(pProc) : -1, pvSecurityId, u32Type, cInstances));
+ RT_NOREF_PV(cInstances);
+ return true;
+ }
+
+ Log(("VBoxUSBClient::initWithTask([%p], %p(->%p:{.pid=%d}, %p, %#x) -> false\n",
+ this, OwningTask, pProc, pProc ? proc_pid(pProc) : -1, pvSecurityId, u32Type));
+ return false;
+}
+
+
+/**
+ * Free the object.
+ * @remark Only for logging.
+ */
+void
+org_virtualbox_VBoxUSBClient::free()
+{
+ uint32_t cInstances = ASMAtomicDecU32(&g_cInstances); NOREF(cInstances);
+ Log(("VBoxUSBClient::free([%p]) new g_cInstances=%d\n", this, cInstances));
+ IOUserClient::free();
+}
+
+
+/**
+ * Start the client service.
+ */
+bool
+org_virtualbox_VBoxUSBClient::start(IOService *pProvider)
+{
+ Log(("VBoxUSBClient::start([%p], %p)\n", this, pProvider));
+ if (IOUserClient::start(pProvider))
+ {
+ m_pProvider = OSDynamicCast(org_virtualbox_VBoxUSB, pProvider);
+ if (m_pProvider)
+ {
+ /*
+ * Add ourselves to the list of user clients.
+ */
+ VBOXUSB_LOCK();
+
+ m_pNext = s_pHead;
+ s_pHead = this;
+
+ VBOXUSB_UNLOCK();
+
+ return true;
+ }
+ Log(("VBoxUSBClient::start: %p isn't org_virtualbox_VBoxUSB\n", pProvider));
+ }
+ return false;
+}
+
+
+/**
+ * Client exits normally.
+ */
+IOReturn
+org_virtualbox_VBoxUSBClient::clientClose(void)
+{
+ Log(("VBoxUSBClient::clientClose([%p:{.m_Process=%d}])\n", this, (int)m_Process));
+
+ /*
+ * Remove this process from the client list.
+ */
+ VBOXUSB_LOCK();
+
+ org_virtualbox_VBoxUSBClient *pPrev = NULL;
+ for (org_virtualbox_VBoxUSBClient *pCur = s_pHead; pCur; pCur = pCur->m_pNext)
+ {
+ if (pCur == this)
+ {
+ if (pPrev)
+ pPrev->m_pNext = m_pNext;
+ else
+ s_pHead = m_pNext;
+ m_pNext = NULL;
+ break;
+ }
+ pPrev = pCur;
+ }
+
+ VBOXUSB_UNLOCK();
+
+ /*
+ * Drop all filters owned by this client.
+ */
+ if (m_Process != NIL_RTPROCESS)
+ VBoxUSBFilterRemoveOwner(m_Process);
+
+ /*
+ * Schedule all devices owned (filtered) by this process for
+ * immediate release or release upon close.
+ */
+ if (m_Process != NIL_RTPROCESS)
+ org_virtualbox_VBoxUSBDevice::scheduleReleaseByOwner(m_Process);
+
+ /*
+ * Initiate termination.
+ */
+ m_pProvider = NULL;
+ terminate();
+
+ return kIOReturnSuccess;
+}
+
+
+/**
+ * The client exits abnormally / forgets to do cleanups.
+ * @remark Only for logging.
+ */
+IOReturn
+org_virtualbox_VBoxUSBClient::clientDied(void)
+{
+ Log(("VBoxUSBClient::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n",
+ this, m_Task, RTR0ProcHandleSelf(), RTProcSelf()));
+
+ /* IOUserClient::clientDied() calls clientClose... */
+ return IOUserClient::clientDied();
+}
+
+
+/**
+ * Terminate the service (initiate the destruction).
+ * @remark Only for logging.
+ */
+bool
+org_virtualbox_VBoxUSBClient::terminate(IOOptionBits fOptions)
+{
+ /* kIOServiceRecursing, kIOServiceRequired, kIOServiceTerminate, kIOServiceSynchronous - interesting option bits */
+ Log(("VBoxUSBClient::terminate([%p], %#x)\n", this, fOptions));
+ return IOUserClient::terminate(fOptions);
+}
+
+
+/**
+ * The final stage of the client service destruction.
+ * @remark Only for logging.
+ */
+bool
+org_virtualbox_VBoxUSBClient::finalize(IOOptionBits fOptions)
+{
+ Log(("VBoxUSBClient::finalize([%p], %#x)\n", this, fOptions));
+ return IOUserClient::finalize(fOptions);
+}
+
+
+/**
+ * Stop the client service.
+ */
+void
+org_virtualbox_VBoxUSBClient::stop(IOService *pProvider)
+{
+ Log(("VBoxUSBClient::stop([%p])\n", this));
+ IOUserClient::stop(pProvider);
+
+ /*
+ * Paranoia.
+ */
+ VBOXUSB_LOCK();
+
+ org_virtualbox_VBoxUSBClient *pPrev = NULL;
+ for (org_virtualbox_VBoxUSBClient *pCur = s_pHead; pCur; pCur = pCur->m_pNext)
+ {
+ if (pCur == this)
+ {
+ if (pPrev)
+ pPrev->m_pNext = m_pNext;
+ else
+ s_pHead = m_pNext;
+ m_pNext = NULL;
+ break;
+ }
+ pPrev = pCur;
+ }
+
+ VBOXUSB_UNLOCK();
+}
+
+
+/**
+ * Translate a user method index into a service object and an external method structure.
+ *
+ * @returns Pointer to external method structure descripting the method.
+ * NULL if the index isn't valid.
+ * @param ppService Where to store the service object on success.
+ * @param iMethod The method index.
+ */
+IOExternalMethod *
+org_virtualbox_VBoxUSBClient::getTargetAndMethodForIndex(IOService **ppService, UInt32 iMethod)
+{
+ static IOExternalMethod s_aMethods[VBOXUSBMETHOD_END] =
+ {
+ /*[VBOXUSBMETHOD_ADD_FILTER] = */
+ {
+ (IOService *)0, /* object */
+ (IOMethod)&org_virtualbox_VBoxUSBClient::addFilter, /* func */
+ kIOUCStructIStructO, /* flags - struct input (count0) and struct output (count1) */
+ sizeof(USBFILTER), /* count0 - size of the input struct. */
+ sizeof(VBOXUSBADDFILTEROUT) /* count1 - size of the return struct. */
+ },
+ /* [VBOXUSBMETHOD_FILTER_REMOVE] = */
+ {
+ (IOService *)0, /* object */
+ (IOMethod)&org_virtualbox_VBoxUSBClient::removeFilter, /* func */
+ kIOUCStructIStructO, /* flags - struct input (count0) and struct output (count1) */
+ sizeof(uintptr_t), /* count0 - size of the input (id) */
+ sizeof(int) /* count1 - size of the output (rc) */
+ },
+ };
+
+ if (RT_UNLIKELY(iMethod >= RT_ELEMENTS(s_aMethods)))
+ return NULL;
+
+ *ppService = this;
+ return &s_aMethods[iMethod];
+}
+
+
+/**
+ * Add filter user request.
+ *
+ * @returns IOKit status code.
+ * @param pFilter The filter to add.
+ * @param pOut Pointer to the output structure.
+ * @param cbFilter Size of the filter structure.
+ * @param pcbOut In/Out - sizeof(*pOut).
+ */
+IOReturn
+org_virtualbox_VBoxUSBClient::addFilter(PUSBFILTER pFilter, PVBOXUSBADDFILTEROUT pOut, IOByteCount cbFilter, IOByteCount *pcbOut)
+{
+ Log(("VBoxUSBClient::addFilter: [%p:{.m_Process=%d}] pFilter=%p pOut=%p\n", this, (int)m_Process, pFilter, pOut));
+
+ /*
+ * Validate input.
+ */
+ if (RT_UNLIKELY( cbFilter != sizeof(*pFilter)
+ || *pcbOut != sizeof(*pOut)))
+ {
+ printf("VBoxUSBClient::addFilter: cbFilter=%#x expected %#x; *pcbOut=%#x expected %#x\n",
+ (int)cbFilter, (int)sizeof(*pFilter), (int)*pcbOut, (int)sizeof(*pOut));
+ return kIOReturnBadArgument;
+ }
+
+ /*
+ * Log the filter details.
+ */
+#ifdef DEBUG
+ Log2(("VBoxUSBClient::addFilter: idVendor=%#x idProduct=%#x bcdDevice=%#x bDeviceClass=%#x bDeviceSubClass=%#x bDeviceProtocol=%#x bBus=%#x bPort=%#x\n",
+ USBFilterGetNum(pFilter, USBFILTERIDX_VENDOR_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PRODUCT_ID),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_REV),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_SUB_CLASS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_DEVICE_PROTOCOL),
+ USBFilterGetNum(pFilter, USBFILTERIDX_BUS),
+ USBFilterGetNum(pFilter, USBFILTERIDX_PORT)));
+ Log2(("VBoxUSBClient::addFilter: Manufacturer=%s Product=%s Serial=%s\n",
+ USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_MANUFACTURER_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_PRODUCT_STR) : "<null>",
+ USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) ? USBFilterGetString(pFilter, USBFILTERIDX_SERIAL_NUMBER_STR) : "<null>"));
+#endif
+
+ /*
+ * Since we cannot query the bus number, make sure the filter
+ * isn't requiring that field to be present.
+ */
+ int rc = USBFilterSetMustBePresent(pFilter, USBFILTERIDX_BUS, false /* fMustBePresent */); AssertRC(rc);
+
+ /*
+ * Add the filter.
+ */
+ pOut->uId = 0;
+ pOut->rc = VBoxUSBFilterAdd(pFilter, m_Process, &pOut->uId);
+
+ Log(("VBoxUSBClient::addFilter: returns *pOut={.rc=%d, .uId=%p}\n", pOut->rc, (void *)pOut->uId));
+ return kIOReturnSuccess;
+}
+
+
+/**
+ * Removes filter user request.
+ *
+ * @returns IOKit status code.
+ * @param puId Where to get the filter ID.
+ * @param prc Where to store the return code.
+ * @param cbIn sizeof(*puId).
+ * @param pcbOut In/Out - sizeof(*prc).
+ */
+IOReturn
+org_virtualbox_VBoxUSBClient::removeFilter(uintptr_t *puId, int *prc, IOByteCount cbIn, IOByteCount *pcbOut)
+{
+ Log(("VBoxUSBClient::removeFilter: [%p:{.m_Process=%d}] *puId=%p m_Proc\n", this, (int)m_Process, *puId));
+
+ /*
+ * Validate input.
+ */
+ if (RT_UNLIKELY( cbIn != sizeof(*puId)
+ || *pcbOut != sizeof(*prc)))
+ {
+ printf("VBoxUSBClient::removeFilter: cbIn=%#x expected %#x; *pcbOut=%#x expected %#x\n",
+ (int)cbIn, (int)sizeof(*puId), (int)*pcbOut, (int)sizeof(*prc));
+ return kIOReturnBadArgument;
+ }
+
+ /*
+ * Remove the filter.
+ */
+ *prc = VBoxUSBFilterRemove(m_Process, *puId);
+
+ Log(("VBoxUSBClient::removeFilter: returns *prc=%d\n", *prc));
+ return kIOReturnSuccess;
+}
+
+
+/**
+ * Checks whether the specified task is a VBoxUSB client task or not.
+ *
+ * This is used to validate clients trying to open any of the device
+ * or interfaces that we've hijacked.
+ *
+ * @returns true / false.
+ * @param ClientTask The task.
+ *
+ * @remark This protecting against other user clients is not currently implemented
+ * as it turned out to be more bothersome than first imagined.
+ */
+/* static*/ bool
+org_virtualbox_VBoxUSBClient::isClientTask(task_t ClientTask)
+{
+ VBOXUSB_LOCK();
+
+ for (org_virtualbox_VBoxUSBClient *pCur = s_pHead; pCur; pCur = pCur->m_pNext)
+ if (pCur->m_Task == ClientTask)
+ {
+ VBOXUSB_UNLOCK();
+ return true;
+ }
+
+ VBOXUSB_UNLOCK();
+ return false;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ *
+ * org_virtualbox_VBoxUSBDevice
+ *
+ */
+
+/**
+ * Initialize instance data.
+ *
+ * @returns Success indicator.
+ * @param pDictionary The dictionary that will become the registry entry's
+ * property table, or NULL. Hand it up to our parents.
+ */
+bool
+org_virtualbox_VBoxUSBDevice::init(OSDictionary *pDictionary)
+{
+ uint32_t cInstances = ASMAtomicIncU32(&g_cInstances);
+ Log(("VBoxUSBDevice::init([%p], %p) new g_cInstances=%d\n", this, pDictionary, cInstances));
+ RT_NOREF_PV(cInstances);
+
+ m_pDevice = NULL;
+ m_Owner = NIL_RTPROCESS;
+ m_Client = NIL_RTPROCESS;
+ m_uId = ~(uintptr_t)0;
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+ m_fReleaseOnClose = false;
+ m_fBeingUnloaded = false;
+ m_pNext = NULL;
+#ifdef DEBUG
+ m_pNotifier = NULL;
+#endif
+
+ return IOUSBUserClientInit::init(pDictionary);
+}
+
+/**
+ * Free the object.
+ * @remark Only for logging.
+ */
+void
+org_virtualbox_VBoxUSBDevice::free()
+{
+ uint32_t cInstances = ASMAtomicDecU32(&g_cInstances); NOREF(cInstances);
+ Log(("VBoxUSBDevice::free([%p]) new g_cInstances=%d\n", this, cInstances));
+ IOUSBUserClientInit::free();
+}
+
+
+/**
+ * The device/driver probing.
+ *
+ * I/O Kit will iterate all device drivers suitable for this kind of device
+ * (this is something it figures out from the property file) and call their
+ * probe() method in order to try determine which is the best match for the
+ * device. We will match the device against the registered filters and set
+ * a ridiculously high score if we find it, thus making it extremely likely
+ * that we'll be the first driver to be started. We'll also set a couple of
+ * attributes so that it's not necessary to do a rematch in init to find
+ * the appropriate filter (might not be necessary..., see todo).
+ *
+ * @returns Service instance to be started and *pi32Score if matching.
+ * NULL if not a device suitable for this driver.
+ *
+ * @param pProvider The provider instance.
+ * @param pi32Score Where to store the probe score.
+ */
+IOService *
+org_virtualbox_VBoxUSBDevice::probe(IOService *pProvider, SInt32 *pi32Score)
+{
+ Log(("VBoxUSBDevice::probe([%p], %p {%s}, %p={%d})\n", this,
+ pProvider, pProvider->getName(), pi32Score, pi32Score ? *pi32Score : 0));
+
+ /*
+ * Check against filters.
+ */
+ USBFILTER Device;
+ USBFilterInit(&Device, USBFILTERTYPE_CAPTURE);
+
+ static const struct
+ {
+ const char *pszName;
+ USBFILTERIDX enmIdx;
+ bool fNumeric;
+ } s_aProps[] =
+ {
+ { kUSBVendorID, USBFILTERIDX_VENDOR_ID, true },
+ { kUSBProductID, USBFILTERIDX_PRODUCT_ID, true },
+ { kUSBDeviceReleaseNumber, USBFILTERIDX_DEVICE_REV, true },
+ { kUSBDeviceClass, USBFILTERIDX_DEVICE_CLASS, true },
+ { kUSBDeviceSubClass, USBFILTERIDX_DEVICE_SUB_CLASS, true },
+ { kUSBDeviceProtocol, USBFILTERIDX_DEVICE_PROTOCOL, true },
+ { "PortNum", USBFILTERIDX_PORT, true },
+ /// @todo { , USBFILTERIDX_BUS, true }, - must be derived :-/
+ /// Seems to be the upper byte of locationID and our "grand parent" has a USBBusNumber prop.
+ { "USB Vendor Name", USBFILTERIDX_MANUFACTURER_STR, false },
+ { "USB Product Name", USBFILTERIDX_PRODUCT_STR, false },
+ { "USB Serial Number", USBFILTERIDX_SERIAL_NUMBER_STR, false },
+ };
+ for (unsigned i = 0; i < RT_ELEMENTS(s_aProps); i++)
+ {
+ OSObject *pObj = pProvider->getProperty(s_aProps[i].pszName);
+ if (!pObj)
+ continue;
+ if (s_aProps[i].fNumeric)
+ {
+ OSNumber *pNum = OSDynamicCast(OSNumber, pObj);
+ if (pNum)
+ {
+ uint16_t u16 = pNum->unsigned16BitValue();
+ Log2(("VBoxUSBDevice::probe: %d/%s - %#x (32bit=%#x)\n", i, s_aProps[i].pszName, u16, pNum->unsigned32BitValue()));
+ int vrc = USBFilterSetNumExact(&Device, s_aProps[i].enmIdx, u16, true);
+ if (RT_FAILURE(vrc))
+ Log(("VBoxUSBDevice::probe: pObj=%p pNum=%p - %d/%s - rc=%d!\n", pObj, pNum, i, s_aProps[i].pszName, vrc));
+ }
+ else
+ Log(("VBoxUSBDevice::probe: pObj=%p pNum=%p - %d/%s!\n", pObj, pNum, i, s_aProps[i].pszName));
+ }
+ else
+ {
+ OSString *pStr = OSDynamicCast(OSString, pObj);
+ if (pStr)
+ {
+ Log2(("VBoxUSBDevice::probe: %d/%s - %s\n", i, s_aProps[i].pszName, pStr->getCStringNoCopy()));
+ int vrc = USBFilterSetStringExact(&Device, s_aProps[i].enmIdx, pStr->getCStringNoCopy(),
+ true /*fMustBePresent*/, true /*fPurge*/);
+ if (RT_FAILURE(vrc))
+ Log(("VBoxUSBDevice::probe: pObj=%p pStr=%p - %d/%s - rc=%d!\n", pObj, pStr, i, s_aProps[i].pszName, vrc));
+ }
+ else
+ Log(("VBoxUSBDevice::probe: pObj=%p pStr=%p - %d/%s\n", pObj, pStr, i, s_aProps[i].pszName));
+ }
+ }
+ /** @todo try figure the blasted bus number */
+
+ /*
+ * Run filters on it.
+ */
+ uintptr_t uId = 0;
+ RTPROCESS Owner = VBoxUSBFilterMatch(&Device, &uId);
+ USBFilterDelete(&Device);
+ if (Owner == NIL_RTPROCESS)
+ {
+ Log(("VBoxUSBDevice::probe: returns NULL uId=%d\n", uId));
+ return NULL;
+ }
+
+ /*
+ * It matched. Save the owner in the provider registry (hope that works).
+ */
+ /*IOService *pRet = IOUSBUserClientInit::probe(pProvider, pi32Score); - call always returns NULL on 10.11+ */
+ /*AssertMsg(pRet == this, ("pRet=%p this=%p *pi32Score=%d \n", pRet, this, pi32Score ? *pi32Score : 0)); - call always returns NULL on 10.11+ */
+ IOService *pRet = this;
+ m_Owner = Owner;
+ m_uId = uId;
+ Log(("%p: m_Owner=%d m_uId=%d\n", this, (int)m_Owner, (int)m_uId));
+ *pi32Score = _1G;
+ Log(("VBoxUSBDevice::probe: returns %p and *pi32Score=%d\n", pRet, *pi32Score));
+ return pRet;
+}
+
+
+/**
+ * Try start the device driver.
+ *
+ * We will do device linking, copy the filter and owner properties from the provider,
+ * set the client property, retain the device, and try open (seize) the device.
+ *
+ * @returns Success indicator.
+ * @param pProvider The provider instance.
+ */
+bool
+org_virtualbox_VBoxUSBDevice::start(IOService *pProvider)
+{
+ Log(("VBoxUSBDevice::start([%p:{.m_Owner=%d, .m_uId=%p}], %p {%s})\n",
+ this, m_Owner, m_uId, pProvider, pProvider->getName()));
+
+ m_pDevice = OSDynamicCast(IOUSBDevice, pProvider);
+ if (!m_pDevice)
+ {
+ printf("VBoxUSBDevice::start([%p], %p {%s}): failed!\n", this, pProvider, pProvider->getName());
+ return false;
+ }
+
+#ifdef DEBUG
+ /* for some extra log messages */
+ m_pNotifier = pProvider->registerInterest(gIOGeneralInterest,
+ &org_virtualbox_VBoxUSBDevice::MyInterestHandler,
+ this, /* pvTarget */
+ NULL); /* pvRefCon */
+#endif
+
+ /*
+ * Exploit IOUSBUserClientInit to process IOProviderMergeProperties.
+ */
+ IOUSBUserClientInit::start(pProvider); /* returns false */
+
+ /*
+ * Link ourselves into the list of hijacked device.
+ */
+ VBOXUSB_LOCK();
+
+ m_pNext = s_pHead;
+ s_pHead = this;
+
+ VBOXUSB_UNLOCK();
+
+ /*
+ * Set the VBoxUSB properties.
+ */
+ if (!setProperty(VBOXUSB_OWNER_KEY, (unsigned long long)m_Owner, sizeof(m_Owner) * 8 /* bits */))
+ Log(("VBoxUSBDevice::start: failed to set the '" VBOXUSB_OWNER_KEY "' property!\n"));
+ if (!setProperty(VBOXUSB_CLIENT_KEY, (unsigned long long)m_Client, sizeof(m_Client) * 8 /* bits */))
+ Log(("VBoxUSBDevice::start: failed to set the '" VBOXUSB_CLIENT_KEY "' property!\n"));
+ if (!setProperty(VBOXUSB_FILTER_KEY, (unsigned long long)m_uId, sizeof(m_uId) * 8 /* bits */))
+ Log(("VBoxUSBDevice::start: failed to set the '" VBOXUSB_FILTER_KEY "' property!\n"));
+
+ /*
+ * Retain and open the device.
+ */
+ m_pDevice->retain();
+ m_fOpen = m_pDevice->open(this, kIOServiceSeize, 0);
+ if (!m_fOpen)
+ Log(("VBoxUSBDevice::start: failed to open the device!\n"));
+ m_fOpenOnWasClosed = !m_fOpen;
+
+ Log(("VBoxUSBDevice::start: returns %d\n", true));
+ return true;
+}
+
+
+/**
+ * Stop the device driver.
+ *
+ * We'll unlink the device, start device re-enumeration and close it. And call
+ * the parent stop method of course.
+ *
+ * @param pProvider The provider instance.
+ */
+void
+org_virtualbox_VBoxUSBDevice::stop(IOService *pProvider)
+{
+ Log(("VBoxUSBDevice::stop([%p], %p {%s})\n", this, pProvider, pProvider->getName()));
+
+ /*
+ * Remove ourselves from the list of device.
+ */
+ VBOXUSB_LOCK();
+
+ org_virtualbox_VBoxUSBDevice *pPrev = NULL;
+ for (org_virtualbox_VBoxUSBDevice *pCur = s_pHead; pCur; pCur = pCur->m_pNext)
+ {
+ if (pCur == this)
+ {
+ if (pPrev)
+ pPrev->m_pNext = m_pNext;
+ else
+ s_pHead = m_pNext;
+ m_pNext = NULL;
+ break;
+ }
+ pPrev = pCur;
+ }
+
+ VBOXUSB_UNLOCK();
+
+ /*
+ * Should we release the device?
+ */
+ if (m_fBeingUnloaded)
+ {
+ if (m_pDevice)
+ {
+ IOReturn irc = m_pDevice->ReEnumerateDevice(0); NOREF(irc);
+ Log(("VBoxUSBDevice::stop([%p], %p {%s}): m_pDevice=%p unload & ReEnumerateDevice -> %#x\n",
+ this, pProvider, pProvider->getName(), m_pDevice, irc));
+ }
+ else
+ {
+ IOUSBDevice *pDevice = OSDynamicCast(IOUSBDevice, pProvider);
+ if (pDevice)
+ {
+ IOReturn irc = pDevice->ReEnumerateDevice(0); NOREF(irc);
+ Log(("VBoxUSBDevice::stop([%p], %p {%s}): pDevice=%p unload & ReEnumerateDevice -> %#x\n",
+ this, pProvider, pProvider->getName(), pDevice, irc));
+ }
+ else
+ Log(("VBoxUSBDevice::stop([%p], %p {%s}): failed to cast provider to IOUSBDevice\n",
+ this, pProvider, pProvider->getName()));
+ }
+ }
+ else if (m_fReleaseOnClose)
+ {
+ ASMAtomicWriteBool(&m_fReleaseOnClose, false);
+ if (m_pDevice)
+ {
+ IOReturn irc = m_pDevice->ReEnumerateDevice(0); NOREF(irc);
+ Log(("VBoxUSBDevice::stop([%p], %p {%s}): m_pDevice=%p close & ReEnumerateDevice -> %#x\n",
+ this, pProvider, pProvider->getName(), m_pDevice, irc));
+ }
+ }
+
+ /*
+ * Close and release the IOUSBDevice if didn't do that already in message().
+ */
+ if (m_pDevice)
+ {
+ /* close it */
+ if (m_fOpen)
+ {
+ m_fOpenOnWasClosed = false;
+ m_fOpen = false;
+ m_pDevice->close(this, 0);
+ }
+
+ /* release it (see start()) */
+ m_pDevice->release();
+ m_pDevice = NULL;
+ }
+
+#ifdef DEBUG
+ /* avoid crashing on unload. */
+ if (m_pNotifier)
+ {
+ m_pNotifier->release();
+ m_pNotifier = NULL;
+ }
+#endif
+
+ IOUSBUserClientInit::stop(pProvider);
+ Log(("VBoxUSBDevice::stop: returns void\n"));
+}
+
+
+/**
+ * Terminate the service (initiate the destruction).
+ * @remark Only for logging.
+ */
+bool
+org_virtualbox_VBoxUSBDevice::terminate(IOOptionBits fOptions)
+{
+ /* kIOServiceRecursing, kIOServiceRequired, kIOServiceTerminate, kIOServiceSynchronous - interesting option bits */
+ Log(("VBoxUSBDevice::terminate([%p], %#x)\n", this, fOptions));
+
+ /*
+ * There aren't too many reasons why we gets terminated.
+ * The most common one is that the device is being unplugged. Another is
+ * that we've triggered reenumeration. In both cases we'll get a
+ * kIOMessageServiceIsTerminated message before we're stopped.
+ *
+ * But, when we're unloaded the provider service isn't terminated, and
+ * for some funny reason we're frequently causing kernel panics when the
+ * device is detached (after we're unloaded). So, for now, let's try
+ * re-enumerate it in stop.
+ *
+ * To avoid creating unnecessary trouble we'll try guess if we're being
+ * unloaded from the option bit mask. (kIOServiceRecursing is private btw.)
+ */
+ /** @todo would be nice if there was a documented way of doing the unload detection this, or
+ * figure out what exactly we're doing wrong in the unload scenario. */
+ if ((fOptions & 0xffff) == (kIOServiceRequired | kIOServiceSynchronous))
+ m_fBeingUnloaded = true;
+
+ return IOUSBUserClientInit::terminate(fOptions);
+}
+
+
+/**
+ * Intercept open requests and only let Mr. Right (the VM process) open the device.
+ * This is where it all gets a bit complicated...
+ *
+ * @return Status code.
+ *
+ * @param enmMsg The message number.
+ * @param pProvider Pointer to the provider instance.
+ * @param pvArg Message argument.
+ */
+IOReturn
+org_virtualbox_VBoxUSBDevice::message(UInt32 enmMsg, IOService *pProvider, void *pvArg)
+{
+ Log(("VBoxUSBDevice::message([%p], %#x {%s}, %p {%s}, %p) - pid=%d\n",
+ this, enmMsg, DbgGetIOKitMessageName(enmMsg), pProvider, pProvider->getName(), pvArg, RTProcSelf()));
+
+ IOReturn irc;
+ switch (enmMsg)
+ {
+ /*
+ * This message is send to the current IOService client from IOService::handleOpen(),
+ * expecting it to call pProvider->close() if it agrees to the other party seizing
+ * the service. It is also called in IOService::didTerminate() and perhaps some other
+ * odd places. The way to find out is to examin the pvArg, which would be including
+ * kIOServiceSeize if it's the handleOpen case.
+ *
+ * How to validate that the other end is actually our VM process? Well, IOKit doesn't
+ * provide any clue about the new client really. But fortunately, it seems like the
+ * calling task/process context when the VM tries to open the device is the VM process.
+ * We'll ASSUME this'll remain like this for now...
+ */
+ case kIOMessageServiceIsRequestingClose:
+ irc = kIOReturnExclusiveAccess;
+ /* If it's not a seize request, assume it's didTerminate and pray that it isn't a rouge driver.
+ ... weird, doesn't seem to match for the post has-terminated messages. */
+ if (!((uintptr_t)pvArg & kIOServiceSeize))
+ {
+ Log(("VBoxUSBDevice::message([%p],%p {%s}, %p) - pid=%d: not seize - closing...\n",
+ this, pProvider, pProvider->getName(), pvArg, RTProcSelf()));
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+ if (m_pDevice)
+ m_pDevice->close(this, 0);
+ m_Client = NIL_RTPROCESS;
+ irc = kIOReturnSuccess;
+ }
+ else
+ {
+ if (org_virtualbox_VBoxUSBClient::isClientTask(current_task()))
+ {
+ Log(("VBoxUSBDevice::message([%p],%p {%s}, %p) - pid=%d task=%p: client process, closing.\n",
+ this, pProvider, pProvider->getName(), pvArg, RTProcSelf(), current_task()));
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+ if (m_pDevice)
+ m_pDevice->close(this, 0);
+ m_fOpenOnWasClosed = true;
+ m_Client = RTProcSelf();
+ irc = kIOReturnSuccess;
+ }
+ else
+ Log(("VBoxUSBDevice::message([%p],%p {%s}, %p) - pid=%d task=%p: not client process!\n",
+ this, pProvider, pProvider->getName(), pvArg, RTProcSelf(), current_task()));
+ }
+ if (!setProperty(VBOXUSB_CLIENT_KEY, (unsigned long long)m_Client, sizeof(m_Client) * 8 /* bits */))
+ Log(("VBoxUSBDevice::message: failed to set the '" VBOXUSB_CLIENT_KEY "' property!\n"));
+ break;
+
+ /*
+ * The service was closed by the current client.
+ * Update the client property, check for scheduled re-enumeration and re-open.
+ *
+ * Note that we will not be called if we're doing the closing. (Even if we was
+ * called in that case, the code should be able to handle it.)
+ */
+ case kIOMessageServiceWasClosed:
+ /*
+ * Update the client property value.
+ */
+ if (m_Client != NIL_RTPROCESS)
+ {
+ m_Client = NIL_RTPROCESS;
+ if (!setProperty(VBOXUSB_CLIENT_KEY, (unsigned long long)m_Client, sizeof(m_Client) * 8 /* bits */))
+ Log(("VBoxUSBDevice::message: failed to set the '" VBOXUSB_CLIENT_KEY "' property!\n"));
+ }
+
+ if (m_pDevice)
+ {
+ /*
+ * Should we release the device?
+ */
+ if (ASMAtomicXchgBool(&m_fReleaseOnClose, false))
+ {
+ m_fOpenOnWasClosed = false;
+ irc = m_pDevice->ReEnumerateDevice(0);
+ Log(("VBoxUSBDevice::message([%p], %p {%s}) - ReEnumerateDevice() -> %#x\n",
+ this, pProvider, pProvider->getName(), irc));
+ }
+ /*
+ * Should we attempt to re-open the device?
+ */
+ else if (m_fOpenOnWasClosed)
+ {
+ Log(("VBoxUSBDevice::message: attempting to re-open the device...\n"));
+ m_fOpenOnWasClosed = false;
+ m_fOpen = m_pDevice->open(this, kIOServiceSeize, 0);
+ if (!m_fOpen)
+ Log(("VBoxUSBDevice::message: failed to open the device!\n"));
+ m_fOpenOnWasClosed = !m_fOpen;
+ }
+ }
+
+ irc = IOUSBUserClientInit::message(enmMsg, pProvider, pvArg);
+ break;
+
+ /*
+ * The IOUSBDevice is shutting down, so close it if we've opened it.
+ */
+ case kIOMessageServiceIsTerminated:
+ m_fBeingUnloaded = false;
+ ASMAtomicWriteBool(&m_fReleaseOnClose, false);
+ if (m_pDevice)
+ {
+ /* close it */
+ if (m_fOpen)
+ {
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+ Log(("VBoxUSBDevice::message: closing the device (%p)...\n", m_pDevice));
+ m_pDevice->close(this, 0);
+ }
+
+ /* release it (see start()) */
+ Log(("VBoxUSBDevice::message: releasing the device (%p)...\n", m_pDevice));
+ m_pDevice->release();
+ m_pDevice = NULL;
+ }
+
+ irc = IOUSBUserClientInit::message(enmMsg, pProvider, pvArg);
+ break;
+
+ default:
+ irc = IOUSBUserClientInit::message(enmMsg, pProvider, pvArg);
+ break;
+ }
+
+ Log(("VBoxUSBDevice::message([%p], %#x {%s}, %p {%s}, %p) -> %#x\n",
+ this, enmMsg, DbgGetIOKitMessageName(enmMsg), pProvider, pProvider->getName(), pvArg, irc));
+ return irc;
+}
+
+
+/**
+ * Schedule all devices belonging to the specified process for release.
+ *
+ * Devices that aren't currently in use will be released immediately.
+ *
+ * @param Owner The owner process.
+ */
+/* static */ void
+org_virtualbox_VBoxUSBDevice::scheduleReleaseByOwner(RTPROCESS Owner)
+{
+ Log2(("VBoxUSBDevice::scheduleReleaseByOwner: Owner=%d\n", Owner));
+ AssertReturnVoid(Owner && Owner != NIL_RTPROCESS);
+
+ /*
+ * Walk the list of devices looking for device belonging to this process.
+ *
+ * If we release a device, we have to lave the spinlock and will therefore
+ * have to restart the search.
+ */
+ VBOXUSB_LOCK();
+
+ org_virtualbox_VBoxUSBDevice *pCur;
+ do
+ {
+ for (pCur = s_pHead; pCur; pCur = pCur->m_pNext)
+ {
+ Log2(("VBoxUSBDevice::scheduleReleaseByOwner: pCur=%p m_Owner=%d (%s) m_fReleaseOnClose=%d\n",
+ pCur, pCur->m_Owner, pCur->m_Owner == Owner ? "match" : "mismatch", pCur->m_fReleaseOnClose));
+ if (pCur->m_Owner == Owner)
+ {
+ /* make sure we won't hit it again. */
+ pCur->m_Owner = NIL_RTPROCESS;
+ IOUSBDevice *pDevice = pCur->m_pDevice;
+ if ( pDevice
+ && !pCur->m_fReleaseOnClose)
+ {
+ pCur->m_fOpenOnWasClosed = false;
+ if (pCur->m_Client != NIL_RTPROCESS)
+ {
+ /* It's currently open, so just schedule it for re-enumeration on close. */
+ ASMAtomicWriteBool(&pCur->m_fReleaseOnClose, true);
+ Log(("VBoxUSBDevice::scheduleReleaseByOwner: %p {%s} - used by %d\n",
+ pDevice, pDevice->getName(), pCur->m_Client));
+ }
+ else
+ {
+ /*
+ * Get the USBDevice object and do the re-enumeration now.
+ * Retain the device so we don't run into any trouble.
+ */
+ pDevice->retain();
+ VBOXUSB_UNLOCK();
+
+ IOReturn irc = pDevice->ReEnumerateDevice(0); NOREF(irc);
+ Log(("VBoxUSBDevice::scheduleReleaseByOwner: %p {%s} - ReEnumerateDevice -> %#x\n",
+ pDevice, pDevice->getName(), irc));
+
+ pDevice->release();
+ VBOXUSB_LOCK();
+ break;
+ }
+ }
+ }
+ }
+ } while (pCur);
+
+ VBOXUSB_UNLOCK();
+}
+
+
+#ifdef DEBUG
+/*static*/ IOReturn
+org_virtualbox_VBoxUSBDevice::MyInterestHandler(void *pvTarget, void *pvRefCon, UInt32 enmMsgType,
+ IOService *pProvider, void * pvMsgArg, vm_size_t cbMsgArg)
+{
+ org_virtualbox_VBoxUSBDevice *pThis = (org_virtualbox_VBoxUSBDevice *)pvTarget;
+ if (!pThis)
+ return kIOReturnError;
+
+ switch (enmMsgType)
+ {
+ case kIOMessageServiceIsAttemptingOpen:
+ /* pvMsgArg == the open() fOptions, so we could check for kIOServiceSeize if we care.
+ We'll also get a kIIOServiceRequestingClose message() for that... */
+ Log(("VBoxUSBDevice::MyInterestHandler: kIOMessageServiceIsAttemptingOpen - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
+ pvRefCon, pProvider, pvMsgArg, cbMsgArg));
+ break;
+
+ case kIOMessageServiceWasClosed:
+ Log(("VBoxUSBDevice::MyInterestHandler: kIOMessageServiceWasClosed - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
+ pvRefCon, pProvider, pvMsgArg, cbMsgArg));
+ break;
+
+ case kIOMessageServiceIsTerminated:
+ Log(("VBoxUSBDevice::MyInterestHandler: kIOMessageServiceIsTerminated - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
+ pvRefCon, pProvider, pvMsgArg, cbMsgArg));
+ break;
+
+ case kIOUSBMessagePortHasBeenReset:
+ Log(("VBoxUSBDevice::MyInterestHandler: kIOUSBMessagePortHasBeenReset - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
+ pvRefCon, pProvider, pvMsgArg, cbMsgArg));
+ break;
+
+ default:
+ Log(("VBoxUSBDevice::MyInterestHandler: %#x (%s) - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
+ enmMsgType, DbgGetIOKitMessageName(enmMsgType), pvRefCon, pProvider, pvMsgArg, cbMsgArg));
+ break;
+ }
+
+ return kIOReturnSuccess;
+}
+#endif /* DEBUG */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ *
+ * org_virtualbox_VBoxUSBInterface
+ *
+ */
+
+/**
+ * Initialize our data members.
+ */
+bool
+org_virtualbox_VBoxUSBInterface::init(OSDictionary *pDictionary)
+{
+ uint32_t cInstances = ASMAtomicIncU32(&g_cInstances);
+ Log(("VBoxUSBInterface::init([%p], %p) new g_cInstances=%d\n", this, pDictionary, cInstances));
+ RT_NOREF_PV(cInstances);
+
+ m_pInterface = NULL;
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+
+ return IOUSBUserClientInit::init(pDictionary);
+}
+
+
+/**
+ * Free the object.
+ * @remark Only for logging.
+ */
+void
+org_virtualbox_VBoxUSBInterface::free()
+{
+ uint32_t cInstances = ASMAtomicDecU32(&g_cInstances); NOREF(cInstances);
+ Log(("VBoxUSBInterfaces::free([%p]) new g_cInstances=%d\n", this, cInstances));
+ IOUSBUserClientInit::free();
+}
+
+
+/**
+ * Probe the interface to see if we're the right driver for it.
+ *
+ * We implement this similarly to org_virtualbox_VBoxUSBDevice, except that
+ * we don't bother matching filters but instead just check if the parent is
+ * handled by org_virtualbox_VBoxUSBDevice or not.
+ */
+IOService *
+org_virtualbox_VBoxUSBInterface::probe(IOService *pProvider, SInt32 *pi32Score)
+{
+ Log(("VBoxUSBInterface::probe([%p], %p {%s}, %p={%d})\n", this,
+ pProvider, pProvider->getName(), pi32Score, pi32Score ? *pi32Score : 0));
+
+ /*
+ * Check if VBoxUSBDevice is the parent's driver.
+ */
+ bool fHijackIt = false;
+ const IORegistryPlane *pServicePlane = getPlane(kIOServicePlane);
+ IORegistryEntry *pParent = pProvider->getParentEntry(pServicePlane);
+ if (pParent)
+ {
+ Log(("VBoxUSBInterface::probe: pParent=%p {%s}\n", pParent, pParent->getName()));
+
+ OSIterator *pSiblings = pParent->getChildIterator(pServicePlane);
+ if (pSiblings)
+ {
+ IORegistryEntry *pSibling;
+ while ( (pSibling = OSDynamicCast(IORegistryEntry, pSiblings->getNextObject())) )
+ {
+ const OSMetaClass *pMetaClass = pSibling->getMetaClass();
+ Log2(("sibling: %p - %s - %s\n", pMetaClass, pSibling->getName(), pMetaClass->getClassName()));
+ if (pMetaClass == &org_virtualbox_VBoxUSBDevice::gMetaClass)
+ {
+ fHijackIt = true;
+ break;
+ }
+ }
+ pSiblings->release();
+ }
+ }
+ if (!fHijackIt)
+ {
+ Log(("VBoxUSBInterface::probe: returns NULL\n"));
+ return NULL;
+ }
+
+ /* IOService *pRet = IOUSBUserClientInit::probe(pProvider, pi32Score); - call always returns NULL on 10.11+ */
+ IOService *pRet = this;
+ *pi32Score = _1G;
+ Log(("VBoxUSBInterface::probe: returns %p and *pi32Score=%d - hijack it.\n", pRet, *pi32Score));
+ return pRet;
+}
+
+
+/**
+ * Start the driver (this), retain and open the USB interface object (pProvider).
+ */
+bool
+org_virtualbox_VBoxUSBInterface::start(IOService *pProvider)
+{
+ Log(("VBoxUSBInterface::start([%p], %p {%s})\n", this, pProvider, pProvider->getName()));
+
+ /*
+ * Exploit IOUSBUserClientInit to process IOProviderMergeProperties.
+ */
+ IOUSBUserClientInit::start(pProvider); /* returns false */
+
+ /*
+ * Retain the and open the interface (stop() or message() cleans up).
+ */
+ bool fRc = true;
+ m_pInterface = OSDynamicCast(IOUSBInterface, pProvider);
+ if (m_pInterface)
+ {
+ m_pInterface->retain();
+ m_fOpen = m_pInterface->open(this, kIOServiceSeize, 0);
+ if (!m_fOpen)
+ Log(("VBoxUSBInterface::start: failed to open the interface!\n"));
+ m_fOpenOnWasClosed = !m_fOpen;
+ }
+ else
+ {
+ printf("VBoxUSBInterface::start([%p], %p {%s}): failed!\n", this, pProvider, pProvider->getName());
+ fRc = false;
+ }
+
+ Log(("VBoxUSBInterface::start: returns %d\n", fRc));
+ return fRc;
+}
+
+
+/**
+ * Close and release the USB interface object (pProvider) and stop the driver (this).
+ */
+void
+org_virtualbox_VBoxUSBInterface::stop(IOService *pProvider)
+{
+ Log(("org_virtualbox_VBoxUSBInterface::stop([%p], %p {%s})\n", this, pProvider, pProvider->getName()));
+
+ /*
+ * Close and release the IOUSBInterface if didn't do that already in message().
+ */
+ if (m_pInterface)
+ {
+ /* close it */
+ if (m_fOpen)
+ {
+ m_fOpenOnWasClosed = false;
+ m_fOpen = false;
+ m_pInterface->close(this, 0);
+ }
+
+ /* release it (see start()) */
+ m_pInterface->release();
+ m_pInterface = NULL;
+ }
+
+ IOUSBUserClientInit::stop(pProvider);
+ Log(("VBoxUSBInterface::stop: returns void\n"));
+}
+
+
+/**
+ * Terminate the service (initiate the destruction).
+ * @remark Only for logging.
+ */
+bool
+org_virtualbox_VBoxUSBInterface::terminate(IOOptionBits fOptions)
+{
+ /* kIOServiceRecursing, kIOServiceRequired, kIOServiceTerminate, kIOServiceSynchronous - interesting option bits */
+ Log(("VBoxUSBInterface::terminate([%p], %#x)\n", this, fOptions));
+ return IOUSBUserClientInit::terminate(fOptions);
+}
+
+
+/**
+ * @copydoc org_virtualbox_VBoxUSBDevice::message
+ */
+IOReturn
+org_virtualbox_VBoxUSBInterface::message(UInt32 enmMsg, IOService *pProvider, void *pvArg)
+{
+ Log(("VBoxUSBInterface::message([%p], %#x {%s}, %p {%s}, %p)\n",
+ this, enmMsg, DbgGetIOKitMessageName(enmMsg), pProvider, pProvider->getName(), pvArg));
+
+ IOReturn irc;
+ switch (enmMsg)
+ {
+ /*
+ * See explanation in org_virtualbox_VBoxUSBDevice::message.
+ */
+ case kIOMessageServiceIsRequestingClose:
+ irc = kIOReturnExclusiveAccess;
+ if (!((uintptr_t)pvArg & kIOServiceSeize))
+ {
+ Log(("VBoxUSBInterface::message([%p],%p {%s}, %p) - pid=%d: not seize - closing...\n",
+ this, pProvider, pProvider->getName(), pvArg, RTProcSelf()));
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+ if (m_pInterface)
+ m_pInterface->close(this, 0);
+ irc = kIOReturnSuccess;
+ }
+ else
+ {
+ if (org_virtualbox_VBoxUSBClient::isClientTask(current_task()))
+ {
+ Log(("VBoxUSBInterface::message([%p],%p {%s}, %p) - pid=%d task=%p: client process, closing.\n",
+ this, pProvider, pProvider->getName(), pvArg, RTProcSelf(), current_task()));
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+ if (m_pInterface)
+ m_pInterface->close(this, 0);
+ m_fOpenOnWasClosed = true;
+ irc = kIOReturnSuccess;
+ }
+ else
+ Log(("VBoxUSBInterface::message([%p],%p {%s}, %p) - pid=%d task=%p: not client process!\n",
+ this, pProvider, pProvider->getName(), pvArg, RTProcSelf(), current_task()));
+ }
+ break;
+
+ /*
+ * The service was closed by the current client, check for re-open.
+ */
+ case kIOMessageServiceWasClosed:
+ if (m_pInterface && m_fOpenOnWasClosed)
+ {
+ Log(("VBoxUSBInterface::message: attempting to re-open the interface...\n"));
+ m_fOpenOnWasClosed = false;
+ m_fOpen = m_pInterface->open(this, kIOServiceSeize, 0);
+ if (!m_fOpen)
+ Log(("VBoxUSBInterface::message: failed to open the interface!\n"));
+ m_fOpenOnWasClosed = !m_fOpen;
+ }
+
+ irc = IOUSBUserClientInit::message(enmMsg, pProvider, pvArg);
+ break;
+
+ /*
+ * The IOUSBInterface/Device is shutting down, so close and release.
+ */
+ case kIOMessageServiceIsTerminated:
+ if (m_pInterface)
+ {
+ /* close it */
+ if (m_fOpen)
+ {
+ m_fOpen = false;
+ m_fOpenOnWasClosed = false;
+ m_pInterface->close(this, 0);
+ }
+
+ /* release it (see start()) */
+ m_pInterface->release();
+ m_pInterface = NULL;
+ }
+
+ irc = IOUSBUserClientInit::message(enmMsg, pProvider, pvArg);
+ break;
+
+ default:
+ irc = IOUSBUserClientInit::message(enmMsg, pProvider, pvArg);
+ break;
+ }
+
+ Log(("VBoxUSBInterface::message([%p], %#x {%s}, %p {%s}, %p) -> %#x\n",
+ this, enmMsg, DbgGetIOKitMessageName(enmMsg), pProvider, pProvider->getName(), pvArg, irc));
+ return irc;
+}
+