summaryrefslogtreecommitdiffstats
path: root/LibOVR/Src
diff options
context:
space:
mode:
authorBrad Davis <[email protected]>2014-05-23 01:52:35 -0700
committerBrad Davis <[email protected]>2014-05-23 01:52:35 -0700
commit1393f96fa857a59ca495e51ce8c704eb36e3eed7 (patch)
tree6c6a55b09df48d2ed5f8532c4403ea6f084d284b /LibOVR/Src
parent46acc0e194ff3c1f120199eeca8324b4502118e9 (diff)
Adding 0.3.2 Linux specific files
Diffstat (limited to 'LibOVR/Src')
-rw-r--r--LibOVR/Src/Kernel/OVR_ThreadsPthread.cpp787
-rw-r--r--LibOVR/Src/OVR_Linux_DeviceManager.cpp331
-rw-r--r--LibOVR/Src/OVR_Linux_DeviceManager.h122
-rw-r--r--LibOVR/Src/OVR_Linux_HIDDevice.cpp819
-rw-r--r--LibOVR/Src/OVR_Linux_HIDDevice.h135
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.cpp291
-rw-r--r--LibOVR/Src/OVR_Linux_HMDDevice.h154
-rw-r--r--LibOVR/Src/OVR_Linux_SensorDevice.cpp57
8 files changed, 2696 insertions, 0 deletions
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
+
+