oculus1

annotate libovr/Src/linux/OVR_Linux_DeviceManager.cpp @ 23:0c76f70fb7e9

merged
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 28 Sep 2013 04:13:33 +0300
parents
children
rev   line source
nuclear@1 1 /************************************************************************************
nuclear@1 2
nuclear@1 3 Filename : OVR_Linux_DeviceManager.h
nuclear@1 4 Content : Linux implementation of DeviceManager.
nuclear@1 5 Created :
nuclear@1 6 Authors :
nuclear@1 7
nuclear@1 8 Copyright : Copyright 2012 Oculus VR, Inc. All Rights reserved.
nuclear@1 9
nuclear@1 10 Use of this software is subject to the terms of the Oculus license
nuclear@1 11 agreement provided at the time of installation or download, or which
nuclear@1 12 otherwise accompanies this software in either electronic or hard copy form.
nuclear@1 13
nuclear@1 14 *************************************************************************************/
nuclear@1 15
nuclear@1 16 #include "OVR_Linux_DeviceManager.h"
nuclear@1 17
nuclear@1 18 // Sensor & HMD Factories
nuclear@1 19 #include "OVR_LatencyTestImpl.h"
nuclear@1 20 #include "OVR_SensorImpl.h"
nuclear@1 21 #include "OVR_Linux_HIDDevice.h"
nuclear@1 22 #include "OVR_Linux_HMDDevice.h"
nuclear@1 23
nuclear@1 24 #include "Kernel/OVR_Timer.h"
nuclear@1 25 #include "Kernel/OVR_Std.h"
nuclear@1 26 #include "Kernel/OVR_Log.h"
nuclear@1 27
nuclear@1 28 namespace OVR { namespace Linux {
nuclear@1 29
nuclear@1 30
nuclear@1 31 //-------------------------------------------------------------------------------------
nuclear@1 32 // **** Linux::DeviceManager
nuclear@1 33
nuclear@1 34 DeviceManager::DeviceManager()
nuclear@1 35 {
nuclear@1 36 }
nuclear@1 37
nuclear@1 38 DeviceManager::~DeviceManager()
nuclear@1 39 {
nuclear@1 40 }
nuclear@1 41
nuclear@1 42 bool DeviceManager::Initialize(DeviceBase*)
nuclear@1 43 {
nuclear@1 44 if (!DeviceManagerImpl::Initialize(0))
nuclear@1 45 return false;
nuclear@1 46
nuclear@1 47 pThread = *new DeviceManagerThread();
nuclear@1 48 if (!pThread || !pThread->Start())
nuclear@1 49 return false;
nuclear@1 50
nuclear@1 51 // Wait for the thread to be fully up and running.
nuclear@1 52 pThread->StartupEvent.Wait();
nuclear@1 53
nuclear@1 54 // Do this now that we know the thread's run loop.
nuclear@1 55 HidDeviceManager = *HIDDeviceManager::CreateInternal(this);
nuclear@1 56
nuclear@1 57 pCreateDesc->pDevice = this;
nuclear@1 58 LogText("OVR::DeviceManager - initialized.\n");
nuclear@1 59 return true;
nuclear@1 60 }
nuclear@1 61
nuclear@1 62 void DeviceManager::Shutdown()
nuclear@1 63 {
nuclear@1 64 LogText("OVR::DeviceManager - shutting down.\n");
nuclear@1 65
nuclear@1 66 // Set Manager shutdown marker variable; this prevents
nuclear@1 67 // any existing DeviceHandle objects from accessing device.
nuclear@1 68 pCreateDesc->pLock->pManager = 0;
nuclear@1 69
nuclear@1 70 // Push for thread shutdown *WITH NO WAIT*.
nuclear@1 71 // This will have the following effect:
nuclear@1 72 // - Exit command will get enqueued, which will be executed later on the thread itself.
nuclear@1 73 // - Beyond this point, this DeviceManager object may be deleted by our caller.
nuclear@1 74 // - Other commands, such as CreateDevice, may execute before ExitCommand, but they will
nuclear@1 75 // fail gracefully due to pLock->pManager == 0. Future commands can't be enqued
nuclear@1 76 // after pManager is null.
nuclear@1 77 // - Once ExitCommand executes, ThreadCommand::Run loop will exit and release the last
nuclear@1 78 // reference to the thread object.
nuclear@1 79 pThread->PushExitCommand(false);
nuclear@1 80 pThread.Clear();
nuclear@1 81
nuclear@1 82 DeviceManagerImpl::Shutdown();
nuclear@1 83 }
nuclear@1 84
nuclear@1 85 ThreadCommandQueue* DeviceManager::GetThreadQueue()
nuclear@1 86 {
nuclear@1 87 return pThread;
nuclear@1 88 }
nuclear@1 89
nuclear@1 90 ThreadId DeviceManager::GetThreadId() const
nuclear@1 91 {
nuclear@1 92 return pThread->GetThreadId();
nuclear@1 93 }
nuclear@1 94
nuclear@1 95 bool DeviceManager::GetDeviceInfo(DeviceInfo* info) const
nuclear@1 96 {
nuclear@1 97 if ((info->InfoClassType != Device_Manager) &&
nuclear@1 98 (info->InfoClassType != Device_None))
nuclear@1 99 return false;
nuclear@1 100
nuclear@1 101 info->Type = Device_Manager;
nuclear@1 102 info->Version = 0;
nuclear@1 103 OVR_strcpy(info->ProductName, DeviceInfo::MaxNameLength, "DeviceManager");
nuclear@1 104 OVR_strcpy(info->Manufacturer,DeviceInfo::MaxNameLength, "Oculus VR, Inc.");
nuclear@1 105 return true;
nuclear@1 106 }
nuclear@1 107
nuclear@1 108 DeviceEnumerator<> DeviceManager::EnumerateDevicesEx(const DeviceEnumerationArgs& args)
nuclear@1 109 {
nuclear@1 110 // TBD: Can this be avoided in the future, once proper device notification is in place?
nuclear@1 111 pThread->PushCall((DeviceManagerImpl*)this,
nuclear@1 112 &DeviceManager::EnumerateAllFactoryDevices, true);
nuclear@1 113
nuclear@1 114 return DeviceManagerImpl::EnumerateDevicesEx(args);
nuclear@1 115 }
nuclear@1 116
nuclear@1 117
nuclear@1 118 //-------------------------------------------------------------------------------------
nuclear@1 119 // ***** DeviceManager Thread
nuclear@1 120
nuclear@1 121 DeviceManagerThread::DeviceManagerThread()
nuclear@1 122 : Thread(ThreadStackSize)
nuclear@1 123 {
nuclear@1 124 int result = pipe(CommandFd);
nuclear@1 125 OVR_ASSERT(!result);
nuclear@1 126
nuclear@1 127 AddSelectFd(NULL, CommandFd[0]);
nuclear@1 128 }
nuclear@1 129
nuclear@1 130 DeviceManagerThread::~DeviceManagerThread()
nuclear@1 131 {
nuclear@1 132 if (CommandFd[0])
nuclear@1 133 {
nuclear@1 134 RemoveSelectFd(NULL, CommandFd[0]);
nuclear@1 135 close(CommandFd[0]);
nuclear@1 136 close(CommandFd[1]);
nuclear@1 137 }
nuclear@1 138 }
nuclear@1 139
nuclear@1 140 bool DeviceManagerThread::AddSelectFd(Notifier* notify, int fd)
nuclear@1 141 {
nuclear@1 142 struct pollfd pfd;
nuclear@1 143 pfd.fd = fd;
nuclear@1 144 pfd.events = POLLIN|POLLHUP|POLLERR;
nuclear@1 145 pfd.revents = 0;
nuclear@1 146
nuclear@1 147 FdNotifiers.PushBack(notify);
nuclear@1 148 PollFds.PushBack(pfd);
nuclear@1 149
nuclear@1 150 OVR_ASSERT(FdNotifiers.GetSize() == PollFds.GetSize());
nuclear@1 151 return true;
nuclear@1 152 }
nuclear@1 153
nuclear@1 154 bool DeviceManagerThread::RemoveSelectFd(Notifier* notify, int fd)
nuclear@1 155 {
nuclear@1 156 // [0] is reserved for thread commands with notify of null, but we still
nuclear@1 157 // can use this function to remove it.
nuclear@1 158 for (UPInt i = 0; i < FdNotifiers.GetSize(); i++)
nuclear@1 159 {
nuclear@1 160 if ((FdNotifiers[i] == notify) && (PollFds[i].fd == fd))
nuclear@1 161 {
nuclear@1 162 FdNotifiers.RemoveAt(i);
nuclear@1 163 PollFds.RemoveAt(i);
nuclear@1 164 return true;
nuclear@1 165 }
nuclear@1 166 }
nuclear@1 167 return false;
nuclear@1 168 }
nuclear@1 169
nuclear@1 170
nuclear@1 171
nuclear@1 172 int DeviceManagerThread::Run()
nuclear@1 173 {
nuclear@1 174 ThreadCommand::PopBuffer command;
nuclear@1 175
nuclear@1 176 SetThreadName("OVR::DeviceManagerThread");
nuclear@1 177 LogText("OVR::DeviceManagerThread - running (ThreadId=%p).\n", GetThreadId());
nuclear@1 178
nuclear@1 179 // Signal to the parent thread that initialization has finished.
nuclear@1 180 StartupEvent.SetEvent();
nuclear@1 181
nuclear@1 182 while(!IsExiting())
nuclear@1 183 {
nuclear@1 184 // PopCommand will reset event on empty queue.
nuclear@1 185 if (PopCommand(&command))
nuclear@1 186 {
nuclear@1 187 command.Execute();
nuclear@1 188 }
nuclear@1 189 else
nuclear@1 190 {
nuclear@1 191 bool commands = 0;
nuclear@1 192 do
nuclear@1 193 {
nuclear@1 194 int waitMs = -1;
nuclear@1 195
nuclear@1 196 // If devices have time-dependent logic registered, get the longest wait
nuclear@1 197 // allowed based on current ticks.
nuclear@1 198 if (!TicksNotifiers.IsEmpty())
nuclear@1 199 {
nuclear@1 200 UInt64 ticksMks = Timer::GetTicks();
nuclear@1 201 int waitAllowed;
nuclear@1 202
nuclear@1 203 for (UPInt j = 0; j < TicksNotifiers.GetSize(); j++)
nuclear@1 204 {
nuclear@1 205 waitAllowed = (int)(TicksNotifiers[j]->OnTicks(ticksMks) / Timer::MksPerMs);
nuclear@1 206 if (waitAllowed < waitMs)
nuclear@1 207 waitMs = waitAllowed;
nuclear@1 208 }
nuclear@1 209 }
nuclear@1 210
nuclear@1 211 // wait until there is data available on one of the devices or the timeout expires
nuclear@1 212 int n = poll(&PollFds[0], PollFds.GetSize(), waitMs);
nuclear@1 213
nuclear@1 214 if (n > 0)
nuclear@1 215 {
nuclear@1 216 // Iterate backwards through the list so the ordering will not be
nuclear@1 217 // affected if the called object gets removed during the callback
nuclear@1 218 // Also, the HID data streams are located toward the back of the list
nuclear@1 219 // and servicing them first will allow a disconnect to be handled
nuclear@1 220 // and cleaned directly at the device first instead of the general HID monitor
nuclear@1 221 for (int i=PollFds.GetSize()-1; i>=0; i--)
nuclear@1 222 {
nuclear@1 223 if (PollFds[i].revents & POLLERR)
nuclear@1 224 {
nuclear@1 225 OVR_DEBUG_LOG(("poll: error on [%d]: %d", i, PollFds[i].fd));
nuclear@1 226 }
nuclear@1 227 else if (PollFds[i].revents & POLLIN)
nuclear@1 228 {
nuclear@1 229 if (FdNotifiers[i])
nuclear@1 230 FdNotifiers[i]->OnEvent(i, PollFds[i].fd);
nuclear@1 231 else if (i == 0) // command
nuclear@1 232 {
nuclear@1 233 char dummy[128];
nuclear@1 234 read(PollFds[i].fd, dummy, 128);
nuclear@1 235 commands = 1;
nuclear@1 236 }
nuclear@1 237 }
nuclear@1 238
nuclear@1 239 if (PollFds[i].revents & POLLHUP)
nuclear@1 240 PollFds[i].events = 0;
nuclear@1 241
nuclear@1 242 if (PollFds[i].revents != 0)
nuclear@1 243 {
nuclear@1 244 n--;
nuclear@1 245 if (n == 0)
nuclear@1 246 break;
nuclear@1 247 }
nuclear@1 248 }
nuclear@1 249 }
nuclear@1 250 } while (PollFds.GetSize() > 0 && !commands);
nuclear@1 251 }
nuclear@1 252 }
nuclear@1 253
nuclear@1 254 LogText("OVR::DeviceManagerThread - exiting (ThreadId=%p).\n", GetThreadId());
nuclear@1 255 return 0;
nuclear@1 256 }
nuclear@1 257
nuclear@1 258 bool DeviceManagerThread::AddTicksNotifier(Notifier* notify)
nuclear@1 259 {
nuclear@1 260 TicksNotifiers.PushBack(notify);
nuclear@1 261 return true;
nuclear@1 262 }
nuclear@1 263
nuclear@1 264 bool DeviceManagerThread::RemoveTicksNotifier(Notifier* notify)
nuclear@1 265 {
nuclear@1 266 for (UPInt i = 0; i < TicksNotifiers.GetSize(); i++)
nuclear@1 267 {
nuclear@1 268 if (TicksNotifiers[i] == notify)
nuclear@1 269 {
nuclear@1 270 TicksNotifiers.RemoveAt(i);
nuclear@1 271 return true;
nuclear@1 272 }
nuclear@1 273 }
nuclear@1 274 return false;
nuclear@1 275 }
nuclear@1 276
nuclear@1 277 } // namespace Linux
nuclear@1 278
nuclear@1 279
nuclear@1 280 //-------------------------------------------------------------------------------------
nuclear@1 281 // ***** Creation
nuclear@1 282
nuclear@1 283
nuclear@1 284 // Creates a new DeviceManager and initializes OVR.
nuclear@1 285 DeviceManager* DeviceManager::Create()
nuclear@1 286 {
nuclear@1 287 if (!System::IsInitialized())
nuclear@1 288 {
nuclear@1 289 // Use custom message, since Log is not yet installed.
nuclear@1 290 OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
nuclear@1 291 LogMessage(Log_Debug, "DeviceManager::Create failed - OVR::System not initialized"); );
nuclear@1 292 return 0;
nuclear@1 293 }
nuclear@1 294
nuclear@1 295 Ptr<Linux::DeviceManager> manager = *new Linux::DeviceManager;
nuclear@1 296
nuclear@1 297 if (manager)
nuclear@1 298 {
nuclear@1 299 if (manager->Initialize(0))
nuclear@1 300 {
nuclear@1 301 manager->AddFactory(&LatencyTestDeviceFactory::Instance);
nuclear@1 302 manager->AddFactory(&SensorDeviceFactory::Instance);
nuclear@1 303 manager->AddFactory(&Linux::HMDDeviceFactory::Instance);
nuclear@1 304
nuclear@1 305 manager->AddRef();
nuclear@1 306 }
nuclear@1 307 else
nuclear@1 308 {
nuclear@1 309 manager.Clear();
nuclear@1 310 }
nuclear@1 311
nuclear@1 312 }
nuclear@1 313
nuclear@1 314 return manager.GetPtr();
nuclear@1 315 }
nuclear@1 316
nuclear@1 317
nuclear@1 318 } // namespace OVR
nuclear@1 319