VBoxUSB.cpp revision 59190ecd61435d19ba3515b876272aee7bd12298
/* $Id$ */
/** @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 arbriating
* access to hijacked USB devices.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* Oracle Corporation confidential
* All rights reserved
*/
/*******************************************************************************
* 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 "VBoxUSBInterface.h"
#include "VBoxUSBFilterMgr.h"
#include <VBox/usblib-darwin.h>
#include <iprt/initterm.h>
#include <iprt/semaphore.h>
#include <IOKit/IOService.h>
#include <IOKit/IOUserClient.h>
#include <IOKit/IOKitKeys.h>
/* private: */
extern void *get_bsdtask_info(task_t);
/*******************************************************************************
* Defined Constants And Macros *
*******************************************************************************/
/** Locks the lists. */
/** Unlocks the lists. */
/*******************************************************************************
* Internal Functions *
*******************************************************************************/
/*******************************************************************************
* 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
{
public:
/** @name IOService
* @{ */
virtual void free();
/** @} */
};
/**
* The user client class that pairs up with org_virtualbox_VBoxUSB.
*/
class org_virtualbox_VBoxUSBClient : public IOUserClient
{
public:
/** @name IOService & IOUserClient
* @{ */
virtual IOReturn clientClose(void);
virtual IOReturn clientDied(void);
virtual void free();
/** @} */
/** @name User client methods
* @{ */
IOReturn addFilter(PUSBFILTER pFilter, PVBOXUSBADDFILTEROUT pOut, IOByteCount cbFilter, IOByteCount *pcbOut);
/** @} */
private:
/** The service provider. */
/** The client task. */
/** The client 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;
};
/**
* 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
{
public:
/** @name IOService
* @{ */
virtual void free();
/** @} */
private:
/** The interface we're driving (aka. the provider). */
/** The owner process, meaning the VBoxSVC process. */
/** The client process, meaning the VM process. */
/** The ID of the matching filter. */
/** 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. */
#endif
};
/**
* 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
{
public:
/** @name IOService
* @{ */
virtual void free();
/** @} */
private:
/** The interface we're driving (aka. the provider). */
/** 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;
};
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/*
* Declare the module stuff.
*/
/** Mutex protecting the lists. */
/** Global instance count - just for checking prooving that everything is destroyed correctly. */
static volatile uint32_t g_cInstances = 0;
/**
* Start the kernel module.
*/
{
int rc;
Log(("VBoxUSBStart\n"));
/*
* Initialize IPRT.
*/
if (RT_SUCCESS(rc))
{
/*
* Create the spinlock.
*/
if (RT_SUCCESS(rc))
{
rc = VBoxUSBFilterInit();
if (RT_SUCCESS(rc))
{
#if 0 /* testing */
#endif
return KMOD_RETURN_SUCCESS;
}
}
else
RTR0Term();
}
else
return KMOD_RETURN_FAILURE;
}
/**
* Stop the kernel module.
*/
{
int rc;
/** @todo Fix problem with crashing when unloading a driver that's in use. */
/*
* Undo the work done during start (in reverse order).
*/
RTR0Term();
Log(("VBoxUSBStop - done\n"));
return KMOD_RETURN_SUCCESS;
}
/**
* Gets the name of a IOKit message.
*
* @returns Message name (read only).
* @param enmMsg The message.
*/
{
#ifdef DEBUG
switch (enmMsg)
{
// MY_CASE(kIOUSBMessageCompositeDriverReconfigured);
}
#endif /* DEBUG */
return "unknown";
}
/*
*
* org_virtualbox_VBoxUSB
*
*/
/**
* Initialize the object.
* @remark Only for logging.
*/
bool
{
{
/* init members. */
return true;
}
return false;
}
/**
* Free the object.
* @remark Only for logging.
*/
void
{
}
/**
* Start this service.
*/
bool
{
{
/* register the service. */
return true;
}
return false;
}
/**
* Stop this service.
* @remark Only for logging.
*/
void
{
}
/**
* Stop this service.
* @remark Only for logging.
*/
bool
org_virtualbox_VBoxUSB::open(IOService *pForClient, IOOptionBits fOptions/* = 0*/, void *pvArg/* = 0*/)
{
return fRc;
}
/**
* Stop this service.
* @remark Only for logging.
*/
void
{
}
/**
* Terminate request.
* @remark Only for logging.
*/
bool
{
return fRc;
}
/*
*
* org_virtualbox_VBoxUSBClient
*
*/
/**
* Initializer called when the client opens the service.
*/
bool
{
if (!OwningTask)
{
Log(("VBoxUSBClient::initWithTask([%p], %p, %p, %#x) -> false (no task)\n", this, OwningTask, pvSecurityId, u32Type));
return false;
}
Log(("VBoxUSBClient::initWithTask([%p], %p(->%p:{.pid=%d}, %p, %#x)\n",
{
m_pProvider = NULL;
m_Task = OwningTask;
Log(("VBoxUSBClient::initWithTask([%p], %p(->%p:{.pid=%d}, %p, %#x) -> true; new g_cInstances=%d\n",
return true;
}
Log(("VBoxUSBClient::initWithTask([%p], %p(->%p:{.pid=%d}, %p, %#x) -> false\n",
return false;
}
/**
* Free the object.
* @remark Only for logging.
*/
void
{
}
/**
* Start the client service.
*/
bool
{
{
if (m_pProvider)
{
/*
* Add ourselves to the list of user clients.
*/
VBOXUSB_LOCK();
s_pHead = this;
return true;
}
}
return false;
}
/**
* Client exits normally.
*/
{
/*
* Remove this process from the client list.
*/
VBOXUSB_LOCK();
{
if (pCur == this)
{
if (pPrev)
else
break;
}
}
/*
* Drop all filters owned by this client.
*/
if (m_Process != NIL_RTPROCESS)
/*
* Schedule all devices owned (filtered) by this process for
* immediate release or release upon close.
*/
if (m_Process != NIL_RTPROCESS)
/*
* Initiate termination.
*/
m_pProvider = NULL;
terminate();
return kIOReturnSuccess;
}
/**
* The client exits abnormally / forgets to do cleanups.
* @remark Only for logging.
*/
{
Log(("VBoxUSBClient::clientDied([%p]) m_Task=%p R0Process=%p Process=%d\n",
/* IOUserClient::clientDied() calls clientClose... */
return IOUserClient::clientDied();
}
/**
* Terminate the service (initiate the destruction).
* @remark Only for logging.
*/
bool
{
/* kIOServiceRecursing, kIOServiceRequired, kIOServiceTerminate, kIOServiceSynchronous - interesting option bits */
}
/**
* The final stage of the client service destruction.
* @remark Only for logging.
*/
bool
{
}
/**
* Stop the client service.
*/
void
{
Log(("VBoxUSBClient::stop([%p])\n", this));
/*
* Paranoia.
*/
VBOXUSB_LOCK();
{
if (pCur == this)
{
if (pPrev)
else
break;
}
}
}
/**
* 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.
*/
{
{
/*[VBOXUSBMETHOD_ADD_FILTER] = */
{
(IOService *)0, /* object */
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 */
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) */
},
};
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.
*/
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.
*/
{
printf("VBoxUSBClient::addFilter: cbFilter=%#x expected %#x; *pcbOut=%#x expected %#x\n",
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",
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.
*/
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).
*/
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.
*/
{
printf("VBoxUSBClient::removeFilter: cbIn=%#x expected %#x; *pcbOut=%#x expected %#x\n",
return kIOReturnBadArgument;
}
/*
* Remove the filter.
*/
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
{
VBOXUSB_LOCK();
{
return true;
}
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
{
m_fOpen = false;
m_fOpenOnWasClosed = false;
m_fReleaseOnClose = false;
m_fBeingUnloaded = false;
#ifdef DEBUG
m_pNotifier = NULL;
#endif
}
/**
* Free the object.
* @remark Only for logging.
*/
void
{
}
/**
*
* 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 necssary..., 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.
*/
{
Log(("VBoxUSBDevice::probe([%p], %p {%s}, %p={%d})\n", this,
/*
* Check against filters.
*/
static const struct
{
const char *pszName;
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++)
{
if (!pObj)
continue;
{
if (pNum)
{
Log2(("VBoxUSBDevice::probe: %d/%s - %#x (32bit=%#x)\n", i, s_aProps[i].pszName, u16, pNum->unsigned32BitValue()));
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
}
else
{
if (pStr)
{
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
}
}
/** @todo try figure the blasted bus number */
/*
* Run filters on it.
*/
if (Owner == NIL_RTPROCESS)
{
return NULL;
}
/*
* It matched. Save the owner in the provider registry (hope that works).
*/
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
{
Log(("VBoxUSBDevice::start([%p:{.m_Owner=%d, .m_uId=%p}], %p {%s})\n",
if (!m_pDevice)
{
return false;
}
#ifdef DEBUG
/* for some extra log messages */
this, /* pvTarget */
NULL); /* pvRefCon */
#endif
/*
* Exploit IOUSBUserClientInit to process IOProviderMergeProperties.
*/
/*
* Link ourselves into the list of hijacked device.
*/
VBOXUSB_LOCK();
s_pHead = this;
/*
* Set the VBoxUSB properties.
*/
if (!setProperty(VBOXUSB_CLIENT_KEY, (unsigned long long)m_Client, sizeof(m_Client) * 8 /* bits */))
/*
* Retain and open the device.
*/
if (!m_fOpen)
Log(("VBoxUSBDevice::start: failed to open the device!\n"));
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
{
/*
* Remove ourselves from the list of device.
*/
VBOXUSB_LOCK();
{
if (pCur == this)
{
if (pPrev)
else
break;
}
}
/*
* Should we release the device?
*/
if (m_fBeingUnloaded)
{
if (m_pDevice)
{
Log(("VBoxUSBDevice::stop([%p], %p {%s}): m_pDevice=%p unload & ReEnumerateDevice -> %#x\n",
}
else
{
if (pDevice)
{
Log(("VBoxUSBDevice::stop([%p], %p {%s}): pDevice=%p unload & ReEnumerateDevice -> %#x\n",
}
else
Log(("VBoxUSBDevice::stop([%p], %p {%s}): failed to cast provider to IOUSBDevice\n",
}
}
else if (m_fReleaseOnClose)
{
ASMAtomicWriteBool(&m_fReleaseOnClose, false);
if (m_pDevice)
{
Log(("VBoxUSBDevice::stop([%p], %p {%s}): m_pDevice=%p close & ReEnumerateDevice -> %#x\n",
}
}
/*
* 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;
}
/* release it (see start()) */
}
#ifdef DEBUG
/* avoid crashing on unload. */
if (m_pNotifier)
{
m_pNotifier->release();
m_pNotifier = NULL;
}
#endif
Log(("VBoxUSBDevice::stop: returns void\n"));
}
/**
* Terminate the service (initiate the destruction).
* @remark Only for logging.
*/
bool
{
/* kIOServiceRecursing, kIOServiceRequired, kIOServiceTerminate, kIOServiceSynchronous - interesting option bits */
/*
* 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 deteached (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. */
m_fBeingUnloaded = true;
}
/**
* 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.
*/
{
Log(("VBoxUSBDevice::message([%p], %#x {%s}, %p {%s}, %p) - pid=%d\n",
this, enmMsg, DbgGetIOKitMessageName(enmMsg), pProvider, pProvider->getName(), pvArg, RTProcSelf()));
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 IOSerivce::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
* We'll ASSUME this'll remain like this for now...
*/
/* 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. */
{
Log(("VBoxUSBDevice::message([%p],%p {%s}, %p) - pid=%d: not seize - closing...\n",
m_fOpen = false;
m_fOpenOnWasClosed = false;
if (m_pDevice)
}
else
{
{
Log(("VBoxUSBDevice::message([%p],%p {%s}, %p) - pid=%d task=%p: client process, closing.\n",
m_fOpen = false;
m_fOpenOnWasClosed = false;
if (m_pDevice)
m_fOpenOnWasClosed = true;
m_Client = RTProcSelf();
}
else
Log(("VBoxUSBDevice::message([%p],%p {%s}, %p) - pid=%d task=%p: not client process!\n",
}
if (!setProperty(VBOXUSB_CLIENT_KEY, (unsigned long long)m_Client, sizeof(m_Client) * 8 /* bits */))
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.)
*/
/*
* Update the client property value.
*/
if (m_Client != NIL_RTPROCESS)
{
if (!setProperty(VBOXUSB_CLIENT_KEY, (unsigned long long)m_Client, sizeof(m_Client) * 8 /* bits */))
}
if (m_pDevice)
{
/*
* Should we release the device?
*/
if (ASMAtomicXchgBool(&m_fReleaseOnClose, false))
{
m_fOpenOnWasClosed = false;
Log(("VBoxUSBDevice::message([%p], %p {%s}) - ReEnumerateDevice() -> %#x\n",
}
/*
* 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;
if (!m_fOpen)
Log(("VBoxUSBDevice::message: failed to open the device!\n"));
}
}
break;
/*
* The IOUSBDevice is shutting down, so close it if we've opened it.
*/
m_fBeingUnloaded = false;
ASMAtomicWriteBool(&m_fReleaseOnClose, false);
if (m_pDevice)
{
/* close it */
if (m_fOpen)
{
m_fOpen = false;
m_fOpenOnWasClosed = false;
}
/* release it (see start()) */
}
break;
default:
break;
}
Log(("VBoxUSBDevice::message([%p], %#x {%s}, %p {%s}, %p) -> %#x\n",
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
{
/*
* 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();
do
{
{
Log2(("VBoxUSBDevice::scheduleReleaseByOwner: pCur=%p m_Owner=%d (%s) m_fReleaseOnClose=%d\n",
{
/* make sure we won't hit it again. */
if ( pDevice
&& !pCur->m_fReleaseOnClose)
{
pCur->m_fOpenOnWasClosed = false;
{
/* It's currently open, so just schedule it for re-enumeration on close. */
Log(("VBoxUSBDevice::scheduleReleaseByOwner: %p {%s} - used by %d\n",
}
else
{
/*
* Get the USBDevice object and do the re-enumeration now.
* Retain the device so we don't run into any trouble.
*/
Log(("VBoxUSBDevice::scheduleReleaseByOwner: %p {%s} - ReEnumerateDevice -> %#x\n",
VBOXUSB_LOCK();
break;
}
}
}
}
} while (pCur);
}
#ifdef DEBUG
/*static*/ IOReturn
{
if (!pThis)
return kIOReturnError;
switch (enmMsgType)
{
/* 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",
break;
Log(("VBoxUSBDevice::MyInterestHandler: kIOMessageServiceWasClosed - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
break;
Log(("VBoxUSBDevice::MyInterestHandler: kIOMessageServiceIsTerminated - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
break;
Log(("VBoxUSBDevice::MyInterestHandler: kIOUSBMessagePortHasBeenReset - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
break;
default:
Log(("VBoxUSBDevice::MyInterestHandler: %#x (%s) - pvRefCon=%p pProvider=%p pvMsgArg=%p cbMsgArg=%d\n",
break;
}
return kIOReturnSuccess;
}
#endif /* DEBUG */
/*
*
* org_virtualbox_VBoxUSBInterface
*
*/
/**
* Initialize our data members.
*/
bool
{
m_pInterface = NULL;
m_fOpen = false;
m_fOpenOnWasClosed = false;
}
/**
* Free the object.
* @remark Only for logging.
*/
void
{
}
/**
* 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.
*/
{
Log(("VBoxUSBInterface::probe([%p], %p {%s}, %p={%d})\n", this,
/*
* Check if VBoxUSBDevice is the parent's driver.
*/
bool fHijackIt = false;
if (pParent)
{
if (pSiblings)
{
{
{
fHijackIt = true;
break;
}
}
}
}
if (!fHijackIt)
{
Log(("VBoxUSBInterface::probe: returns NULL\n"));
return NULL;
}
return pRet;
}
/**
* Start the driver (this), retain and open the USB interface object (pProvider).
*/
bool
{
/*
* Exploit IOUSBUserClientInit to process IOProviderMergeProperties.
*/
/*
* Retain the and open the interface (stop() or message() cleans up).
*/
bool fRc = true;
if (m_pInterface)
{
m_pInterface->retain();
if (!m_fOpen)
Log(("VBoxUSBInterface::start: failed to open the interface!\n"));
}
else
{
fRc = false;
}
return fRc;
}
/**
* Close and release the USB interface object (pProvider) and stop the driver (this).
*/
void
{
Log(("org_virtualbox_VBoxUSBInterface::stop([%p], %p {%s})\n", this, pProvider, pProvider->getName()));
/*
* Close and relase 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;
}
Log(("VBoxUSBInterface::stop: returns void\n"));
}
/**
* Terminate the service (initiate the destruction).
* @remark Only for logging.
*/
bool
{
/* kIOServiceRecursing, kIOServiceRequired, kIOServiceTerminate, kIOServiceSynchronous - interesting option bits */
}
/**
* @copydoc org_virtualbox_VBoxUSBDevice::message
*/
{
Log(("VBoxUSBInterface::message([%p], %#x {%s}, %p {%s}, %p)\n",
switch (enmMsg)
{
/*
* See explanation in org_virtualbox_VBoxUSBDevice::message.
*/
{
Log(("VBoxUSBInterface::message([%p],%p {%s}, %p) - pid=%d: not seize - closing...\n",
m_fOpen = false;
m_fOpenOnWasClosed = false;
if (m_pInterface)
m_pInterface->close(this, 0);
}
else
{
{
Log(("VBoxUSBInterface::message([%p],%p {%s}, %p) - pid=%d task=%p: client process, closing.\n",
m_fOpen = false;
m_fOpenOnWasClosed = false;
if (m_pInterface)
m_pInterface->close(this, 0);
m_fOpenOnWasClosed = true;
}
else
Log(("VBoxUSBInterface::message([%p],%p {%s}, %p) - pid=%d task=%p: not client process!\n",
}
break;
/*
* The service was closed by the current client, check for re-open.
*/
if (m_pInterface && m_fOpenOnWasClosed)
{
Log(("VBoxUSBInterface::message: attempting to re-open the interface...\n"));
m_fOpenOnWasClosed = false;
if (!m_fOpen)
Log(("VBoxUSBInterface::message: failed to open the interface!\n"));
}
break;
/*
* The IOUSBInterface/Device is shutting down, so close and release.
*/
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;
}
break;
default:
break;
}
Log(("VBoxUSBInterface::message([%p], %#x {%s}, %p {%s}, %p) -> %#x\n",
return irc;
}