diff options
Diffstat (limited to 'LibOVR')
-rw-r--r-- | LibOVR/90-oculus.rules | 2 | ||||
-rw-r--r-- | LibOVR/Makefile | 278 | ||||
-rw-r--r-- | LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp | 787 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_DeviceManager.cpp | 331 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_DeviceManager.h | 122 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HIDDevice.cpp | 819 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HIDDevice.h | 135 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HMDDevice.cpp | 291 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_HMDDevice.h | 154 | ||||
-rw-r--r-- | LibOVR/Src/OVR_Linux_SensorDevice.cpp | 57 |
10 files changed, 2976 insertions, 0 deletions
diff --git a/LibOVR/90-oculus.rules b/LibOVR/90-oculus.rules new file mode 100644 index 0000000..56bccdb --- /dev/null +++ b/LibOVR/90-oculus.rules @@ -0,0 +1,2 @@ +# Oculus HID Sensor naming and permissioning +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="2833", MODE="0666" diff --git a/LibOVR/Makefile b/LibOVR/Makefile new file mode 100644 index 0000000..f939cec --- /dev/null +++ b/LibOVR/Makefile @@ -0,0 +1,278 @@ +############################################################################# +# +# Filename : Makefile +# Content : Makefile for building linux version of: libovr +# Created : 2013 +# Authors : Simon Hallam and Peter Giokaris +# Copyright : Copyright 2013 OculusVR, Inc. All Rights Reserved +# Instruction : The g++ compiler and stdndard lib packages need to be +# installed on the system. Navigate in a shell to the +# directory where this Makefile is located and enter: +# +# make builds the release version for the +# current architechture +# make clean delete intermediate release object files +# and the library file +# make DEBUG=1 builds the debug version for the current +# architechture +# make clean DEBUG=1 deletes intermediate debug object files +# and the library file +# +# Output : Relative to the directory this Makefile lives in, libraries +# are built at the following locations depending upon the +# architechture of the system you are running: +# +# ./Lib/Linux/Debug/i386/libovr.a +# ./Lib/Linux/Debug/x86_64/libovr.a +# ./Lib/Linux/Release/i386/libovr.a +# ./Lib/Linux/Release/x86_64/libovr.a +# +############################################################################# + +####### Detect system architecture + +SYSARCH = i386 +ifeq ($(shell uname -m),x86_64) +SYSARCH = x86_64 +endif + +####### Compiler, tools and options + +CXX = g++ +LINK = ar rvs +DELETEFILE = rm -f + +####### Detect debug or release + +DEBUG = 0 +ifeq ($(DEBUG), 1) + CXXFLAGS = -pipe -fPIC -DDEBUG -DOVR_BUILD_DEBUG -g + RELEASETYPE = Debug +else + CXXFLAGS = -pipe -fPIC -O2 + RELEASETYPE = Release +endif + +####### Paths + +LIBOVRPATH = . +3RDPARTYPATH = ../3rdParty +INCPATH = -I. -I.. -I$(LIBOVRPATH)/Include -I$(LIBOVRPATH)/Src +OBJPATH = ./Obj/Linux/$(RELEASETYPE)/$(SYSARCH) +CXXBUILD = $(CXX) -c $(CXXFLAGS) $(INCPATH) -o $(OBJPATH)/ + +####### Files + +TARGET = ./Lib/Linux/$(RELEASETYPE)/$(SYSARCH)/libovr.a + +OBJECTS = $(OBJPATH)/OVR_CAPI.o \ + $(OBJPATH)/CAPI_DistortionRenderer.o \ + $(OBJPATH)/CAPI_GL_DistortionRenderer.o \ + $(OBJPATH)/CAPI_GL_Util.o \ + $(OBJPATH)/CAPI_FrameTimeManager.o \ + $(OBJPATH)/CAPI_GlobalState.o \ + $(OBJPATH)/CAPI_HMDRenderState.o \ + $(OBJPATH)/CAPI_HMDState.o \ + $(OBJPATH)/OVR_DeviceHandle.o \ + $(OBJPATH)/OVR_DeviceImpl.o \ + $(OBJPATH)/OVR_JSON.o \ + $(OBJPATH)/OVR_LatencyTestImpl.o \ + $(OBJPATH)/OVR_Profile.o \ + $(OBJPATH)/OVR_Linux_SensorDevice.o\ + $(OBJPATH)/OVR_SensorCalibration.o\ + $(OBJPATH)/OVR_SensorFilter.o\ + $(OBJPATH)/OVR_SensorFusion.o\ + $(OBJPATH)/OVR_SensorImpl.o \ + $(OBJPATH)/OVR_Sensor2Impl.o \ + $(OBJPATH)/OVR_SensorImpl_Common.o \ + $(OBJPATH)/OVR_SensorTimeFilter.o \ + $(OBJPATH)/OVR_Stereo.o \ + $(OBJPATH)/OVR_ThreadCommandQueue.o \ + $(OBJPATH)/OVR_Alg.o \ + $(OBJPATH)/OVR_Allocator.o \ + $(OBJPATH)/OVR_Atomic.o \ + $(OBJPATH)/OVR_File.o \ + $(OBJPATH)/OVR_FileFILE.o \ + $(OBJPATH)/OVR_Log.o \ + $(OBJPATH)/OVR_Math.o \ + $(OBJPATH)/OVR_Recording.o \ + $(OBJPATH)/OVR_RefCount.o \ + $(OBJPATH)/OVR_Std.o \ + $(OBJPATH)/OVR_String.o \ + $(OBJPATH)/OVR_String_FormatUtil.o \ + $(OBJPATH)/OVR_String_PathUtil.o \ + $(OBJPATH)/OVR_SysFile.o \ + $(OBJPATH)/OVR_System.o \ + $(OBJPATH)/OVR_Timer.o \ + $(OBJPATH)/OVR_UTF8Util.o \ + $(OBJPATH)/Util_LatencyTest.o \ + $(OBJPATH)/Util_LatencyTest2.o \ + $(OBJPATH)/Util_Render_Stereo.o \ + $(OBJPATH)/OVR_ThreadsPthread.o \ + $(OBJPATH)/OVR_Linux_HIDDevice.o \ + $(OBJPATH)/OVR_Linux_SensorDevice.o \ + $(OBJPATH)/OVR_Linux_DeviceManager.o \ + $(OBJPATH)/OVR_Linux_HMDDevice.o \ + $(OBJPATH)/edid.o + +####### Rules + +all: $(TARGET) + +$(TARGET): $(OBJECTS) + $(LINK) $(TARGET) $(OBJECTS) + +$(OBJPATH)/OVR_CAPI.o: $(LIBOVRPATH)/Src/OVR_CAPI.cpp + $(CXXBUILD)OVR_CAPI.o $(LIBOVRPATH)/Src/OVR_CAPI.cpp + +$(OBJPATH)/CAPI_DistortionRenderer.o: $(LIBOVRPATH)/Src/CAPI/CAPI_DistortionRenderer.cpp + $(CXXBUILD)CAPI_DistortionRenderer.o $(LIBOVRPATH)/Src/CAPI/CAPI_DistortionRenderer.cpp + +$(OBJPATH)/CAPI_GL_DistortionRenderer.o: $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp + $(CXXBUILD)CAPI_GL_DistortionRenderer.o $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_DistortionRenderer.cpp + +$(OBJPATH)/CAPI_GL_Util.o: $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_Util.cpp + $(CXXBUILD)CAPI_GL_Util.o $(LIBOVRPATH)/Src/CAPI/GL/CAPI_GL_Util.cpp + +$(OBJPATH)/CAPI_FrameTimeManager.o: $(LIBOVRPATH)/Src/CAPI/CAPI_FrameTimeManager.cpp + $(CXXBUILD)CAPI_FrameTimeManager.o $(LIBOVRPATH)/Src/CAPI/CAPI_FrameTimeManager.cpp + +$(OBJPATH)/CAPI_GlobalState.o: $(LIBOVRPATH)/Src/CAPI/CAPI_GlobalState.cpp + $(CXXBUILD)CAPI_GlobalState.o $(LIBOVRPATH)/Src/CAPI/CAPI_GlobalState.cpp + +$(OBJPATH)/CAPI_HMDRenderState.o: $(LIBOVRPATH)/Src/CAPI/CAPI_HMDRenderState.cpp + $(CXXBUILD)CAPI_HMDRenderState.o $(LIBOVRPATH)/Src/CAPI/CAPI_HMDRenderState.cpp + +$(OBJPATH)/CAPI_HMDState.o: $(LIBOVRPATH)/Src/CAPI/CAPI_HMDState.cpp + $(CXXBUILD)CAPI_HMDState.o $(LIBOVRPATH)/Src/CAPI/CAPI_HMDState.cpp + +$(OBJPATH)/OVR_DeviceHandle.o: $(LIBOVRPATH)/Src/OVR_DeviceHandle.cpp + $(CXXBUILD)OVR_DeviceHandle.o $(LIBOVRPATH)/Src/OVR_DeviceHandle.cpp + +$(OBJPATH)/OVR_DeviceImpl.o: $(LIBOVRPATH)/Src/OVR_DeviceImpl.cpp + $(CXXBUILD)OVR_DeviceImpl.o $(LIBOVRPATH)/Src/OVR_DeviceImpl.cpp + +$(OBJPATH)/OVR_JSON.o: $(LIBOVRPATH)/Src/OVR_JSON.cpp + $(CXXBUILD)OVR_JSON.o $(LIBOVRPATH)/Src/OVR_JSON.cpp + +$(OBJPATH)/OVR_LatencyTestImpl.o: $(LIBOVRPATH)/Src/OVR_LatencyTestImpl.cpp + $(CXXBUILD)OVR_LatencyTestImpl.o $(LIBOVRPATH)/Src/OVR_LatencyTestImpl.cpp + +$(OBJPATH)/OVR_Profile.o: $(LIBOVRPATH)/Src/OVR_Profile.cpp + $(CXXBUILD)OVR_Profile.o $(LIBOVRPATH)/Src/OVR_Profile.cpp + +$(OBJPATH)/OVR_SensorDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp + $(CXXBUILD)OVR_Linux_SensorDevice.o $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp + +$(OBJPATH)/OVR_SensorCalibration.o: $(LIBOVRPATH)/Src/OVR_SensorCalibration.cpp + $(CXXBUILD)OVR_SensorCalibration.o $(LIBOVRPATH)/Src/OVR_SensorCalibration.cpp + +$(OBJPATH)/OVR_SensorFilter.o: $(LIBOVRPATH)/Src/OVR_SensorFilter.cpp + $(CXXBUILD)OVR_SensorFilter.o $(LIBOVRPATH)/Src/OVR_SensorFilter.cpp + +$(OBJPATH)/OVR_SensorFusion.o: $(LIBOVRPATH)/Src/OVR_SensorFusion.cpp + $(CXXBUILD)OVR_SensorFusion.o $(LIBOVRPATH)/Src/OVR_SensorFusion.cpp + +$(OBJPATH)/OVR_SensorImpl.o: $(LIBOVRPATH)/Src/OVR_SensorImpl.cpp + $(CXXBUILD)OVR_SensorImpl.o $(LIBOVRPATH)/Src/OVR_SensorImpl.cpp + +$(OBJPATH)/OVR_Sensor2Impl.o: $(LIBOVRPATH)/Src/OVR_Sensor2Impl.cpp + $(CXXBUILD)OVR_Sensor2Impl.o $(LIBOVRPATH)/Src/OVR_Sensor2Impl.cpp + +$(OBJPATH)/OVR_SensorImpl_Common.o: $(LIBOVRPATH)/Src/OVR_SensorImpl_Common.cpp + $(CXXBUILD)OVR_SensorImpl_Common.o $(LIBOVRPATH)/Src/OVR_SensorImpl_Common.cpp + +$(OBJPATH)/OVR_SensorTimeFilter.o: $(LIBOVRPATH)/Src/OVR_SensorTimeFilter.cpp + $(CXXBUILD)OVR_SensorTimeFilter.o $(LIBOVRPATH)/Src/OVR_SensorTimeFilter.cpp + +$(OBJPATH)/OVR_Stereo.o: $(LIBOVRPATH)/Src/OVR_Stereo.cpp + $(CXXBUILD)OVR_Stereo.o $(LIBOVRPATH)/Src/OVR_Stereo.cpp + +$(OBJPATH)/OVR_ThreadCommandQueue.o: $(LIBOVRPATH)/Src/OVR_ThreadCommandQueue.cpp + $(CXXBUILD)OVR_ThreadCommandQueue.o $(LIBOVRPATH)/Src/OVR_ThreadCommandQueue.cpp + +$(OBJPATH)/OVR_Alg.o: $(LIBOVRPATH)/Src/Kernel/OVR_Alg.cpp + $(CXXBUILD)OVR_Alg.o $(LIBOVRPATH)/Src/Kernel/OVR_Alg.cpp + +$(OBJPATH)/OVR_Allocator.o: $(LIBOVRPATH)/Src/Kernel/OVR_Allocator.cpp + $(CXXBUILD)OVR_Allocator.o $(LIBOVRPATH)/Src/Kernel/OVR_Allocator.cpp + +$(OBJPATH)/OVR_Atomic.o: $(LIBOVRPATH)/Src/Kernel/OVR_Atomic.cpp + $(CXXBUILD)OVR_Atomic.o $(LIBOVRPATH)/Src/Kernel/OVR_Atomic.cpp + +$(OBJPATH)/OVR_File.o: $(LIBOVRPATH)/Src/Kernel/OVR_File.cpp + $(CXXBUILD)OVR_File.o $(LIBOVRPATH)/Src/Kernel/OVR_File.cpp + +$(OBJPATH)/OVR_FileFILE.o: $(LIBOVRPATH)/Src/Kernel/OVR_FileFILE.cpp + $(CXXBUILD)OVR_FileFILE.o $(LIBOVRPATH)/Src/Kernel/OVR_FileFILE.cpp + +$(OBJPATH)/OVR_Log.o: $(LIBOVRPATH)/Src/Kernel/OVR_Log.cpp + $(CXXBUILD)OVR_Log.o $(LIBOVRPATH)/Src/Kernel/OVR_Log.cpp + +$(OBJPATH)/OVR_Math.o: $(LIBOVRPATH)/Src/Kernel/OVR_Math.cpp + $(CXXBUILD)OVR_Math.o $(LIBOVRPATH)/Src/Kernel/OVR_Math.cpp + +$(OBJPATH)/OVR_Recording.o: $(LIBOVRPATH)/Src/OVR_Recording.cpp + $(CXXBUILD)OVR_Recording.o $(LIBOVRPATH)/Src/OVR_Recording.cpp + +$(OBJPATH)/OVR_RefCount.o: $(LIBOVRPATH)/Src/Kernel/OVR_RefCount.cpp + $(CXXBUILD)OVR_RefCount.o $(LIBOVRPATH)/Src/Kernel/OVR_RefCount.cpp + +$(OBJPATH)/OVR_Std.o: $(LIBOVRPATH)/Src/Kernel/OVR_Std.cpp + $(CXXBUILD)OVR_Std.o $(LIBOVRPATH)/Src/Kernel/OVR_Std.cpp + +$(OBJPATH)/OVR_String.o: $(LIBOVRPATH)/Src/Kernel/OVR_String.cpp + $(CXXBUILD)OVR_String.o $(LIBOVRPATH)/Src/Kernel/OVR_String.cpp + +$(OBJPATH)/OVR_String_FormatUtil.o: $(LIBOVRPATH)/Src/Kernel/OVR_String_FormatUtil.cpp + $(CXXBUILD)OVR_String_FormatUtil.o $(LIBOVRPATH)/Src/Kernel/OVR_String_FormatUtil.cpp + +$(OBJPATH)/OVR_String_PathUtil.o: $(LIBOVRPATH)/Src/Kernel/OVR_String_PathUtil.cpp + $(CXXBUILD)OVR_String_PathUtil.o $(LIBOVRPATH)/Src/Kernel/OVR_String_PathUtil.cpp + +$(OBJPATH)/OVR_SysFile.o: $(LIBOVRPATH)/Src/Kernel/OVR_SysFile.cpp + $(CXXBUILD)OVR_SysFile.o $(LIBOVRPATH)/Src/Kernel/OVR_SysFile.cpp + +$(OBJPATH)/OVR_System.o: $(LIBOVRPATH)/Src/Kernel/OVR_System.cpp + $(CXXBUILD)OVR_System.o $(LIBOVRPATH)/Src/Kernel/OVR_System.cpp + +$(OBJPATH)/OVR_Timer.o: $(LIBOVRPATH)/Src/Kernel/OVR_Timer.cpp + $(CXXBUILD)OVR_Timer.o $(LIBOVRPATH)/Src/Kernel/OVR_Timer.cpp + +$(OBJPATH)/OVR_UTF8Util.o: $(LIBOVRPATH)/Src/Kernel/OVR_UTF8Util.cpp + $(CXXBUILD)OVR_UTF8Util.o $(LIBOVRPATH)/Src/Kernel/OVR_UTF8Util.cpp + +$(OBJPATH)/Util_LatencyTest.o: $(LIBOVRPATH)/Src/Util/Util_LatencyTest.cpp + $(CXXBUILD)Util_LatencyTest.o $(LIBOVRPATH)/Src/Util/Util_LatencyTest.cpp + +$(OBJPATH)/Util_LatencyTest2.o: $(LIBOVRPATH)/Src/Util/Util_LatencyTest2.cpp + $(CXXBUILD)Util_LatencyTest2.o $(LIBOVRPATH)/Src/Util/Util_LatencyTest2.cpp + +$(OBJPATH)/Util_Render_Stereo.o: $(LIBOVRPATH)/Src/Util/Util_Render_Stereo.cpp + $(CXXBUILD)Util_Render_Stereo.o $(LIBOVRPATH)/Src/Util/Util_Render_Stereo.cpp + +$(OBJPATH)/OVR_ThreadsPthread.o: $(LIBOVRPATH)/Src/Kernel/OVR_ThreadsPthread.cpp + $(CXXBUILD)OVR_ThreadsPthread.o $(LIBOVRPATH)/Src/Kernel/OVR_ThreadsPthread.cpp + +$(OBJPATH)/OVR_Linux_HIDDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_HIDDevice.cpp + $(CXXBUILD)OVR_Linux_HIDDevice.o $(LIBOVRPATH)/Src/OVR_Linux_HIDDevice.cpp + +$(OBJPATH)/OVR_Linux_SensorDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp + $(CXXBUILD)OVR_Linux_SensorDevice.o $(LIBOVRPATH)/Src/OVR_Linux_SensorDevice.cpp + +$(OBJPATH)/OVR_Linux_DeviceManager.o: $(LIBOVRPATH)/Src/OVR_Linux_DeviceManager.cpp + $(CXXBUILD)OVR_Linux_DeviceManager.o $(LIBOVRPATH)/Src/OVR_Linux_DeviceManager.cpp + +$(OBJPATH)/OVR_Linux_HMDDevice.o: $(LIBOVRPATH)/Src/OVR_Linux_HMDDevice.cpp + $(CXXBUILD)OVR_Linux_HMDDevice.o $(LIBOVRPATH)/Src/OVR_Linux_HMDDevice.cpp + +$(OBJPATH)/tinyxml2.o: $(3RDPARTYPATH)/TinyXml/tinyxml2.cpp + $(CXXBUILD)tinyxml2.o $(3RDPARTYPATH)/TinyXml/tinyxml2.cpp + +$(OBJPATH)/edid.o: $(3RDPARTYPATH)/EDID/edid.cpp + $(CXXBUILD)edid.o $(3RDPARTYPATH)/EDID/edid.cpp + +clean: + -$(DELETEFILE) $(OBJECTS) + -$(DELETEFILE) $(TARGET) + diff --git a/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp new file mode 100644 index 0000000..da483d5 --- /dev/null +++ b/LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp @@ -0,0 +1,787 @@ +/************************************************************************************ + +Filename : OVR_ThreadsPthread.cpp +Content : +Created : +Notes : + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "OVR_Threads.h" +#include "OVR_Hash.h" + +#ifdef OVR_ENABLE_THREADS + +#include "OVR_Timer.h" +#include "OVR_Log.h" + +#include <pthread.h> +#include <time.h> +#include <unistd.h> +#include <sys/time.h> +#include <errno.h> + + +namespace OVR { + +// ***** Mutex implementation + + +// *** Internal Mutex implementation structure + +class MutexImpl : public NewOverrideBase +{ + // System mutex or semaphore + pthread_mutex_t SMutex; + bool Recursive; + unsigned LockCount; + pthread_t LockedBy; + + friend class WaitConditionImpl; + +public: + // Constructor/destructor + MutexImpl(Mutex* pmutex, bool recursive = 1); + ~MutexImpl(); + + // Locking functions + void DoLock(); + bool TryLock(); + void Unlock(Mutex* pmutex); + // Returns 1 if the mutes is currently locked + bool IsLockedByAnotherThread(Mutex* pmutex); + bool IsSignaled() const; +}; + +pthread_mutexattr_t Lock::RecursiveAttr; +bool Lock::RecursiveAttrInit = 0; + +// *** Constructor/destructor +MutexImpl::MutexImpl(Mutex* pmutex, bool recursive) +{ + OVR_UNUSED(pmutex); + Recursive = recursive; + LockCount = 0; + + if (Recursive) + { + if (!Lock::RecursiveAttrInit) + { + pthread_mutexattr_init(&Lock::RecursiveAttr); + pthread_mutexattr_settype(&Lock::RecursiveAttr, PTHREAD_MUTEX_RECURSIVE); + Lock::RecursiveAttrInit = 1; + } + + pthread_mutex_init(&SMutex, &Lock::RecursiveAttr); + } + else + pthread_mutex_init(&SMutex, 0); +} + +MutexImpl::~MutexImpl() +{ + pthread_mutex_destroy(&SMutex); +} + + +// Lock and try lock +void MutexImpl::DoLock() +{ + while (pthread_mutex_lock(&SMutex)) + ; + LockCount++; + LockedBy = pthread_self(); +} + +bool MutexImpl::TryLock() +{ + if (!pthread_mutex_trylock(&SMutex)) + { + LockCount++; + LockedBy = pthread_self(); + return 1; + } + + return 0; +} + +void MutexImpl::Unlock(Mutex* pmutex) +{ + OVR_UNUSED(pmutex); + OVR_ASSERT(pthread_self() == LockedBy && LockCount > 0); + + unsigned lockCount; + LockCount--; + lockCount = LockCount; + + pthread_mutex_unlock(&SMutex); +} + +bool MutexImpl::IsLockedByAnotherThread(Mutex* pmutex) +{ + OVR_UNUSED(pmutex); + // There could be multiple interpretations of IsLocked with respect to current thread + if (LockCount == 0) + return 0; + if (pthread_self() != LockedBy) + return 1; + return 0; +} + +bool MutexImpl::IsSignaled() const +{ + // An mutex is signaled if it is not locked ANYWHERE + // Note that this is different from IsLockedByAnotherThread function, + // that takes current thread into account + return LockCount == 0; +} + + +// *** Actual Mutex class implementation + +Mutex::Mutex(bool recursive) +{ + // NOTE: RefCount mode already thread-safe for all waitables. + pImpl = new MutexImpl(this, recursive); +} + +Mutex::~Mutex() +{ + delete pImpl; +} + +// Lock and try lock +void Mutex::DoLock() +{ + pImpl->DoLock(); +} +bool Mutex::TryLock() +{ + return pImpl->TryLock(); +} +void Mutex::Unlock() +{ + pImpl->Unlock(this); +} +bool Mutex::IsLockedByAnotherThread() +{ + return pImpl->IsLockedByAnotherThread(this); +} + + + +//----------------------------------------------------------------------------------- +// ***** Event + +bool Event::Wait(unsigned delay) +{ + Mutex::Locker lock(&StateMutex); + + // Do the correct amount of waiting + if (delay == OVR_WAIT_INFINITE) + { + while(!State) + StateWaitCondition.Wait(&StateMutex); + } + else if (delay) + { + if (!State) + StateWaitCondition.Wait(&StateMutex, delay); + } + + bool state = State; + // Take care of temporary 'pulsing' of a state + if (Temporary) + { + Temporary = false; + State = false; + } + return state; +} + +void Event::updateState(bool newState, bool newTemp, bool mustNotify) +{ + Mutex::Locker lock(&StateMutex); + State = newState; + Temporary = newTemp; + if (mustNotify) + StateWaitCondition.NotifyAll(); +} + + + +// ***** Wait Condition Implementation + +// Internal implementation class +class WaitConditionImpl : public NewOverrideBase +{ + pthread_mutex_t SMutex; + pthread_cond_t Condv; + +public: + + // Constructor/destructor + WaitConditionImpl(); + ~WaitConditionImpl(); + + // Release mutex and wait for condition. The mutex is re-aqured after the wait. + bool Wait(Mutex *pmutex, unsigned delay = OVR_WAIT_INFINITE); + + // Notify a condition, releasing at one object waiting + void Notify(); + // Notify a condition, releasing all objects waiting + void NotifyAll(); +}; + + +WaitConditionImpl::WaitConditionImpl() +{ + pthread_mutex_init(&SMutex, 0); + pthread_cond_init(&Condv, 0); +} + +WaitConditionImpl::~WaitConditionImpl() +{ + pthread_mutex_destroy(&SMutex); + pthread_cond_destroy(&Condv); +} + +bool WaitConditionImpl::Wait(Mutex *pmutex, unsigned delay) +{ + bool result = 1; + unsigned lockCount = pmutex->pImpl->LockCount; + + // Mutex must have been locked + if (lockCount == 0) + return 0; + + pthread_mutex_lock(&SMutex); + + // Finally, release a mutex or semaphore + if (pmutex->pImpl->Recursive) + { + // Release the recursive mutex N times + pmutex->pImpl->LockCount = 0; + for(unsigned i=0; i<lockCount; i++) + pthread_mutex_unlock(&pmutex->pImpl->SMutex); + } + else + { + pmutex->pImpl->LockCount = 0; + pthread_mutex_unlock(&pmutex->pImpl->SMutex); + } + + // Note that there is a gap here between mutex.Unlock() and Wait(). + // The other mutex protects this gap. + + if (delay == OVR_WAIT_INFINITE) + pthread_cond_wait(&Condv,&SMutex); + else + { + timespec ts; + + struct timeval tv; + gettimeofday(&tv, 0); + + ts.tv_sec = tv.tv_sec + (delay / 1000); + ts.tv_nsec = (tv.tv_usec + (delay % 1000) * 1000) * 1000; + + if (ts.tv_nsec > 999999999) + { + ts.tv_sec++; + ts.tv_nsec -= 1000000000; + } + int r = pthread_cond_timedwait(&Condv,&SMutex, &ts); + OVR_ASSERT(r == 0 || r == ETIMEDOUT); + if (r) + result = 0; + } + + pthread_mutex_unlock(&SMutex); + + // Re-aquire the mutex + for(unsigned i=0; i<lockCount; i++) + pmutex->DoLock(); + + // Return the result + return result; +} + +// Notify a condition, releasing the least object in a queue +void WaitConditionImpl::Notify() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_signal(&Condv); + pthread_mutex_unlock(&SMutex); +} + +// Notify a condition, releasing all objects waiting +void WaitConditionImpl::NotifyAll() +{ + pthread_mutex_lock(&SMutex); + pthread_cond_broadcast(&Condv); + pthread_mutex_unlock(&SMutex); +} + + + +// *** Actual implementation of WaitCondition + +WaitCondition::WaitCondition() +{ + pImpl = new WaitConditionImpl; +} +WaitCondition::~WaitCondition() +{ + delete pImpl; +} + +bool WaitCondition::Wait(Mutex *pmutex, unsigned delay) +{ + return pImpl->Wait(pmutex, delay); +} +// Notification +void WaitCondition::Notify() +{ + pImpl->Notify(); +} +void WaitCondition::NotifyAll() +{ + pImpl->NotifyAll(); +} + + +// ***** Current thread + +// Per-thread variable +/* +static __thread Thread* pCurrentThread = 0; + +// Static function to return a pointer to the current thread +void Thread::InitCurrentThread(Thread *pthread) +{ + pCurrentThread = pthread; +} + +// Static function to return a pointer to the current thread +Thread* Thread::GetThread() +{ + return pCurrentThread; +} +*/ + + +// *** Thread constructors. + +Thread::Thread(UPInt stackSize, int processor) +{ + // NOTE: RefCount mode already thread-safe for all Waitable objects. + CreateParams params; + params.stackSize = stackSize; + params.processor = processor; + Init(params); +} + +Thread::Thread(Thread::ThreadFn threadFunction, void* userHandle, UPInt stackSize, + int processor, Thread::ThreadState initialState) +{ + CreateParams params(threadFunction, userHandle, stackSize, processor, initialState); + Init(params); +} + +Thread::Thread(const CreateParams& params) +{ + Init(params); +} + +void Thread::Init(const CreateParams& params) +{ + // Clear the variables + ThreadFlags = 0; + ThreadHandle = 0; + ExitCode = 0; + SuspendCount = 0; + StackSize = params.stackSize; + Processor = params.processor; + Priority = params.priority; + + // Clear Function pointers + ThreadFunction = params.threadFunction; + UserHandle = params.userHandle; + if (params.initialState != NotRunning) + Start(params.initialState); +} + +Thread::~Thread() +{ + // Thread should not running while object is being destroyed, + // this would indicate ref-counting issue. + //OVR_ASSERT(IsRunning() == 0); + + // Clean up thread. + ThreadHandle = 0; +} + + + +// *** Overridable User functions. + +// Default Run implementation +int Thread::Run() +{ + // Call pointer to function, if available. + return (ThreadFunction) ? ThreadFunction(this, UserHandle) : 0; +} +void Thread::OnExit() +{ +} + + +// Finishes the thread and releases internal reference to it. +void Thread::FinishAndRelease() +{ + // Note: thread must be US. + ThreadFlags &= (UInt32)~(OVR_THREAD_STARTED); + ThreadFlags |= OVR_THREAD_FINISHED; + + // Release our reference; this is equivalent to 'delete this' + // from the point of view of our thread. + Release(); +} + + + +// *** ThreadList - used to track all created threads + +class ThreadList : public NewOverrideBase +{ + //------------------------------------------------------------------------ + struct ThreadHashOp + { + size_t operator()(const Thread* ptr) + { + return (((size_t)ptr) >> 6) ^ (size_t)ptr; + } + }; + + HashSet<Thread*, ThreadHashOp> ThreadSet; + Mutex ThreadMutex; + WaitCondition ThreadsEmpty; + // Track the root thread that created us. + pthread_t RootThreadId; + + static ThreadList* volatile pRunningThreads; + + void addThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Add(pthread); + } + + void removeThread(Thread *pthread) + { + Mutex::Locker lock(&ThreadMutex); + ThreadSet.Remove(pthread); + if (ThreadSet.GetSize() == 0) + ThreadsEmpty.Notify(); + } + + void finishAllThreads() + { + // Only original root thread can call this. + OVR_ASSERT(pthread_self() == RootThreadId); + + Mutex::Locker lock(&ThreadMutex); + while (ThreadSet.GetSize() != 0) + ThreadsEmpty.Wait(&ThreadMutex); + } + +public: + + ThreadList() + { + RootThreadId = pthread_self(); + } + ~ThreadList() { } + + + static void AddRunningThread(Thread *pthread) + { + // Non-atomic creation ok since only the root thread + if (!pRunningThreads) + { + pRunningThreads = new ThreadList; + OVR_ASSERT(pRunningThreads); + } + pRunningThreads->addThread(pthread); + } + + // NOTE: 'pthread' might be a dead pointer when this is + // called so it should not be accessed; it is only used + // for removal. + static void RemoveRunningThread(Thread *pthread) + { + OVR_ASSERT(pRunningThreads); + pRunningThreads->removeThread(pthread); + } + + static void FinishAllThreads() + { + // This is ok because only root thread can wait for other thread finish. + if (pRunningThreads) + { + pRunningThreads->finishAllThreads(); + delete pRunningThreads; + pRunningThreads = 0; + } + } +}; + +// By default, we have no thread list. +ThreadList* volatile ThreadList::pRunningThreads = 0; + + +// FinishAllThreads - exposed publicly in Thread. +void Thread::FinishAllThreads() +{ + ThreadList::FinishAllThreads(); +} + +// *** Run override + +int Thread::PRun() +{ + // Suspend us on start, if requested + if (ThreadFlags & OVR_THREAD_START_SUSPENDED) + { + Suspend(); + ThreadFlags &= (UInt32)~OVR_THREAD_START_SUSPENDED; + } + + // Call the virtual run function + ExitCode = Run(); + return ExitCode; +} + + + + +// *** User overridables + +bool Thread::GetExitFlag() const +{ + return (ThreadFlags & OVR_THREAD_EXIT) != 0; +} + +void Thread::SetExitFlag(bool exitFlag) +{ + // The below is atomic since ThreadFlags is AtomicInt. + if (exitFlag) + ThreadFlags |= OVR_THREAD_EXIT; + else + ThreadFlags &= (UInt32) ~OVR_THREAD_EXIT; +} + + +// Determines whether the thread was running and is now finished +bool Thread::IsFinished() const +{ + return (ThreadFlags & OVR_THREAD_FINISHED) != 0; +} +// Determines whether the thread is suspended +bool Thread::IsSuspended() const +{ + return SuspendCount > 0; +} +// Returns current thread state +Thread::ThreadState Thread::GetThreadState() const +{ + if (IsSuspended()) + return Suspended; + if (ThreadFlags & OVR_THREAD_STARTED) + return Running; + return NotRunning; +} +/* +static const char* mapsched_policy(int policy) +{ + switch(policy) + { + case SCHED_OTHER: + return "SCHED_OTHER"; + case SCHED_RR: + return "SCHED_RR"; + case SCHED_FIFO: + return "SCHED_FIFO"; + + } + return "UNKNOWN"; +} + int policy; + sched_param sparam; + pthread_getschedparam(pthread_self(), &policy, &sparam); + int max_prior = sched_get_priority_max(policy); + int min_prior = sched_get_priority_min(policy); + printf(" !!!! policy: %s, priority: %d, max priority: %d, min priority: %d\n", mapsched_policy(policy), sparam.sched_priority, max_prior, min_prior); +#include <stdio.h> +*/ +// ***** Thread management + +// The actual first function called on thread start +void* Thread_PthreadStartFn(void* phandle) +{ + Thread* pthread = (Thread*)phandle; + int result = pthread->PRun(); + // Signal the thread as done and release it atomically. + pthread->FinishAndRelease(); + // At this point Thread object might be dead; however we can still pass + // it to RemoveRunningThread since it is only used as a key there. + ThreadList::RemoveRunningThread(pthread); + return reinterpret_cast<void*>(result); +} + +int Thread::InitAttr = 0; +pthread_attr_t Thread::Attr; + +/* static */ +int Thread::GetOSPriority(ThreadPriority p) +//static inline int MapToSystemPrority(Thread::ThreadPriority p) +{ + OVR_UNUSED(p); + return -1; +} + +bool Thread::Start(ThreadState initialState) +{ + if (initialState == NotRunning) + return 0; + if (GetThreadState() != NotRunning) + { + OVR_DEBUG_LOG(("Thread::Start failed - thread %p already running", this)); + return 0; + } + + if (!InitAttr) + { + pthread_attr_init(&Attr); + pthread_attr_setdetachstate(&Attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&Attr, 128 * 1024); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(NormalPriority); + pthread_attr_setschedparam(&Attr, &sparam); + InitAttr = 1; + } + + ExitCode = 0; + SuspendCount = 0; + ThreadFlags = (initialState == Running) ? 0 : OVR_THREAD_START_SUSPENDED; + + // AddRef to us until the thread is finished + AddRef(); + ThreadList::AddRunningThread(this); + + int result; + if (StackSize != 128 * 1024 || Priority != NormalPriority) + { + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + pthread_attr_setstacksize(&attr, StackSize); + sched_param sparam; + sparam.sched_priority = Thread::GetOSPriority(Priority); + pthread_attr_setschedparam(&attr, &sparam); + result = pthread_create(&ThreadHandle, &attr, Thread_PthreadStartFn, this); + pthread_attr_destroy(&attr); + } + else + result = pthread_create(&ThreadHandle, &Attr, Thread_PthreadStartFn, this); + + if (result) + { + ThreadFlags = 0; + Release(); + ThreadList::RemoveRunningThread(this); + return 0; + } + return 1; +} + + +// Suspend the thread until resumed +bool Thread::Suspend() +{ + OVR_DEBUG_LOG(("Thread::Suspend - cannot suspend threads on this system")); + return 0; +} + +// Resumes currently suspended thread +bool Thread::Resume() +{ + return 0; +} + + +// Quits with an exit code +void Thread::Exit(int exitCode) +{ + // Can only exist the current thread + // if (GetThread() != this) + // return; + + // Call the virtual OnExit function + OnExit(); + + // Signal this thread object as done and release it's references. + FinishAndRelease(); + ThreadList::RemoveRunningThread(this); + + pthread_exit(reinterpret_cast<void*>(exitCode)); +} + +ThreadId GetCurrentThreadId() +{ + return (void*)pthread_self(); +} + +// *** Sleep functions + +/* static */ +bool Thread::Sleep(unsigned secs) +{ + sleep(secs); + return 1; +} +/* static */ +bool Thread::MSleep(unsigned msecs) +{ + usleep(msecs*1000); + return 1; +} + +/* static */ +int Thread::GetCPUCount() +{ + return 1; +} + +} + +#endif // OVR_ENABLE_THREADS diff --git a/LibOVR/Src/OVR_Linux_DeviceManager.cpp b/LibOVR/Src/OVR_Linux_DeviceManager.cpp new file mode 100644 index 0000000..f1c4278 --- /dev/null +++ b/LibOVR/Src/OVR_Linux_DeviceManager.cpp @@ -0,0 +1,331 @@ +/************************************************************************************ + +Filename : OVR_Linux_DeviceManager.h +Content : Linux implementation of DeviceManager. +Created : +Authors : + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OVR_Linux_DeviceManager.h" + +// Sensor & HMD Factories +#include "OVR_LatencyTestImpl.h" +#include "OVR_SensorImpl.h" +#include "OVR_Linux_HIDDevice.h" +#include "OVR_Linux_HMDDevice.h" + +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_Std.h" +#include "Kernel/OVR_Log.h" + +namespace OVR { namespace Linux { + + +//------------------------------------------------------------------------------------- +// **** Linux::DeviceManager + +DeviceManager::DeviceManager() +{ +} + +DeviceManager::~DeviceManager() +{ +} + +bool DeviceManager::Initialize(DeviceBase*) +{ + if (!DeviceManagerImpl::Initialize(0)) + return false; + + pThread = *new DeviceManagerThread(); + if (!pThread || !pThread->Start()) + return false; + + // Wait for the thread to be fully up and running. + pThread->StartupEvent.Wait(); + + // Do this now that we know the thread's run loop. + HidDeviceManager = *HIDDeviceManager::CreateInternal(this); + + pCreateDesc->pDevice = this; + LogText("OVR::DeviceManager - initialized.\n"); + return true; +} + +void DeviceManager::Shutdown() +{ + LogText("OVR::DeviceManager - shutting down.\n"); + + // Set Manager shutdown marker variable; this prevents + // any existing DeviceHandle objects from accessing device. + pCreateDesc->pLock->pManager = 0; + + // Push for thread shutdown *WITH NO WAIT*. + // This will have the following effect: + // - Exit command will get enqueued, which will be executed later on the thread itself. + // - Beyond this point, this DeviceManager object may be deleted by our caller. + // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will + // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued + // after pManager is null. + // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last + // reference to the thread object. + pThread->PushExitCommand(false); + pThread.Clear(); + + DeviceManagerImpl::Shutdown(); +} + +ThreadCommandQueue* DeviceManager::GetThreadQueue() +{ + return pThread; +} + +ThreadId DeviceManager::GetThreadId() const +{ + return pThread->GetThreadId(); +} + +bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const +{ + if ((info->InfoClassType != Device_Manager) && + (info->InfoClassType != Device_None)) + return false; + + info->Type = Device_Manager; + info->Version = 0; + info->ProductName = "DeviceManager"; + info->Manufacturer = "Oculus VR, Inc."; + return true; +} + +DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) +{ + // TBD: Can this be avoided in the future, once proper device notification is in place? + pThread->PushCall((DeviceManagerImpl*)this, + &DeviceManager::EnumerateAllFactoryDevices, true); + + return DeviceManagerImpl::EnumerateDevicesEx(args); +} + + +//------------------------------------------------------------------------------------- +// ***** DeviceManager Thread + +DeviceManagerThread::DeviceManagerThread() + : Thread(ThreadStackSize) +{ + int result = pipe(CommandFd); + OVR_ASSERT(!result); + OVR_UNUSED(result); + + AddSelectFd(NULL, CommandFd[0]); +} + +DeviceManagerThread::~DeviceManagerThread() +{ + if (CommandFd[0]) + { + RemoveSelectFd(NULL, CommandFd[0]); + close(CommandFd[0]); + close(CommandFd[1]); + } +} + +bool DeviceManagerThread::AddSelectFd(Notifier* notify, int fd) +{ + struct pollfd pfd; + pfd.fd = fd; + pfd.events = POLLIN|POLLHUP|POLLERR; + pfd.revents = 0; + + FdNotifiers.PushBack(notify); + PollFds.PushBack(pfd); + + OVR_ASSERT(FdNotifiers.GetSize() == PollFds.GetSize()); + return true; +} + +bool DeviceManagerThread::RemoveSelectFd(Notifier* notify, int fd) +{ + // [0] is reserved for thread commands with notify of null, but we still + // can use this function to remove it. + for (UPInt i = 0; i < FdNotifiers.GetSize(); i++) + { + if ((FdNotifiers[i] == notify) && (PollFds[i].fd == fd)) + { + FdNotifiers.RemoveAt(i); + PollFds.RemoveAt(i); + return true; + } + } + return false; +} + + + +int DeviceManagerThread::Run() +{ + ThreadCommand::PopBuffer command; + + SetThreadName("OVR::DeviceManagerThread"); + LogText("OVR::DeviceManagerThread - running (ThreadId=%p).\n", GetThreadId()); + + // Signal to the parent thread that initialization has finished. + StartupEvent.SetEvent(); + + while(!IsExiting()) + { + // PopCommand will reset event on empty queue. + if (PopCommand(&command)) + { + command.Execute(); + } + else + { + bool commands = 0; + do + { + int waitMs = -1; + + // If devices have time-dependent logic registered, get the longest wait + // allowed based on current ticks. + if (!TicksNotifiers.IsEmpty()) + { + double timeSeconds = Timer::GetSeconds(); + unsigned waitAllowed; + + for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) + { + waitAllowed = (unsigned)(TicksNotifiers[j]->OnTicks(timeSeconds) * Timer::MsPerSecond); + if (waitAllowed < (unsigned)waitMs) + waitMs = waitAllowed; + } + } + + // wait until there is data available on one of the devices or the timeout expires + int n = poll(&PollFds[0], PollFds.GetSize(), waitMs); + + if (n > 0) + { + // Iterate backwards through the list so the ordering will not be + // affected if the called object gets removed during the callback + // Also, the HID data streams are located toward the back of the list + // and servicing them first will allow a disconnect to be handled + // and cleaned directly at the device first instead of the general HID monitor + for (int i=PollFds.GetSize()-1; i>=0; i--) + { + if (PollFds[i].revents & POLLERR) + { + OVR_DEBUG_LOG(("poll: error on [%d]: %d", i, PollFds[i].fd)); + } + else if (PollFds[i].revents & POLLIN) + { + if (FdNotifiers[i]) + FdNotifiers[i]->OnEvent(i, PollFds[i].fd); + else if (i == 0) // command + { + char dummy[128]; + read(PollFds[i].fd, dummy, 128); + commands = 1; + } + } + + if (PollFds[i].revents & POLLHUP) + PollFds[i].events = 0; + + if (PollFds[i].revents != 0) + { + n--; + if (n == 0) + break; + } + } + } + } while (PollFds.GetSize() > 0 && !commands); + } + } + + LogText("OVR::DeviceManagerThread - exiting (ThreadId=%p).\n", GetThreadId()); + return 0; +} + +bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) +{ + TicksNotifiers.PushBack(notify); + return true; +} + +bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) +{ + for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) + { + if (TicksNotifiers[i] == notify) + { + TicksNotifiers.RemoveAt(i); + return true; + } + } + return false; +} + +} // namespace Linux + + +//------------------------------------------------------------------------------------- +// ***** Creation + + +// Creates a new DeviceManager and initializes OVR. +DeviceManager* DeviceManager::Create() +{ + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr<Linux::DeviceManager> manager = *new Linux::DeviceManager; + + if (manager) + { + if (manager->Initialize(0)) + { + manager->AddFactory(&LatencyTestDeviceFactory::GetInstance()); + manager->AddFactory(&SensorDeviceFactory::GetInstance()); + manager->AddFactory(&Linux::HMDDeviceFactory::GetInstance()); + + manager->AddRef(); + } + else + { + manager.Clear(); + } + + } + + return manager.GetPtr(); +} + + +} // namespace OVR + diff --git a/LibOVR/Src/OVR_Linux_DeviceManager.h b/LibOVR/Src/OVR_Linux_DeviceManager.h new file mode 100644 index 0000000..6532103 --- /dev/null +++ b/LibOVR/Src/OVR_Linux_DeviceManager.h @@ -0,0 +1,122 @@ +/************************************************************************************ + +Filename : OVR_Linux_DeviceManager.h +Content : Linux-specific DeviceManager header. +Created : +Authors : + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Linux_DeviceManager_h +#define OVR_Linux_DeviceManager_h + +#include "OVR_DeviceImpl.h" + +#include <unistd.h> +#include <sys/poll.h> + + +namespace OVR { namespace Linux { + +class DeviceManagerThread; + +//------------------------------------------------------------------------------------- +// ***** Linux DeviceManager + +class DeviceManager : public DeviceManagerImpl +{ +public: + DeviceManager(); + ~DeviceManager(); + + // Initialize/Shutdowncreate and shutdown manger thread. + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + virtual ThreadCommandQueue* GetThreadQueue(); + virtual ThreadId GetThreadId() const; + + virtual DeviceEnumerator<> EnumerateDevicesEx(const DeviceEnumerationArgs& args); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + Ptr<DeviceManagerThread> pThread; +}; + +//------------------------------------------------------------------------------------- +// ***** Device Manager Background Thread + +class DeviceManagerThread : public Thread, public ThreadCommandQueue +{ + friend class DeviceManager; + enum { ThreadStackSize = 64 * 1024 }; +public: + DeviceManagerThread(); + ~DeviceManagerThread(); + + virtual int Run(); + + // ThreadCommandQueue notifications for CommandEvent handling. + virtual void OnPushNonEmpty_Locked() { write(CommandFd[1], this, 1); } + virtual void OnPopEmpty_Locked() { } + + class Notifier + { + public: + // Called when I/O is received + virtual void OnEvent(int i, int fd) = 0; + + // Called when timing ticks are updated. + // Returns the largest number of seconds this function can + // wait till next call. + virtual double OnTicks(double tickSeconds) + { + OVR_UNUSED1(tickSeconds); + return 1000.0; + } + }; + + // Add I/O notifier + bool AddSelectFd(Notifier* notify, int fd); + bool RemoveSelectFd(Notifier* notify, int fd); + + // Add notifier that will be called at regular intervals. + bool AddTicksNotifier(Notifier* notify); + bool RemoveTicksNotifier(Notifier* notify); + +private: + + bool threadInitialized() { return CommandFd[0] != 0; } + + // pipe used to signal commands + int CommandFd[2]; + + Array<struct pollfd> PollFds; + Array<Notifier*> FdNotifiers; + + Event StartupEvent; + + // Ticks notifiers - used for time-dependent events such as keep-alive. + Array<Notifier*> TicksNotifiers; +}; + +}} // namespace Linux::OVR + +#endif // OVR_Linux_DeviceManager_h diff --git a/LibOVR/Src/OVR_Linux_HIDDevice.cpp b/LibOVR/Src/OVR_Linux_HIDDevice.cpp new file mode 100644 index 0000000..133e5c3 --- /dev/null +++ b/LibOVR/Src/OVR_Linux_HIDDevice.cpp @@ -0,0 +1,819 @@ +/************************************************************************************ +Filename : OVR_Linux_HIDDevice.cpp +Content : Linux HID device implementation. +Created : February 26, 2013 +Authors : Lee Cooper + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OVR_Linux_HIDDevice.h" + +#include <sys/ioctl.h> +#include <fcntl.h> +#include <errno.h> +#include <linux/hidraw.h> +#include "OVR_HIDDeviceImpl.h" + +namespace OVR { namespace Linux { + +static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5; + +//------------------------------------------------------------------------------------- +// **** Linux::DeviceManager +//----------------------------------------------------------------------------- +HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) : DevManager(manager) +{ + UdevInstance = NULL; + HIDMonitor = NULL; + HIDMonHandle = -1; +} + +//----------------------------------------------------------------------------- +HIDDeviceManager::~HIDDeviceManager() +{ +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::initializeManager() +{ + if (HIDMonitor) + { + return true; + } + + // Create a udev_monitor handle to watch for device changes (hot-plug detection) + HIDMonitor = udev_monitor_new_from_netlink(UdevInstance, "udev"); + if (HIDMonitor == NULL) + { + return false; + } + + udev_monitor_filter_add_match_subsystem_devtype(HIDMonitor, "hidraw", NULL); // filter for hidraw only + + int err = udev_monitor_enable_receiving(HIDMonitor); + if (err) + { + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + return false; + } + + // Get the file descriptor (fd) for the monitor. + HIDMonHandle = udev_monitor_get_fd(HIDMonitor); + if (HIDMonHandle < 0) + { + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + return false; + } + + // This file handle will be polled along-side with the device hid handles for changes + // Add the handle to the polling list + if (!DevManager->pThread->AddSelectFd(this, HIDMonHandle)) + { + close(HIDMonHandle); + HIDMonHandle = -1; + + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::Initialize() +{ + // Get a udev library handle. This handle must stay active during the + // duration the lifetime of device monitoring handles + UdevInstance = udev_new(); + if (!UdevInstance) + return false; + + return initializeManager(); +} + +//----------------------------------------------------------------------------- +void HIDDeviceManager::Shutdown() +{ + OVR_ASSERT_LOG((UdevInstance), ("Should have called 'Initialize' before 'Shutdown'.")); + + if (HIDMonitor) + { + DevManager->pThread->RemoveSelectFd(this, HIDMonHandle); + close(HIDMonHandle); + HIDMonHandle = -1; + + udev_monitor_unref(HIDMonitor); + HIDMonitor = NULL; + } + + udev_unref(UdevInstance); // release the library + + LogText("OVR::Linux::HIDDeviceManager - shutting down.\n"); +} + +//------------------------------------------------------------------------------- +bool HIDDeviceManager::AddNotificationDevice(HIDDevice* device) +{ + NotificationDevices.PushBack(device); + return true; +} + +//------------------------------------------------------------------------------- +bool HIDDeviceManager::RemoveNotificationDevice(HIDDevice* device) +{ + for (UPInt i = 0; i < NotificationDevices.GetSize(); i++) + { + if (NotificationDevices[i] == device) + { + NotificationDevices.RemoveAt(i); + return true; + } + } + return false; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::getIntProperty(udev_device* device, + const char* propertyName, + SInt32* pResult) +{ + const char* str = udev_device_get_sysattr_value(device, propertyName); + if (str) + { + *pResult = strtol(str, NULL, 16); + return true; + } + else + { + *pResult = 0; + return true; + } +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc) +{ + SInt32 result; + if (getIntProperty(device, "idVendor", &result)) + pDevDesc->VendorId = result; + else + return false; + + if (getIntProperty(device, "idProduct", &result)) + pDevDesc->ProductId = result; + else + return false; + + if (getIntProperty(device, "bcdDevice", &result)) + pDevDesc->VersionNumber = result; + else + return false; + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::getStringProperty(udev_device* device, + const char* propertyName, + OVR::String* pResult) +{ + // Get the attribute in UTF8 + const char* str = udev_device_get_sysattr_value(device, propertyName); + if (str) + { // Copy the string into the return value + *pResult = String(str); + return true; + } + else + { + return false; + } +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) +{ + + if (!initializeManager()) + { + return false; + } + + // Get a list of hid devices + udev_enumerate* devices = udev_enumerate_new(UdevInstance); + udev_enumerate_add_match_subsystem(devices, "hidraw"); + udev_enumerate_scan_devices(devices); + + udev_list_entry* entry = udev_enumerate_get_list_entry(devices); + + // Search each device for the matching vid/pid + while (entry != NULL) + { + // Get the device file name + const char* sysfs_path = udev_list_entry_get_name(entry); + udev_device* hid; // The device's HID udev node. + hid = udev_device_new_from_syspath(UdevInstance, sysfs_path); + const char* dev_path = udev_device_get_devnode(hid); + + // Get the USB device + hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); + if (hid) + { + HIDDeviceDesc devDesc; + + // Check the VID/PID for a match + if (dev_path && + initVendorProductVersion(hid, &devDesc) && + enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId)) + { + devDesc.Path = dev_path; + getFullDesc(hid, &devDesc); + + // Look for the device to check if it is already opened. + Ptr<DeviceCreateDesc> existingDevice = DevManager->FindHIDDevice(devDesc, true); + // if device exists and it is opened then most likely the device open() + // will fail; therefore, we just set Enumerated to 'true' and continue. + if (existingDevice && existingDevice->pDevice) + { + existingDevice->Enumerated = true; + } + else + { // open the device temporarily for startup communication + int device_handle = open(dev_path, O_RDWR); + if (device_handle >= 0) + { + // Construct minimal device that the visitor callback can get feature reports from + Linux::HIDDevice device(this, device_handle); + enumVisitor->Visit(device, devDesc); + + close(device_handle); // close the file handle + } + } + } + + udev_device_unref(hid); + entry = udev_list_entry_get_next(entry); + } + } + + // Free the enumerator and udev objects + udev_enumerate_unref(devices); + + return true; +} + +//----------------------------------------------------------------------------- +OVR::HIDDevice* HIDDeviceManager::Open(const String& path) +{ + Ptr<Linux::HIDDevice> device = *new Linux::HIDDevice(this); + + if (device->HIDInitialize(path)) + { + device->AddRef(); + return device; + } + + return NULL; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::getFullDesc(udev_device* device, HIDDeviceDesc* desc) +{ + + if (!initVendorProductVersion(device, desc)) + { + return false; + } + + if (!getStringProperty(device, "serial", &(desc->SerialNumber))) + { + return false; + } + + getStringProperty(device, "manufacturer", &(desc->Manufacturer)); + getStringProperty(device, "product", &(desc->Product)); + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDeviceManager::GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc) +{ + if (!initializeManager()) + { + return false; + } + + // Search for the udev device from the given pathname so we can + // have a handle to query device properties + + udev_enumerate* devices = udev_enumerate_new(UdevInstance); + udev_enumerate_add_match_subsystem(devices, "hidraw"); + udev_enumerate_scan_devices(devices); + + udev_list_entry* entry = udev_enumerate_get_list_entry(devices); + + bool success = false; + // Search for the device with the matching path + while (entry != NULL) + { + // Get the device file name + const char* sysfs_path = udev_list_entry_get_name(entry); + udev_device* hid; // The device's HID udev node. + hid = udev_device_new_from_syspath(UdevInstance, sysfs_path); + const char* path = udev_device_get_devnode(hid); + + if (OVR_strcmp(dev_path, path) == 0) + { // Found the device so lets collect the device descriptor + + // Get the USB device + hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); + if (hid) + { + desc->Path = dev_path; + success = getFullDesc(hid, desc); + } + + } + + udev_device_unref(hid); + entry = udev_list_entry_get_next(entry); + } + + // Free the enumerator + udev_enumerate_unref(devices); + + return success; +} + +//----------------------------------------------------------------------------- +void HIDDeviceManager::OnEvent(int i, int fd) +{ + OVR_UNUSED(i); + OVR_UNUSED(fd); + + // There is a device status change + udev_device* hid = udev_monitor_receive_device(HIDMonitor); + if (hid) + { + const char* dev_path = udev_device_get_devnode(hid); + const char* action = udev_device_get_action(hid); + + HIDDeviceDesc device_info; + device_info.Path = dev_path; + + MessageType notify_type; + if (OVR_strcmp(action, "add") == 0) + { + notify_type = Message_DeviceAdded; + + // Retrieve the device info. This can only be done on a connected + // device and is invalid for a disconnected device + + // Get the USB device + hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); + if (!hid) + { + return; + } + + getFullDesc(hid, &device_info); + } + else if (OVR_strcmp(action, "remove") == 0) + { + notify_type = Message_DeviceRemoved; + } + else + { + return; + } + + bool error = false; + bool deviceFound = false; + for (UPInt i = 0; i < NotificationDevices.GetSize(); i++) + { + if (NotificationDevices[i] && + NotificationDevices[i]->OnDeviceNotification(notify_type, &device_info, &error)) + { + // The notification was for an existing device + deviceFound = true; + break; + } + } + + if (notify_type == Message_DeviceAdded && !deviceFound) + { + DevManager->DetectHIDDevice(device_info); + } + + udev_device_unref(hid); + } +} + +//============================================================================= +// Linux::HIDDevice +//============================================================================= +HIDDevice::HIDDevice(HIDDeviceManager* manager) + : InMinimalMode(false), HIDManager(manager) +{ + DeviceHandle = -1; +} + +//----------------------------------------------------------------------------- +// This is a minimal constructor used during enumeration for us to pass +// a HIDDevice to the visit function (so that it can query feature reports). +HIDDevice::HIDDevice(HIDDeviceManager* manager, int device_handle) +: InMinimalMode(true), HIDManager(manager), DeviceHandle(device_handle) +{ +} + +//----------------------------------------------------------------------------- +HIDDevice::~HIDDevice() +{ + if (!InMinimalMode) + { + HIDShutdown(); + } +} + +//----------------------------------------------------------------------------- +bool HIDDevice::HIDInitialize(const String& path) +{ + const char* hid_path = path.ToCStr(); + if (!openDevice(hid_path)) + { + LogText("OVR::Linux::HIDDevice - Failed to open HIDDevice: %s", hid_path); + return false; + } + + HIDManager->DevManager->pThread->AddTicksNotifier(this); + HIDManager->AddNotificationDevice(this); + + LogText("OVR::Linux::HIDDevice - Opened '%s'\n" + " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", + DevDesc.Path.ToCStr(), + DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), + DevDesc.SerialNumber.ToCStr()); + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDevice::initInfo() +{ + // Device must have been successfully opened. + OVR_ASSERT(DeviceHandle >= 0); + + int desc_size = 0; + hidraw_report_descriptor rpt_desc; + memset(&rpt_desc, 0, sizeof(rpt_desc)); + + // get report descriptor size + int r = ioctl(DeviceHandle, HIDIOCGRDESCSIZE, &desc_size); + if (r < 0) + { + OVR_ASSERT_LOG(false, ("Failed to get report descriptor size.")); + return false; + } + + // Get the report descriptor + rpt_desc.size = desc_size; + r = ioctl(DeviceHandle, HIDIOCGRDESC, &rpt_desc); + if (r < 0) + { + OVR_ASSERT_LOG(false, ("Failed to get report descriptor.")); + return false; + } + + /* + // Get report lengths. + SInt32 bufferLength; + bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + InputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + OutputReportBufferLength = (UInt16) bufferLength; + + getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength); + OVR_ASSERT(getResult); + FeatureReportBufferLength = (UInt16) bufferLength; + + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + // Get device desc. + if (!HIDManager->getFullDesc(Device, &DevDesc)) + { + OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); + return false; + } + + return true; + */ + + // Get report lengths. +// TODO: hard-coded for now. Need to interpret these values from the report descriptor + InputReportBufferLength = 62; + OutputReportBufferLength = 0; + FeatureReportBufferLength = 69; + + if (ReadBufferSize < InputReportBufferLength) + { + OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +bool HIDDevice::openDevice(const char* device_path) +{ + // First fill out the device descriptor + if (!HIDManager->GetDescriptorFromPath(device_path, &DevDesc)) + { + return false; + } + + // Now open the device + DeviceHandle = open(device_path, O_RDWR); + if (DeviceHandle < 0) + { + OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", errno)); + DeviceHandle = -1; + return false; + } + + // fill out some values from the feature report descriptor + if (!initInfo()) + { + OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info.")); + + close(DeviceHandle); + DeviceHandle = -1; + return false; + } + + // Add the device to the polling list + if (!HIDManager->DevManager->pThread->AddSelectFd(this, DeviceHandle)) + { + OVR_ASSERT_LOG(false, ("Failed to initialize polling for HIDDevice.")); + + close(DeviceHandle); + DeviceHandle = -1; + return false; + } + + return true; +} + +//----------------------------------------------------------------------------- +void HIDDevice::HIDShutdown() +{ + + HIDManager->DevManager->pThread->RemoveTicksNotifier(this); + HIDManager->RemoveNotificationDevice(this); + + if (DeviceHandle >= 0) // Device may already have been closed if unplugged. + { + closeDevice(false); + } + + LogText("OVR::Linux::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr()); +} + +//----------------------------------------------------------------------------- +void HIDDevice::closeDevice(bool wasUnplugged) +{ + OVR_UNUSED(wasUnplugged); + OVR_ASSERT(DeviceHandle >= 0); + + + HIDManager->DevManager->pThread->RemoveSelectFd(this, DeviceHandle); + + close(DeviceHandle); // close the file handle + DeviceHandle = -1; + + LogText("OVR::Linux::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr()); +} + +//----------------------------------------------------------------------------- +void HIDDevice::closeDeviceOnIOError() +{ + LogText("OVR::Linux::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr()); + closeDevice(false); +} + +//----------------------------------------------------------------------------- +bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) +{ + + if (DeviceHandle < 0) + return false; + + UByte reportID = data[0]; + + if (reportID == 0) + { + // Not using reports so remove from data packet. + data++; + length--; + } + + int r = ioctl(DeviceHandle, HIDIOCSFEATURE(length), data); + return (r >= 0); +} + +//----------------------------------------------------------------------------- +bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) +{ + if (DeviceHandle < 0) + return false; + + int r = ioctl(DeviceHandle, HIDIOCGFEATURE(length), data); + return (r >= 0); +} + +//----------------------------------------------------------------------------- +double HIDDevice::OnTicks(double tickSeconds) +{ + if (Handler) + { + return Handler->OnTicks(tickSeconds); + } + + return DeviceManagerThread::Notifier::OnTicks(tickSeconds); +} + +//----------------------------------------------------------------------------- +void HIDDevice::OnEvent(int i, int fd) +{ + OVR_UNUSED(i); + // We have data to read from the device + int bytes = read(fd, ReadBuffer, ReadBufferSize); + if (bytes >= 0) + { +// TODO: I need to handle partial messages and package reconstruction + if (Handler) + { + Handler->OnInputReport(ReadBuffer, bytes); + } + } + else + { // Close the device on read error. + closeDeviceOnIOError(); + } +} + +//----------------------------------------------------------------------------- +bool HIDDevice::OnDeviceNotification(MessageType messageType, + HIDDeviceDesc* device_info, + bool* error) +{ + const char* device_path = device_info->Path.ToCStr(); + + if (messageType == Message_DeviceAdded && DeviceHandle < 0) + { + // Is this the correct device? + if (!(device_info->VendorId == DevDesc.VendorId + && device_info->ProductId == DevDesc.ProductId + && device_info->SerialNumber == DevDesc.SerialNumber)) + { + return false; + } + + // A closed device has been re-added. Try to reopen. + if (!openDevice(device_path)) + { + LogError("OVR::Linux::HIDDevice - Failed to reopen a device '%s' that was re-added.\n", + device_path); + *error = true; + return true; + } + + LogText("OVR::Linux::HIDDevice - Reopened device '%s'\n", device_path); + + if (Handler) + { + Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceAdded); + } + } + else if (messageType == Message_DeviceRemoved) + { + // Is this the correct device? + // For disconnected device, the device description will be invalid so + // checking the path is the only way to match them + if (DevDesc.Path.CompareNoCase(device_path) != 0) + { + return false; + } + + if (DeviceHandle >= 0) + { + closeDevice(true); + } + + if (Handler) + { + Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceRemoved); + } + } + else + { + OVR_ASSERT(0); + } + + *error = false; + return true; +} + +//----------------------------------------------------------------------------- +HIDDeviceManager* HIDDeviceManager::CreateInternal(Linux::DeviceManager* devManager) +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr<Linux::HIDDeviceManager> manager = *new Linux::HIDDeviceManager(devManager); + + if (manager) + { + if (manager->Initialize()) + { + manager->AddRef(); + } + else + { + manager.Clear(); + } + } + + return manager.GetPtr(); +} + +} // namespace Linux + +//------------------------------------------------------------------------------------- +// ***** Creation + +// Creates a new HIDDeviceManager and initializes OVR. +HIDDeviceManager* HIDDeviceManager::Create(Ptr<OVR::DeviceManager>& deviceManager) +{ + + if (!System::IsInitialized()) + { + // Use custom message, since Log is not yet installed. + OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> + LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); + return 0; + } + + Ptr<Linux::DeviceManager> deviceManagerLinux = *new Linux::DeviceManager; + + if (!deviceManagerLinux) + { + return NULL; + } + + if (!deviceManagerLinux->Initialize(NULL)) + { + return NULL; + } + + deviceManager = deviceManagerLinux; + + return deviceManagerLinux->GetHIDDeviceManager(); +} + +} // namespace OVR diff --git a/LibOVR/Src/OVR_Linux_HIDDevice.h b/LibOVR/Src/OVR_Linux_HIDDevice.h new file mode 100644 index 0000000..52f2d69 --- /dev/null +++ b/LibOVR/Src/OVR_Linux_HIDDevice.h @@ -0,0 +1,135 @@ +/************************************************************************************ +Filename : OVR_Linux_HIDDevice.h +Content : Linux HID device implementation. +Created : June 13, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_LINUX_HIDDevice_h +#define OVR_LINUX_HIDDevice_h + +#include "OVR_HIDDevice.h" +#include "OVR_Linux_DeviceManager.h" +#include <libudev.h> + +namespace OVR { namespace Linux { + +class HIDDeviceManager; + +//------------------------------------------------------------------------------------- +// ***** Linux HIDDevice + +class HIDDevice : public OVR::HIDDevice, public DeviceManagerThread::Notifier +{ +private: + friend class HIDDeviceManager; + +public: + HIDDevice(HIDDeviceManager* manager); + + // This is a minimal constructor used during enumeration for us to pass + // a HIDDevice to the visit function (so that it can query feature reports). + HIDDevice(HIDDeviceManager* manager, int device_handle); + + virtual ~HIDDevice(); + + bool HIDInitialize(const String& path); + void HIDShutdown(); + + virtual bool SetFeatureReport(UByte* data, UInt32 length); + virtual bool GetFeatureReport(UByte* data, UInt32 length); + + // DeviceManagerThread::Notifier + void OnEvent(int i, int fd); + double OnTicks(double tickSeconds); + + bool OnDeviceNotification(MessageType messageType, + HIDDeviceDesc* device_info, + bool* error); + +private: + bool initInfo(); + bool openDevice(const char* dev_path); + void closeDevice(bool wasUnplugged); + void closeDeviceOnIOError(); + bool setupDevicePluggedInNotification(); + + bool InMinimalMode; + HIDDeviceManager* HIDManager; + int DeviceHandle; // file handle to the device + HIDDeviceDesc DevDesc; + + enum { ReadBufferSize = 96 }; + UByte ReadBuffer[ReadBufferSize]; + + UInt16 InputReportBufferLength; + UInt16 OutputReportBufferLength; + UInt16 FeatureReportBufferLength; +}; + + +//------------------------------------------------------------------------------------- +// ***** Linux HIDDeviceManager + +class HIDDeviceManager : public OVR::HIDDeviceManager, public DeviceManagerThread::Notifier +{ + friend class HIDDevice; + +public: + HIDDeviceManager(Linux::DeviceManager* Manager); + virtual ~HIDDeviceManager(); + + virtual bool Initialize(); + virtual void Shutdown(); + + virtual bool Enumerate(HIDEnumerateVisitor* enumVisitor); + virtual OVR::HIDDevice* Open(const String& path); + + static HIDDeviceManager* CreateInternal(DeviceManager* manager); + + void OnEvent(int i, int fd); + +private: + bool initializeManager(); + bool initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc); + bool getPath(udev_device* device, String* pPath); + bool getIntProperty(udev_device* device, const char* key, int32_t* pResult); + bool getStringProperty(udev_device* device, + const char* propertyName, + OVR::String* pResult); + bool getFullDesc(udev_device* device, HIDDeviceDesc* desc); + bool GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc); + + bool AddNotificationDevice(HIDDevice* device); + bool RemoveNotificationDevice(HIDDevice* device); + + DeviceManager* DevManager; + + udev* UdevInstance; // a handle to the udev library instance + udev_monitor* HIDMonitor; + int HIDMonHandle; // the udev_monitor file handle + + Array<HIDDevice*> NotificationDevices; +}; + +}} // namespace OVR::Linux + +#endif // OVR_Linux_HIDDevice_h diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.cpp b/LibOVR/Src/OVR_Linux_HMDDevice.cpp new file mode 100644 index 0000000..98143d3 --- /dev/null +++ b/LibOVR/Src/OVR_Linux_HMDDevice.cpp @@ -0,0 +1,291 @@ +/************************************************************************************ + +Filename : OVR_Linux_HMDDevice.h +Content : Linux HMDDevice implementation +Created : June 17, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OVR_Linux_HMDDevice.h" + +#include "OVR_Linux_DeviceManager.h" + +#include "OVR_Profile.h" + +#include "../../3rdParty/EDID/edid.h" + +namespace OVR { namespace Linux { + +//------------------------------------------------------------------------------------- + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(DeviceFactory* factory, const String& displayDeviceName, long dispId) + : DeviceCreateDesc(factory, Device_HMD), + DisplayDeviceName(displayDeviceName), + Contents(0), + DisplayId(dispId) +{ + DeviceId = DisplayDeviceName; + + Desktop.X = 0; + Desktop.Y = 0; + ResolutionInPixels = Sizei(0); + ScreenSizeInMeters = Sizef(0.0f); + VCenterFromTopInMeters = 0.0f; + LensSeparationInMeters = 0.0f; +} + +HMDDeviceCreateDesc::HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other) + : DeviceCreateDesc(other.pFactory, Device_HMD), + DeviceId(other.DeviceId), DisplayDeviceName(other.DisplayDeviceName), + Contents(other.Contents), + DisplayId(other.DisplayId) +{ + Desktop.X = other.Desktop.X; + Desktop.Y = other.Desktop.Y; + ResolutionInPixels = other.ResolutionInPixels; + ScreenSizeInMeters = other.ScreenSizeInMeters; + VCenterFromTopInMeters = other.VCenterFromTopInMeters; + LensSeparationInMeters = other.LensSeparationInMeters; +} + +HMDDeviceCreateDesc::MatchResult HMDDeviceCreateDesc::MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc** pcandidate) const +{ + if ((other.Type != Device_HMD) || (other.pFactory != pFactory)) + return Match_None; + + // There are several reasons we can come in here: + // a) Matching this HMD Monitor created desc to OTHER HMD Monitor desc + // - Require exact device DeviceId/DeviceName match + // b) Matching SensorDisplayInfo created desc to OTHER HMD Monitor desc + // - This DeviceId is empty; becomes candidate + // c) Matching this HMD Monitor created desc to SensorDisplayInfo desc + // - This other.DeviceId is empty; becomes candidate + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + if ((DeviceId == s2.DeviceId) && + (DisplayId == s2.DisplayId)) + { + // Non-null DeviceId may match while size is different if screen size was overwritten + // by SensorDisplayInfo in prior iteration. + if (!DeviceId.IsEmpty() || + (ScreenSizeInMeters == s2.ScreenSizeInMeters) ) + { + *pcandidate = 0; + return Match_Found; + } + } + + + // DisplayInfo takes precedence, although we try to match it first. + if ((ResolutionInPixels == s2.ResolutionInPixels) && + (ScreenSizeInMeters == s2.ScreenSizeInMeters)) + { + if (DeviceId.IsEmpty() && !s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + *pcandidate = 0; + return Match_Found; + } + + // SensorDisplayInfo may override resolution settings, so store as candidate. + if (s2.DeviceId.IsEmpty()) + { + *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this); + return Match_Candidate; + } + // OTHER HMD Monitor desc may initialize DeviceName/Id + else if (DeviceId.IsEmpty()) + { + *pcandidate = const_cast<DeviceCreateDesc*>((const DeviceCreateDesc*)this); + return Match_Candidate; + } + + return Match_None; +} + + +bool HMDDeviceCreateDesc::UpdateMatchedCandidate(const DeviceCreateDesc& other, + bool* newDeviceFlag) +{ + // This candidate was the the "best fit" to apply sensor DisplayInfo to. + OVR_ASSERT(other.Type == Device_HMD); + + const HMDDeviceCreateDesc& s2 = (const HMDDeviceCreateDesc&) other; + + // Force screen size on resolution from SensorDisplayInfo. + // We do this because USB detection is more reliable as compared to HDMI EDID, + // which may be corrupted by splitter reporting wrong monitor + if (s2.DeviceId.IsEmpty()) + { + ScreenSizeInMeters = s2.ScreenSizeInMeters; + Contents |= Contents_Screen; + + if (s2.Contents & HMDDeviceCreateDesc::Contents_Distortion) + { + memcpy(DistortionK, s2.DistortionK, sizeof(float)*4); + // TODO: DistortionEqn + Contents |= Contents_Distortion; + } + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + Desktop.X = s2.Desktop.X; + Desktop.Y = s2.Desktop.Y; + if (newDeviceFlag) *newDeviceFlag = true; + } + else if (DeviceId.IsEmpty()) + { + // This branch is executed when 'fake' HMD descriptor is being replaced by + // the real one. + DeviceId = s2.DeviceId; + DisplayId = s2.DisplayId; + DisplayDeviceName = s2.DisplayDeviceName; + Desktop.X = s2.Desktop.X; + Desktop.Y = s2.Desktop.Y; + + // ScreenSize and Resolution are NOT assigned here, since they may have + // come from a sensor DisplayInfo (which has precedence over HDMI). + + if (newDeviceFlag) *newDeviceFlag = true; + } + else + { + if (newDeviceFlag) *newDeviceFlag = false; + } + + return true; +} + +bool HMDDeviceCreateDesc::MatchDevice(const String& path) +{ + return DeviceId.CompareNoCase(path) == 0; +} + +//------------------------------------------------------------------------------------- +// ***** HMDDeviceFactory + +HMDDeviceFactory &HMDDeviceFactory::GetInstance() +{ + static HMDDeviceFactory instance; + return instance; +} + +void HMDDeviceFactory::EnumerateDevices(EnumerateVisitor& visitor) +{ + // For now we'll assume the Rift DK1 is attached in extended monitor mode. Ultimately we need to + // use XFree86 to enumerate X11 screens in case the Rift is attached as a separate screen. + + bool foundHMD = false; + Display* display = XOpenDisplay(NULL); + XRRScreenResources *screen = XRRGetScreenResources(display, DefaultRootWindow(display)); + for (int iscres = screen->noutput - 1; iscres >= 0; --iscres) { + RROutput output = screen->outputs[iscres]; + MonitorInfo * mi = read_edid_data(display, output); + if (mi == NULL) { + continue; + } + + XRROutputInfo * info = XRRGetOutputInfo (display, screen, output); + if (info && (0 == memcmp(mi->manufacturer_code, "OVR", 3))) { + + // Generate a device ID string similar to the way Windows does it + char device_id[32]; + OVR_sprintf(device_id, 32, "%s%04d", mi->manufacturer_code, mi->product_code); + + // The default monitor coordinates + int mx = 0; + int my = 0; + int mwidth = 1280; + int mheight = 800; + + if (info->connection == RR_Connected && info->crtc) { + XRRCrtcInfo * crtc_info = XRRGetCrtcInfo (display, screen, info->crtc); + if (crtc_info) + { + mx = crtc_info->x; + my = crtc_info->y; + //mwidth = crtc_info->width; + //mheight = crtc_info->height; + XRRFreeCrtcInfo(crtc_info); + } + } + + String deviceID = device_id; + HMDDeviceCreateDesc hmdCreateDesc(this, deviceID, iscres); + + // Hard-coded defaults in case the device doesn't have the data itself. + if (strstr(device_id, "OVR0003")) + { // DK2 prototypes and variants (default to HmdType_DK2) + hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12576f, 0.07074f, 0.12576f*0.5f, 0.0635f ); + } + else if (strstr(device_id, "OVR0002")) + { // HD Prototypes (default to HmdType_DKHDProto) + hmdCreateDesc.SetScreenParameters(mx, my, 1920, 1080, 0.12096f, 0.06804f, 0.06804f*0.5f, 0.0635f ); + } + else if (strstr(device_id, "OVR0001")) + { // DK1 + hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f); + } + else if (strstr(device_id, "OVR00")) + { // Future Oculus HMD devices (default to DK1 dimensions) + hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.14976f, 0.0936f, 0.0936f*0.5f, 0.0635f); + } + else + { // Duct-tape prototype + hmdCreateDesc.SetScreenParameters(mx, my, mwidth, mheight, 0.12096f, 0.0756f, 0.0756f*0.5f, 0.0635f); + } + + OVR_DEBUG_LOG_TEXT(("DeviceManager - HMD Found %s - %s\n", device_id, mi->dsc_product_name)); + + // Notify caller about detected device. This will call EnumerateAddDevice + // if the this is the first time device was detected. + visitor.Visit(hmdCreateDesc); + foundHMD = true; + break; + } // if + + XRRFreeOutputInfo(info); + delete mi; + } // for + XRRFreeScreenResources(screen); + + + // Real HMD device is not found; however, we still may have a 'fake' HMD + // device created via SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo. + // Need to find it and set 'Enumerated' to true to avoid Removal notification. + if (!foundHMD) + { + Ptr<DeviceCreateDesc> hmdDevDesc = getManager()->FindDevice("", Device_HMD); + if (hmdDevDesc) + hmdDevDesc->Enumerated = true; + } +} + +#include "OVR_Common_HMDDevice.cpp" + +}} // namespace OVR::Linux + + diff --git a/LibOVR/Src/OVR_Linux_HMDDevice.h b/LibOVR/Src/OVR_Linux_HMDDevice.h new file mode 100644 index 0000000..a8c044f --- /dev/null +++ b/LibOVR/Src/OVR_Linux_HMDDevice.h @@ -0,0 +1,154 @@ +/************************************************************************************ + +Filename : OVR_Linux_HMDDevice.h +Content : Linux HMDDevice implementation +Created : June 17, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#ifndef OVR_Linux_HMDDevice_h +#define OVR_Linux_HMDDevice_h + +#include "OVR_Linux_DeviceManager.h" +#include "OVR_Profile.h" + +namespace OVR { namespace Linux { + +class HMDDevice; + +//------------------------------------------------------------------------------------- + +// HMDDeviceFactory enumerates attached Oculus HMD devices. +// +// This is currently done by matching monitor device strings. + +class HMDDeviceFactory : public DeviceFactory +{ +public: + static HMDDeviceFactory &GetInstance(); + + // Enumerates devices, creating and destroying relevant objects in manager. + virtual void EnumerateDevices(EnumerateVisitor& visitor); + +protected: + DeviceManager* getManager() const { return (DeviceManager*) pManager; } +}; + + +class HMDDeviceCreateDesc : public DeviceCreateDesc +{ + friend class HMDDevice; + +protected: + enum + { + Contents_Screen = 1, + Contents_Distortion = 2, + }; + String DeviceId; + String DisplayDeviceName; + struct + { + int X, Y; + } Desktop; + unsigned int Contents; + + Sizei ResolutionInPixels; + Sizef ScreenSizeInMeters; + float VCenterFromTopInMeters; + float LensSeparationInMeters; + + // TODO: update these to splines. + DistortionEqnType DistortionEqn; + float DistortionK[4]; + + long DisplayId; + +public: + HMDDeviceCreateDesc(DeviceFactory* factory, + const String& displayDeviceName, long dispId); + HMDDeviceCreateDesc(const HMDDeviceCreateDesc& other); + + virtual DeviceCreateDesc* Clone() const + { + return new HMDDeviceCreateDesc(*this); + } + + virtual DeviceBase* NewDeviceInstance(); + + virtual MatchResult MatchDevice(const DeviceCreateDesc& other, + DeviceCreateDesc**) const; + + // Matches device by path. + virtual bool MatchDevice(const String& path); + + virtual bool UpdateMatchedCandidate(const DeviceCreateDesc&, bool* newDeviceFlag = NULL); + + virtual bool GetDeviceInfo(DeviceInfo* info) const; + + void SetScreenParameters(int x, int y, + int hres, int vres, + float hsize, float vsize, + float vCenterFromTopInMeters, float lensSeparationInMeters); + void SetDistortion(const float* dks); + + HmdTypeEnum GetHmdType() const; +}; + + +//------------------------------------------------------------------------------------- + +// HMDDevice represents an Oculus HMD device unit. An instance of this class +// is typically created from the DeviceManager. +// After HMD device is created, we its sensor data can be obtained by +// first creating a Sensor object and then wrappig it in SensorFusion. + +class HMDDevice : public DeviceImpl<OVR::HMDDevice> +{ +public: + HMDDevice(HMDDeviceCreateDesc* createDesc); + ~HMDDevice(); + + virtual bool Initialize(DeviceBase* parent); + virtual void Shutdown(); + + // Requests the currently used default profile. This profile affects the + // settings reported by HMDInfo. + virtual Profile* GetProfile(); + virtual const char* GetProfileName(); + virtual bool SetProfileName(const char* name); + + // Query associated sensor. + virtual OVR::SensorDevice* GetSensor(); + +protected: + HMDDeviceCreateDesc* getDesc() const { return (HMDDeviceCreateDesc*)pCreateDesc.GetPtr(); } + + // User name for the profile used with this device. + String ProfileName; + mutable Ptr<Profile> pCachedProfile; +}; + + +}} // namespace OVR::Linux + +#endif // OVR_Linux_HMDDevice_h + diff --git a/LibOVR/Src/OVR_Linux_SensorDevice.cpp b/LibOVR/Src/OVR_Linux_SensorDevice.cpp new file mode 100644 index 0000000..5b671a6 --- /dev/null +++ b/LibOVR/Src/OVR_Linux_SensorDevice.cpp @@ -0,0 +1,57 @@ +/************************************************************************************ + +Filename : OVR_Linux_SensorDevice.cpp +Content : Linux SensorDevice implementation +Created : June 13, 2013 +Authors : Brant Lewis + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*************************************************************************************/ + +#include "OVR_Linux_HMDDevice.h" +#include "OVR_SensorImpl.h" +#include "OVR_DeviceImpl.h" + +namespace OVR { namespace Linux { + +} // namespace Linux + +//------------------------------------------------------------------------------------- +void SensorDeviceImpl::EnumerateHMDFromSensorDisplayInfo( const SensorDisplayInfoImpl& displayInfo, + DeviceFactory::EnumerateVisitor& visitor) +{ + Linux::HMDDeviceCreateDesc hmdCreateDesc(&Linux::HMDDeviceFactory::GetInstance(), String(), 0); + + hmdCreateDesc.SetScreenParameters( 0, 0, + displayInfo.HResolution, displayInfo.VResolution, + displayInfo.HScreenSize, displayInfo.VScreenSize, + displayInfo.VCenter, displayInfo.LensSeparation); + + if ((displayInfo.DistortionType & SensorDisplayInfoImpl::Mask_BaseFmt) == SensorDisplayInfoImpl::Base_Distortion) + { + // TODO: update to spline system. + hmdCreateDesc.SetDistortion(displayInfo.DistortionK); + } + + visitor.Visit(hmdCreateDesc); +} + +} // namespace OVR + + |