nuclear@1: /************************************************************************************ nuclear@1: nuclear@1: Filename : OVR_Win32_DeviceManager.cpp nuclear@1: Content : Win32 implementation of DeviceManager. nuclear@1: Created : September 21, 2012 nuclear@1: Authors : Michael Antonov nuclear@1: nuclear@1: Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved. nuclear@1: nuclear@1: Use of this software is subject to the terms of the Oculus license nuclear@1: agreement provided at the time of installation or download, or which nuclear@1: otherwise accompanies this software in either electronic or hard copy form. nuclear@1: nuclear@1: *************************************************************************************/ nuclear@1: nuclear@1: #include "OVR_Win32_DeviceManager.h" nuclear@1: nuclear@1: // Sensor & HMD Factories nuclear@1: #include "OVR_SensorImpl.h" nuclear@1: #include "OVR_LatencyTestImpl.h" nuclear@1: #include "OVR_Win32_HMDDevice.h" nuclear@1: #include "OVR_Win32_DeviceStatus.h" nuclear@1: #include "OVR_Win32_HIDDevice.h" nuclear@1: nuclear@1: #include "Kernel/OVR_Timer.h" nuclear@1: #include "Kernel/OVR_Std.h" nuclear@1: #include "Kernel/OVR_Log.h" nuclear@1: nuclear@1: DWORD Debug_WaitedObjectCount = 0; nuclear@1: nuclear@1: namespace OVR { namespace Win32 { nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // **** Win32::DeviceManager nuclear@1: nuclear@1: DeviceManager::DeviceManager() nuclear@1: { nuclear@1: HidDeviceManager = *HIDDeviceManager::CreateInternal(this); nuclear@1: } nuclear@1: nuclear@1: DeviceManager::~DeviceManager() nuclear@1: { nuclear@1: // make sure Shutdown was called. nuclear@1: OVR_ASSERT(!pThread); nuclear@1: } nuclear@1: nuclear@1: bool DeviceManager::Initialize(DeviceBase*) nuclear@1: { nuclear@1: if (!DeviceManagerImpl::Initialize(0)) nuclear@1: return false; nuclear@1: nuclear@1: pThread = *new DeviceManagerThread(this); nuclear@1: if (!pThread || !pThread->Start()) nuclear@1: return false; nuclear@1: nuclear@1: pCreateDesc->pDevice = this; nuclear@1: LogText("OVR::DeviceManager - initialized.\n"); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: void DeviceManager::Shutdown() nuclear@1: { nuclear@1: LogText("OVR::DeviceManager - shutting down.\n"); nuclear@1: nuclear@1: // Set Manager shutdown marker variable; this prevents nuclear@1: // any existing DeviceHandle objects from accessing device. nuclear@1: pCreateDesc->pLock->pManager = 0; nuclear@1: nuclear@1: // Push for thread shutdown *WITH NO WAIT*. nuclear@1: // This will have the following effect: nuclear@1: // - Exit command will get enqueued, which will be executed later on the thread itself. nuclear@1: // - Beyond this point, this DeviceManager object may be deleted by our caller. nuclear@1: // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will nuclear@1: // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued nuclear@1: // after pManager is null. nuclear@1: // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last nuclear@1: // reference to the thread object. nuclear@1: pThread->PushExitCommand(false); nuclear@1: pThread->DetachDeviceManager(); nuclear@1: pThread.Clear(); nuclear@1: nuclear@1: DeviceManagerImpl::Shutdown(); nuclear@1: } nuclear@1: nuclear@1: ThreadCommandQueue* DeviceManager::GetThreadQueue() nuclear@1: { nuclear@1: return pThread; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const nuclear@1: { nuclear@1: if ((info->InfoClassType != Device_Manager) && nuclear@1: (info->InfoClassType != Device_None)) nuclear@1: return false; nuclear@1: nuclear@1: info->Type = Device_Manager; nuclear@1: info->Version = 0; nuclear@1: OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager"); nuclear@1: OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc."); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args) nuclear@1: { nuclear@1: // TBD: Can this be avoided in the future, once proper device notification is in place? nuclear@1: if (GetThreadId() != OVR::GetCurrentThreadId()) nuclear@1: { nuclear@1: pThread->PushCall((DeviceManagerImpl*)this, nuclear@1: &DeviceManager::EnumerateAllFactoryDevices, true); nuclear@1: } nuclear@1: else nuclear@1: DeviceManager::EnumerateAllFactoryDevices(); nuclear@1: nuclear@1: return DeviceManagerImpl::EnumerateDevicesEx(args); nuclear@1: } nuclear@1: nuclear@1: ThreadId DeviceManager::GetThreadId() const nuclear@1: { nuclear@1: return pThread->GetThreadId(); nuclear@1: } nuclear@1: nuclear@1: bool DeviceManager::GetHIDDeviceDesc(const String& path, HIDDeviceDesc* pdevDesc) const nuclear@1: { nuclear@1: if (GetHIDDeviceManager()) nuclear@1: return static_cast(GetHIDDeviceManager())->GetHIDDeviceDesc(path, pdevDesc); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** DeviceManager Thread nuclear@1: nuclear@1: DeviceManagerThread::DeviceManagerThread(DeviceManager* pdevMgr) nuclear@1: : Thread(ThreadStackSize), hCommandEvent(0), pDeviceMgr(pdevMgr) nuclear@1: { nuclear@1: // Create a non-signaled manual-reset event. nuclear@1: hCommandEvent = ::CreateEvent(0, TRUE, FALSE, 0); nuclear@1: if (!hCommandEvent) nuclear@1: return; nuclear@1: nuclear@1: // Must add event before starting. nuclear@1: AddOverlappedEvent(0, hCommandEvent); nuclear@1: nuclear@1: // Create device messages object. nuclear@1: pStatusObject = *new DeviceStatus(this); nuclear@1: } nuclear@1: nuclear@1: DeviceManagerThread::~DeviceManagerThread() nuclear@1: { nuclear@1: // Remove overlapped event [0], after thread service exit. nuclear@1: if (hCommandEvent) nuclear@1: { nuclear@1: RemoveOverlappedEvent(0, hCommandEvent); nuclear@1: ::CloseHandle(hCommandEvent); nuclear@1: hCommandEvent = 0; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: int DeviceManagerThread::Run() nuclear@1: { nuclear@1: ThreadCommand::PopBuffer command; nuclear@1: nuclear@1: SetThreadName("OVR::DeviceManagerThread"); nuclear@1: LogText("OVR::DeviceManagerThread - running (ThreadId=0x%X).\n", GetThreadId()); nuclear@1: nuclear@1: if (!pStatusObject->Initialize()) nuclear@1: { nuclear@1: LogText("OVR::DeviceManagerThread - failed to initialize MessageObject.\n"); nuclear@1: } nuclear@1: nuclear@1: while(!IsExiting()) nuclear@1: { nuclear@1: // PopCommand will reset event on empty queue. nuclear@1: if (PopCommand(&command)) nuclear@1: { nuclear@1: command.Execute(); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: DWORD eventIndex = 0; nuclear@1: do { nuclear@1: UPInt numberOfWaitHandles = WaitHandles.GetSize(); nuclear@1: Debug_WaitedObjectCount = (DWORD)numberOfWaitHandles; nuclear@1: nuclear@1: DWORD waitMs = INFINITE; nuclear@1: nuclear@1: // If devices have time-dependent logic registered, get the longest wait nuclear@1: // allowed based on current ticks. nuclear@1: if (!TicksNotifiers.IsEmpty()) nuclear@1: { nuclear@1: UInt64 ticksMks = Timer::GetTicks(); nuclear@1: DWORD waitAllowed; nuclear@1: nuclear@1: for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++) nuclear@1: { nuclear@1: waitAllowed = (DWORD)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs); nuclear@1: if (waitAllowed < waitMs) nuclear@1: waitMs = waitAllowed; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Wait for event signals or window messages. nuclear@1: eventIndex = MsgWaitForMultipleObjects((DWORD)numberOfWaitHandles, &WaitHandles[0], FALSE, waitMs, QS_ALLINPUT); nuclear@1: nuclear@1: if (eventIndex != WAIT_FAILED) nuclear@1: { nuclear@1: if (eventIndex == WAIT_TIMEOUT) nuclear@1: continue; nuclear@1: nuclear@1: // TBD: Does this ever apply? nuclear@1: OVR_ASSERT(eventIndex < WAIT_ABANDONED_0); nuclear@1: nuclear@1: if (eventIndex == WAIT_OBJECT_0) nuclear@1: { nuclear@1: // Handle [0] services commands. nuclear@1: break; nuclear@1: } nuclear@1: else if (eventIndex == WAIT_OBJECT_0 + numberOfWaitHandles) nuclear@1: { nuclear@1: // Handle Windows messages. nuclear@1: pStatusObject->ProcessMessages(); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: // Notify waiting device that its event is signaled. nuclear@1: unsigned i = eventIndex - WAIT_OBJECT_0; nuclear@1: OVR_ASSERT(i < numberOfWaitHandles); nuclear@1: if (WaitNotifiers[i]) nuclear@1: WaitNotifiers[i]->OnOverlappedEvent(WaitHandles[i]); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: } while(eventIndex != WAIT_FAILED); nuclear@1: nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: pStatusObject->ShutDown(); nuclear@1: nuclear@1: LogText("OVR::DeviceManagerThread - exiting (ThreadId=0x%X).\n", GetThreadId()); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManagerThread::AddOverlappedEvent(Notifier* notify, HANDLE hevent) nuclear@1: { nuclear@1: WaitNotifiers.PushBack(notify); nuclear@1: WaitHandles.PushBack(hevent); nuclear@1: nuclear@1: OVR_ASSERT(WaitNotifiers.GetSize() <= MAXIMUM_WAIT_OBJECTS); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManagerThread::RemoveOverlappedEvent(Notifier* notify, HANDLE hevent) nuclear@1: { nuclear@1: // [0] is reserved for thread commands with notify of null, but we still nuclear@1: // can use this function to remove it. nuclear@1: for (UPInt i = 0; i < WaitNotifiers.GetSize(); i++) nuclear@1: { nuclear@1: if ((WaitNotifiers[i] == notify) && (WaitHandles[i] == hevent)) nuclear@1: { nuclear@1: WaitNotifiers.RemoveAt(i); nuclear@1: WaitHandles.RemoveAt(i); nuclear@1: return true; nuclear@1: } nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManagerThread::AddTicksNotifier(Notifier* notify) nuclear@1: { nuclear@1: TicksNotifiers.PushBack(notify); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify) nuclear@1: { nuclear@1: for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++) nuclear@1: { nuclear@1: if (TicksNotifiers[i] == notify) nuclear@1: { nuclear@1: TicksNotifiers.RemoveAt(i); nuclear@1: return true; nuclear@1: } nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManagerThread::AddMessageNotifier(Notifier* notify) nuclear@1: { nuclear@1: MessageNotifiers.PushBack(notify); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManagerThread::RemoveMessageNotifier(Notifier* notify) nuclear@1: { nuclear@1: for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++) nuclear@1: { nuclear@1: if (MessageNotifiers[i] == notify) nuclear@1: { nuclear@1: MessageNotifiers.RemoveAt(i); nuclear@1: return true; nuclear@1: } nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: bool DeviceManagerThread::OnMessage(MessageType type, const String& devicePath) nuclear@1: { nuclear@1: Notifier::DeviceMessageType notifierMessageType = Notifier::DeviceMessage_DeviceAdded; nuclear@1: if (type == DeviceAdded) nuclear@1: { nuclear@1: } nuclear@1: else if (type == DeviceRemoved) nuclear@1: { nuclear@1: notifierMessageType = Notifier::DeviceMessage_DeviceRemoved; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: OVR_ASSERT(false); nuclear@1: } nuclear@1: nuclear@1: bool error = false; nuclear@1: bool deviceFound = false; nuclear@1: for (UPInt i = 0; i < MessageNotifiers.GetSize(); i++) nuclear@1: { nuclear@1: if (MessageNotifiers[i] && nuclear@1: MessageNotifiers[i]->OnDeviceMessage(notifierMessageType, devicePath, &error)) nuclear@1: { nuclear@1: // The notifier belonged to a device with the specified device name so we're done. nuclear@1: deviceFound = true; nuclear@1: break; nuclear@1: } nuclear@1: } nuclear@1: if (type == DeviceAdded && !deviceFound) nuclear@1: { nuclear@1: Lock::Locker devMgrLock(&DevMgrLock); nuclear@1: // a new device was connected. Go through all device factories and nuclear@1: // try to detect the device using HIDDeviceDesc. nuclear@1: HIDDeviceDesc devDesc; nuclear@1: if (pDeviceMgr->GetHIDDeviceDesc(devicePath, &devDesc)) nuclear@1: { nuclear@1: Lock::Locker deviceLock(pDeviceMgr->GetLock()); nuclear@1: DeviceFactory* factory = pDeviceMgr->Factories.GetFirst(); nuclear@1: while(!pDeviceMgr->Factories.IsNull(factory)) nuclear@1: { nuclear@1: if (factory->DetectHIDDevice(pDeviceMgr, devDesc)) nuclear@1: { nuclear@1: deviceFound = true; nuclear@1: break; nuclear@1: } nuclear@1: factory = factory->pNext; nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: if (!deviceFound && strstr(devicePath.ToCStr(), "#OVR00")) nuclear@1: { nuclear@1: Ptr pmgr; nuclear@1: { nuclear@1: Lock::Locker devMgrLock(&DevMgrLock); nuclear@1: pmgr = pDeviceMgr; nuclear@1: } nuclear@1: // HMD plugged/unplugged nuclear@1: // This is not a final solution to enumerate HMD devices and get nuclear@1: // a first available handle. This won't work with multiple rifts. nuclear@1: // @TODO (!AB) nuclear@1: pmgr->EnumerateDevices(); nuclear@1: } nuclear@1: nuclear@1: return !error; nuclear@1: } nuclear@1: nuclear@1: void DeviceManagerThread::DetachDeviceManager() nuclear@1: { nuclear@1: Lock::Locker devMgrLock(&DevMgrLock); nuclear@1: pDeviceMgr = NULL; nuclear@1: } nuclear@1: nuclear@1: } // namespace Win32 nuclear@1: nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** Creation nuclear@1: nuclear@1: nuclear@1: // Creates a new DeviceManager and initializes OVR. nuclear@1: DeviceManager* DeviceManager::Create() nuclear@1: { nuclear@1: nuclear@1: if (!System::IsInitialized()) nuclear@1: { nuclear@1: // Use custom message, since Log is not yet installed. nuclear@1: OVR_DEBUG_STATEMENT(Log::GetDefaultLog()-> nuclear@1: LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); ); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: Ptr manager = *new Win32::DeviceManager; nuclear@1: nuclear@1: if (manager) nuclear@1: { nuclear@1: if (manager->Initialize(0)) nuclear@1: { nuclear@1: manager->AddFactory(&SensorDeviceFactory::Instance); nuclear@1: manager->AddFactory(&LatencyTestDeviceFactory::Instance); nuclear@1: manager->AddFactory(&Win32::HMDDeviceFactory::Instance); nuclear@1: nuclear@1: manager->AddRef(); nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: manager.Clear(); nuclear@1: } nuclear@1: nuclear@1: } nuclear@1: nuclear@1: return manager.GetPtr(); nuclear@1: } nuclear@1: nuclear@1: nuclear@1: } // namespace OVR nuclear@1: