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
|