USBProxyService.h revision dc0a54940789f994c84390cb4a9f03da0b492285
70N/A/* $Id$ */
70N/A/** @file
70N/A * VirtualBox USB Proxy Service (base) class.
70N/A */
493N/A
70N/A/*
70N/A * Copyright (C) 2006-2007 Oracle Corporation
70N/A *
70N/A * This file is part of VirtualBox Open Source Edition (OSE), as
70N/A * available from http://www.virtualbox.org. This file is free software;
70N/A * you can redistribute it and/or modify it under the terms of the GNU
70N/A * General Public License (GPL) as published by the Free Software
70N/A * Foundation, in version 2 as it comes in the "COPYING" file of the
70N/A * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
70N/A * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
70N/A */
70N/A
70N/A
70N/A#ifndef ____H_USBPROXYSERVICE
70N/A#define ____H_USBPROXYSERVICE
70N/A
70N/A#include <VBox/usb.h>
70N/A#include <VBox/usbfilter.h>
70N/A
70N/A#include "VirtualBoxBase.h"
70N/A#include "VirtualBoxImpl.h"
70N/A#include "HostUSBDeviceImpl.h"
70N/Aclass Host;
70N/A
70N/A/**
70N/A * Base class for the USB Proxy service.
70N/A */
493N/Aclass USBProxyService
70N/A : public VirtualBoxTranslatable
70N/A{
70N/Apublic:
493N/A USBProxyService(Host *aHost);
70N/A virtual HRESULT init(void);
70N/A virtual ~USBProxyService();
493N/A
70N/A /**
70N/A * Override of the default locking class to be used for validating lock
70N/A * order with the standard member lock handle.
70N/A */
70N/A virtual VBoxLockingClass getLockingClass() const
70N/A {
70N/A // the USB proxy service uses the Host object lock, so return the
70N/A // same locking class as the host
70N/A return LOCKCLASS_HOSTOBJECT;
70N/A }
493N/A
70N/A bool isActive(void);
70N/A int getLastError(void);
493N/A HRESULT getLastErrorMessage(BSTR *aError);
70N/A
70N/A RWLockHandle *lockHandle() const;
70N/A
70N/A /** @name Host Interfaces
70N/A * @{ */
HRESULT getDeviceCollection(ComSafeArrayOut(IHostUSBDevice *, aUSBDevices));
/** @} */
/** @name SessionMachine Interfaces
* @{ */
HRESULT captureDeviceForVM(SessionMachine *aMachine, IN_GUID aId);
HRESULT detachDeviceFromVM(SessionMachine *aMachine, IN_GUID aId, bool aDone);
HRESULT autoCaptureDevicesForVM(SessionMachine *aMachine);
HRESULT detachAllDevicesFromVM(SessionMachine *aMachine, bool aDone, bool aAbnormal);
/** @} */
/** @name Interface for the USBController and the Host object.
* @{ */
virtual void *insertFilter(PCUSBFILTER aFilter);
virtual void removeFilter(void *aId);
/** @} */
/** @name Interfaces for the HostUSBDevice
* @{ */
virtual int captureDevice(HostUSBDevice *aDevice);
virtual void captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess);
virtual void detachingDevice(HostUSBDevice *aDevice);
virtual int releaseDevice(HostUSBDevice *aDevice);
virtual void releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess);
/** @} */
protected:
int start(void);
int stop(void);
virtual void serviceThreadInit(void);
virtual void serviceThreadTerm(void);
virtual int wait(RTMSINTERVAL aMillies);
virtual int interruptWait(void);
virtual PUSBDEVICE getDevices(void);
virtual void deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice);
virtual void deviceRemoved(ComObjPtr<HostUSBDevice> &aDevice);
virtual void deviceChanged(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList *pllOpenedMachines, SessionMachine *aIgnoreMachine);
bool updateDeviceStateFake(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
virtual bool updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
ComObjPtr<HostUSBDevice> findDeviceById(IN_GUID aId);
static HRESULT setError(HRESULT aResultCode, const char *aText, ...);
static void initFilterFromDevice(PUSBFILTER aFilter, HostUSBDevice *aDevice);
static void freeDeviceMembers(PUSBDEVICE pDevice);
public:
static void freeDevice(PUSBDEVICE pDevice);
private:
HRESULT runAllFiltersOnDevice(ComObjPtr<HostUSBDevice> &aDevice,
SessionMachinesList &llOpenedMachines,
SessionMachine *aIgnoreMachine);
bool runMachineFilters(SessionMachine *aMachine, ComObjPtr<HostUSBDevice> &aDevice);
void processChanges(void);
static DECLCALLBACK(int) serviceThread(RTTHREAD Thread, void *pvUser);
protected:
/** Pointer to the Host object. */
Host *mHost;
/** Thread handle of the service thread. */
RTTHREAD mThread;
/** Flag which stop() sets to cause serviceThread to return. */
bool volatile mTerminate;
/** VBox status code of the last failure.
* (Only used by start(), stop() and the child constructors.) */
int mLastError;
/** Optional error message to complement mLastError. */
Bstr mLastErrorMessage;
/** List of smart HostUSBDevice pointers. */
typedef std::list<ComObjPtr<HostUSBDevice> > HostUSBDeviceList;
/** List of the known USB devices. */
HostUSBDeviceList mDevices;
};
# ifdef RT_OS_DARWIN
# include <VBox/param.h>
# undef PAGE_SHIFT
# undef PAGE_SIZE
# define OSType Carbon_OSType
# include <Carbon/Carbon.h>
# undef OSType
/**
* The Darwin hosted USB Proxy Service.
*/
class USBProxyServiceDarwin : public USBProxyService
{
public:
USBProxyServiceDarwin(Host *aHost);
HRESULT init(void);
~USBProxyServiceDarwin();
#ifdef VBOX_WITH_NEW_USB_CODE_ON_DARWIN
virtual void *insertFilter(PCUSBFILTER aFilter);
virtual void removeFilter(void *aId);
#endif
virtual int captureDevice(HostUSBDevice *aDevice);
virtual void captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess);
virtual void detachingDevice(HostUSBDevice *aDevice);
virtual int releaseDevice(HostUSBDevice *aDevice);
virtual void releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess);
protected:
virtual int wait(RTMSINTERVAL aMillies);
virtual int interruptWait (void);
virtual PUSBDEVICE getDevices (void);
virtual void serviceThreadInit (void);
virtual void serviceThreadTerm (void);
virtual bool updateDeviceState (HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
private:
/** Reference to the runloop of the service thread.
* This is NULL if the service thread isn't running. */
CFRunLoopRef mServiceRunLoopRef;
/** The opaque value returned by DarwinSubscribeUSBNotifications. */
void *mNotifyOpaque;
/** A hack to work around the problem with the usb device enumeration
* not including newly attached devices. */
bool mWaitABitNextTime;
#ifndef VBOX_WITH_NEW_USB_CODE_ON_DARWIN
/** Whether we've got a fake async event and should return without entering the runloop. */
bool volatile mFakeAsync;
#endif
/** Whether we've successfully initialized the USBLib and should call USBLibTerm in the destructor. */
bool mUSBLibInitialized;
};
# endif /* RT_OS_DARWIN */
# ifdef RT_OS_LINUX
# include <stdio.h>
# ifdef VBOX_USB_WITH_SYSFS
# include <HostHardwareLinux.h>
# endif
/**
* The Linux hosted USB Proxy Service.
*/
class USBProxyServiceLinux
: public USBProxyService
{
public:
USBProxyServiceLinux(Host *aHost);
HRESULT init(void);
~USBProxyServiceLinux();
virtual int captureDevice(HostUSBDevice *aDevice);
virtual int releaseDevice(HostUSBDevice *aDevice);
# ifdef UNIT_TEST
/* Functions for setting our unit test mock functions. Not quite sure if
* it is good form to mix test and production code like this, but it seems
* cleaner to me than tying the unit test to implementation details of the
* class. */
/** Select which access methods will be available to the @a init method
* during unit testing, and (hack!) what return code it will see from
* the access method-specific initialisation. */
void testSetupInit(const char *pcszUsbfsRoot, bool fUsbfsAccessible,
const char *pcszDevicesRoot, bool fDevicesAccessible,
int rcMethodInitResult)
{
mpcszTestUsbfsRoot = pcszUsbfsRoot;
mfTestUsbfsAccessible = fUsbfsAccessible;
mpcszTestDevicesRoot = pcszDevicesRoot;
mfTestDevicesAccessible = fDevicesAccessible;
mrcTestMethodInitResult = rcMethodInitResult;
}
/** Specify the environment that the @a init method will see during unit
* testing. */
void testSetEnv(const char *pcszEnvUsb, const char *pcszEnvUsbRoot)
{
mpcszTestEnvUsb = pcszEnvUsb;
mpcszTestEnvUsbRoot = pcszEnvUsbRoot;
}
bool testGetUsingUsbfs(void) { return mUsingUsbfsDevices; }
const char *testGetDevicesRoot(void) { return mDevicesRoot.c_str(); }
# endif
protected:
int initUsbfs(void);
int initSysfs(void);
void doUsbfsCleanupAsNeeded(void);
virtual int wait(RTMSINTERVAL aMillies);
virtual int interruptWait(void);
virtual PUSBDEVICE getDevices(void);
virtual void deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice);
virtual bool updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
private:
int waitUsbfs(RTMSINTERVAL aMillies);
int waitSysfs(RTMSINTERVAL aMillies);
private:
/** File handle to the '/proc/bus/usb/devices' file. */
RTFILE mhFile;
/** Pipe used to interrupt wait(), the read end. */
RTPIPE mhWakeupPipeR;
/** Pipe used to interrupt wait(), the write end. */
RTPIPE mhWakeupPipeW;
/** The root of usbfs. */
Utf8Str mDevicesRoot;
/** Whether we're using <mUsbfsRoot>/devices or /sys/whatever. */
bool mUsingUsbfsDevices;
/** Number of 500ms polls left to do. See usbDeterminState for details. */
unsigned mUdevPolls;
# ifdef VBOX_USB_WITH_SYSFS
/** Object used for polling for hotplug events from hal. */
VBoxMainHotplugWaiter *mpWaiter;
# endif
# ifdef UNIT_TEST
/** The path we pretend the usbfs root is located at, or NULL. */
const char *mpcszTestUsbfsRoot;
/** Should usbfs be accessible to the current user? */
bool mfTestUsbfsAccessible;
/** The path we pretend the device node tree root is located at, or NULL. */
const char *mpcszTestDevicesRoot;
/** Should the device node tree be accessible to the current user? */
bool mfTestDevicesAccessible;
/** The result of the usbfs/inotify-specific init */
int mrcTestMethodInitResult;
/** The value of the VBOX_USB environment variable. */
const char *mpcszTestEnvUsb;
/** The value of the VBOX_USB_ROOT environment variable. */
const char *mpcszTestEnvUsbRoot;
# endif
};
# endif /* RT_OS_LINUX */
# ifdef RT_OS_OS2
# include <usbcalls.h>
/**
* The Linux hosted USB Proxy Service.
*/
class USBProxyServiceOs2 : public USBProxyService
{
public:
USBProxyServiceOs2 (Host *aHost);
/// @todo virtual HRESULT init(void);
~USBProxyServiceOs2();
virtual int captureDevice (HostUSBDevice *aDevice);
virtual int releaseDevice (HostUSBDevice *aDevice);
protected:
virtual int wait(RTMSINTERVAL aMillies);
virtual int interruptWait(void);
virtual PUSBDEVICE getDevices(void);
int addDeviceToChain(PUSBDEVICE pDev, PUSBDEVICE *ppFirst, PUSBDEVICE **pppNext, int rc);
virtual bool updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
private:
/** The notification event semaphore */
HEV mhev;
/** The notification id. */
USBNOTIFY mNotifyId;
/** The usbcalls.dll handle. */
HMODULE mhmod;
/** UsbRegisterChangeNotification */
APIRET (APIENTRY *mpfnUsbRegisterChangeNotification)(PUSBNOTIFY, HEV, HEV);
/** UsbDeregisterNotification */
APIRET (APIENTRY *mpfnUsbDeregisterNotification)(USBNOTIFY);
/** UsbQueryNumberDevices */
APIRET (APIENTRY *mpfnUsbQueryNumberDevices)(PULONG);
/** UsbQueryDeviceReport */
APIRET (APIENTRY *mpfnUsbQueryDeviceReport)(ULONG, PULONG, PVOID);
};
# endif /* RT_OS_LINUX */
# ifdef RT_OS_SOLARIS
# include <libdevinfo.h>
/**
* The Solaris hosted USB Proxy Service.
*/
class USBProxyServiceSolaris : public USBProxyService
{
public:
USBProxyServiceSolaris(Host *aHost);
HRESULT init(void);
~USBProxyServiceSolaris();
virtual void *insertFilter (PCUSBFILTER aFilter);
virtual void removeFilter (void *aID);
virtual int captureDevice (HostUSBDevice *aDevice);
virtual int releaseDevice (HostUSBDevice *aDevice);
virtual void captureDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess);
virtual void releaseDeviceCompleted(HostUSBDevice *aDevice, bool aSuccess);
protected:
virtual int wait(RTMSINTERVAL aMillies);
virtual int interruptWait(void);
virtual PUSBDEVICE getDevices(void);
virtual bool updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
private:
RTSEMEVENT mNotifyEventSem;
/** Whether we've successfully initialized the USBLib and should call USBLibTerm in the destructor. */
bool mUSBLibInitialized;
};
#endif /* RT_OS_SOLARIS */
# ifdef RT_OS_WINDOWS
/**
* The Windows hosted USB Proxy Service.
*/
class USBProxyServiceWindows : public USBProxyService
{
public:
USBProxyServiceWindows(Host *aHost);
HRESULT init(void);
~USBProxyServiceWindows();
virtual void *insertFilter (PCUSBFILTER aFilter);
virtual void removeFilter (void *aID);
virtual int captureDevice (HostUSBDevice *aDevice);
virtual int releaseDevice (HostUSBDevice *aDevice);
protected:
virtual int wait(RTMSINTERVAL aMillies);
virtual int interruptWait(void);
virtual PUSBDEVICE getDevices(void);
virtual bool updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
private:
HANDLE mhEventInterrupt;
};
# endif /* RT_OS_WINDOWS */
# ifdef RT_OS_FREEBSD
/**
* The FreeBSD hosted USB Proxy Service.
*/
class USBProxyServiceFreeBSD : public USBProxyService
{
public:
USBProxyServiceFreeBSD(Host *aHost);
HRESULT init(void);
~USBProxyServiceFreeBSD();
virtual int captureDevice(HostUSBDevice *aDevice);
virtual int releaseDevice(HostUSBDevice *aDevice);
protected:
int initUsbfs(void);
int initSysfs(void);
virtual int wait(RTMSINTERVAL aMillies);
virtual int interruptWait(void);
virtual PUSBDEVICE getDevices(void);
int addDeviceToChain(PUSBDEVICE pDev, PUSBDEVICE *ppFirst, PUSBDEVICE **pppNext, int rc);
virtual void deviceAdded(ComObjPtr<HostUSBDevice> &aDevice, SessionMachinesList &llOpenedMachines, PUSBDEVICE aUSBDevice);
virtual bool updateDeviceState(HostUSBDevice *aDevice, PUSBDEVICE aUSBDevice, bool *aRunFilters, SessionMachine **aIgnoreMachine);
private:
RTSEMEVENT mNotifyEventSem;
};
# endif /* RT_OS_FREEBSD */
#endif /* !____H_USBPROXYSERVICE */
/* vi: set tabstop=4 shiftwidth=4 expandtab: */