rev |
line source |
nuclear@1
|
1 /************************************************************************************
|
nuclear@1
|
2 Filename : OVR_Linux_HIDDevice.cpp
|
nuclear@1
|
3 Content : Linux HID device implementation.
|
nuclear@1
|
4 Created : February 26, 2013
|
nuclear@1
|
5 Authors : Lee Cooper
|
nuclear@1
|
6
|
nuclear@1
|
7 Copyright : Copyright 2013 Oculus VR, Inc. All Rights reserved.
|
nuclear@1
|
8
|
nuclear@1
|
9 Use of this software is subject to the terms of the Oculus license
|
nuclear@1
|
10 agreement provided at the time of installation or download, or which
|
nuclear@1
|
11 otherwise accompanies this software in either electronic or hard copy form.
|
nuclear@1
|
12
|
nuclear@1
|
13 *************************************************************************************/
|
nuclear@1
|
14
|
nuclear@1
|
15 #include "OVR_Linux_HIDDevice.h"
|
nuclear@1
|
16
|
nuclear@1
|
17 #include <sys/ioctl.h>
|
nuclear@1
|
18 #include <fcntl.h>
|
nuclear@1
|
19 #include <errno.h>
|
nuclear@1
|
20 #include <linux/hidraw.h>
|
nuclear@1
|
21 #include "OVR_HIDDeviceImpl.h"
|
nuclear@1
|
22
|
nuclear@1
|
23 namespace OVR { namespace Linux {
|
nuclear@1
|
24
|
nuclear@1
|
25 static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5;
|
nuclear@1
|
26
|
nuclear@1
|
27 //-------------------------------------------------------------------------------------
|
nuclear@1
|
28 // **** Linux::DeviceManager
|
nuclear@1
|
29 //-----------------------------------------------------------------------------
|
nuclear@1
|
30 HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) : DevManager(manager)
|
nuclear@1
|
31 {
|
nuclear@1
|
32 UdevInstance = NULL;
|
nuclear@1
|
33 HIDMonitor = NULL;
|
nuclear@1
|
34 HIDMonHandle = -1;
|
nuclear@1
|
35 }
|
nuclear@1
|
36
|
nuclear@1
|
37 //-----------------------------------------------------------------------------
|
nuclear@1
|
38 HIDDeviceManager::~HIDDeviceManager()
|
nuclear@1
|
39 {
|
nuclear@1
|
40 }
|
nuclear@1
|
41
|
nuclear@1
|
42 //-----------------------------------------------------------------------------
|
nuclear@1
|
43 bool HIDDeviceManager::initializeManager()
|
nuclear@1
|
44 {
|
nuclear@1
|
45 if (HIDMonitor)
|
nuclear@1
|
46 {
|
nuclear@1
|
47 return true;
|
nuclear@1
|
48 }
|
nuclear@1
|
49
|
nuclear@1
|
50 // Create a udev_monitor handle to watch for device changes (hot-plug detection)
|
nuclear@1
|
51 HIDMonitor = udev_monitor_new_from_netlink(UdevInstance, "udev");
|
nuclear@1
|
52 if (HIDMonitor == NULL)
|
nuclear@1
|
53 {
|
nuclear@1
|
54 return false;
|
nuclear@1
|
55 }
|
nuclear@1
|
56
|
nuclear@1
|
57 udev_monitor_filter_add_match_subsystem_devtype(HIDMonitor, "hidraw", NULL); // filter for hidraw only
|
nuclear@1
|
58
|
nuclear@1
|
59 int err = udev_monitor_enable_receiving(HIDMonitor);
|
nuclear@1
|
60 if (err)
|
nuclear@1
|
61 {
|
nuclear@1
|
62 udev_monitor_unref(HIDMonitor);
|
nuclear@1
|
63 HIDMonitor = NULL;
|
nuclear@1
|
64 return false;
|
nuclear@1
|
65 }
|
nuclear@1
|
66
|
nuclear@1
|
67 // Get the file descriptor (fd) for the monitor.
|
nuclear@1
|
68 HIDMonHandle = udev_monitor_get_fd(HIDMonitor);
|
nuclear@1
|
69 if (HIDMonHandle < 0)
|
nuclear@1
|
70 {
|
nuclear@1
|
71 udev_monitor_unref(HIDMonitor);
|
nuclear@1
|
72 HIDMonitor = NULL;
|
nuclear@1
|
73 return false;
|
nuclear@1
|
74 }
|
nuclear@1
|
75
|
nuclear@1
|
76 // This file handle will be polled along-side with the device hid handles for changes
|
nuclear@1
|
77 // Add the handle to the polling list
|
nuclear@1
|
78 if (!DevManager->pThread->AddSelectFd(this, HIDMonHandle))
|
nuclear@1
|
79 {
|
nuclear@1
|
80 close(HIDMonHandle);
|
nuclear@1
|
81 HIDMonHandle = -1;
|
nuclear@1
|
82
|
nuclear@1
|
83 udev_monitor_unref(HIDMonitor);
|
nuclear@1
|
84 HIDMonitor = NULL;
|
nuclear@1
|
85 return false;
|
nuclear@1
|
86 }
|
nuclear@1
|
87
|
nuclear@1
|
88 return true;
|
nuclear@1
|
89 }
|
nuclear@1
|
90
|
nuclear@1
|
91 //-----------------------------------------------------------------------------
|
nuclear@1
|
92 bool HIDDeviceManager::Initialize()
|
nuclear@1
|
93 {
|
nuclear@1
|
94 // Get a udev library handle. This handle must stay active during the
|
nuclear@1
|
95 // duration the lifetime of device monitoring handles
|
nuclear@1
|
96 UdevInstance = udev_new();
|
nuclear@1
|
97 if (!UdevInstance)
|
nuclear@1
|
98 return false;
|
nuclear@1
|
99
|
nuclear@1
|
100 return initializeManager();
|
nuclear@1
|
101 }
|
nuclear@1
|
102
|
nuclear@1
|
103 //-----------------------------------------------------------------------------
|
nuclear@1
|
104 void HIDDeviceManager::Shutdown()
|
nuclear@1
|
105 {
|
nuclear@1
|
106 OVR_ASSERT_LOG((UdevInstance), ("Should have called 'Initialize' before 'Shutdown'."));
|
nuclear@1
|
107
|
nuclear@1
|
108 if (HIDMonitor)
|
nuclear@1
|
109 {
|
nuclear@1
|
110 DevManager->pThread->RemoveSelectFd(this, HIDMonHandle);
|
nuclear@1
|
111 close(HIDMonHandle);
|
nuclear@1
|
112 HIDMonHandle = -1;
|
nuclear@1
|
113
|
nuclear@1
|
114 udev_monitor_unref(HIDMonitor);
|
nuclear@1
|
115 HIDMonitor = NULL;
|
nuclear@1
|
116 }
|
nuclear@1
|
117
|
nuclear@1
|
118 udev_unref(UdevInstance); // release the library
|
nuclear@1
|
119
|
nuclear@1
|
120 LogText("OVR::Linux::HIDDeviceManager - shutting down.\n");
|
nuclear@1
|
121 }
|
nuclear@1
|
122
|
nuclear@1
|
123 //-------------------------------------------------------------------------------
|
nuclear@1
|
124 bool HIDDeviceManager::AddNotificationDevice(HIDDevice* device)
|
nuclear@1
|
125 {
|
nuclear@1
|
126 NotificationDevices.PushBack(device);
|
nuclear@1
|
127 return true;
|
nuclear@1
|
128 }
|
nuclear@1
|
129
|
nuclear@1
|
130 //-------------------------------------------------------------------------------
|
nuclear@1
|
131 bool HIDDeviceManager::RemoveNotificationDevice(HIDDevice* device)
|
nuclear@1
|
132 {
|
nuclear@1
|
133 for (UPInt i = 0; i < NotificationDevices.GetSize(); i++)
|
nuclear@1
|
134 {
|
nuclear@1
|
135 if (NotificationDevices[i] == device)
|
nuclear@1
|
136 {
|
nuclear@1
|
137 NotificationDevices.RemoveAt(i);
|
nuclear@1
|
138 return true;
|
nuclear@1
|
139 }
|
nuclear@1
|
140 }
|
nuclear@1
|
141 return false;
|
nuclear@1
|
142 }
|
nuclear@1
|
143
|
nuclear@1
|
144 //-----------------------------------------------------------------------------
|
nuclear@1
|
145 bool HIDDeviceManager::getIntProperty(udev_device* device,
|
nuclear@1
|
146 const char* propertyName,
|
nuclear@1
|
147 SInt32* pResult)
|
nuclear@1
|
148 {
|
nuclear@1
|
149 const char* str = udev_device_get_sysattr_value(device, propertyName);
|
nuclear@1
|
150 if (str)
|
nuclear@1
|
151 {
|
nuclear@1
|
152 *pResult = strtol(str, NULL, 16);
|
nuclear@1
|
153 return true;
|
nuclear@1
|
154 }
|
nuclear@1
|
155 else
|
nuclear@1
|
156 {
|
nuclear@1
|
157 *pResult = 0;
|
nuclear@1
|
158 return true;
|
nuclear@1
|
159 }
|
nuclear@1
|
160 }
|
nuclear@1
|
161
|
nuclear@1
|
162 //-----------------------------------------------------------------------------
|
nuclear@1
|
163 bool HIDDeviceManager::initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc)
|
nuclear@1
|
164 {
|
nuclear@1
|
165 SInt32 result;
|
nuclear@1
|
166 if (getIntProperty(device, "idVendor", &result))
|
nuclear@1
|
167 pDevDesc->VendorId = result;
|
nuclear@1
|
168 else
|
nuclear@1
|
169 return false;
|
nuclear@1
|
170
|
nuclear@1
|
171 if (getIntProperty(device, "idProduct", &result))
|
nuclear@1
|
172 pDevDesc->ProductId = result;
|
nuclear@1
|
173 else
|
nuclear@1
|
174 return false;
|
nuclear@1
|
175
|
nuclear@1
|
176 return true;
|
nuclear@1
|
177 }
|
nuclear@1
|
178
|
nuclear@1
|
179 //-----------------------------------------------------------------------------
|
nuclear@1
|
180 bool HIDDeviceManager::getStringProperty(udev_device* device,
|
nuclear@1
|
181 const char* propertyName,
|
nuclear@1
|
182 OVR::String* pResult)
|
nuclear@1
|
183 {
|
nuclear@1
|
184 // Get the attribute in UTF8
|
nuclear@1
|
185 const char* str = udev_device_get_sysattr_value(device, propertyName);
|
nuclear@1
|
186 if (str)
|
nuclear@1
|
187 { // Copy the string into the return value
|
nuclear@1
|
188 *pResult = String(str);
|
nuclear@1
|
189 return true;
|
nuclear@1
|
190 }
|
nuclear@1
|
191 else
|
nuclear@1
|
192 {
|
nuclear@1
|
193 return false;
|
nuclear@1
|
194 }
|
nuclear@1
|
195 }
|
nuclear@1
|
196
|
nuclear@1
|
197 //-----------------------------------------------------------------------------
|
nuclear@1
|
198 bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
|
nuclear@1
|
199 {
|
nuclear@1
|
200
|
nuclear@1
|
201 if (!initializeManager())
|
nuclear@1
|
202 {
|
nuclear@1
|
203 return false;
|
nuclear@1
|
204 }
|
nuclear@1
|
205
|
nuclear@1
|
206 // Get a list of hid devices
|
nuclear@1
|
207 udev_enumerate* devices = udev_enumerate_new(UdevInstance);
|
nuclear@1
|
208 udev_enumerate_add_match_subsystem(devices, "hidraw");
|
nuclear@1
|
209 udev_enumerate_scan_devices(devices);
|
nuclear@1
|
210
|
nuclear@1
|
211 udev_list_entry* entry = udev_enumerate_get_list_entry(devices);
|
nuclear@1
|
212
|
nuclear@1
|
213 // Search each device for the matching vid/pid
|
nuclear@1
|
214 while (entry != NULL)
|
nuclear@1
|
215 {
|
nuclear@1
|
216 // Get the device file name
|
nuclear@1
|
217 const char* sysfs_path = udev_list_entry_get_name(entry);
|
nuclear@1
|
218 udev_device* hid; // The device's HID udev node.
|
nuclear@1
|
219 hid = udev_device_new_from_syspath(UdevInstance, sysfs_path);
|
nuclear@1
|
220 const char* dev_path = udev_device_get_devnode(hid);
|
nuclear@1
|
221
|
nuclear@1
|
222 // Get the USB device
|
nuclear@1
|
223 hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
|
nuclear@1
|
224 if (hid)
|
nuclear@1
|
225 {
|
nuclear@1
|
226 HIDDeviceDesc devDesc;
|
nuclear@1
|
227
|
nuclear@1
|
228 // Check the VID/PID for a match
|
nuclear@1
|
229 if (dev_path &&
|
nuclear@1
|
230 initVendorProductVersion(hid, &devDesc) &&
|
nuclear@1
|
231 enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId))
|
nuclear@1
|
232 {
|
nuclear@1
|
233 devDesc.Path = dev_path;
|
nuclear@1
|
234 getFullDesc(hid, &devDesc);
|
nuclear@1
|
235
|
nuclear@1
|
236 // Look for the device to check if it is already opened.
|
nuclear@1
|
237 Ptr<DeviceCreateDesc> existingDevice = DevManager->FindHIDDevice(devDesc);
|
nuclear@1
|
238 // if device exists and it is opened then most likely the device open()
|
nuclear@1
|
239 // will fail; therefore, we just set Enumerated to 'true' and continue.
|
nuclear@1
|
240 if (existingDevice && existingDevice->pDevice)
|
nuclear@1
|
241 {
|
nuclear@1
|
242 existingDevice->Enumerated = true;
|
nuclear@1
|
243 }
|
nuclear@1
|
244 else
|
nuclear@1
|
245 { // open the device temporarily for startup communication
|
nuclear@1
|
246 int device_handle = open(dev_path, O_RDWR);
|
nuclear@1
|
247 if (device_handle >= 0)
|
nuclear@1
|
248 {
|
nuclear@1
|
249 // Construct minimal device that the visitor callback can get feature reports from
|
nuclear@1
|
250 Linux::HIDDevice device(this, device_handle);
|
nuclear@1
|
251 enumVisitor->Visit(device, devDesc);
|
nuclear@1
|
252
|
nuclear@1
|
253 close(device_handle); // close the file handle
|
nuclear@1
|
254 }
|
nuclear@1
|
255 }
|
nuclear@1
|
256 }
|
nuclear@1
|
257
|
nuclear@1
|
258 udev_device_unref(hid);
|
nuclear@1
|
259 entry = udev_list_entry_get_next(entry);
|
nuclear@1
|
260 }
|
nuclear@1
|
261 }
|
nuclear@1
|
262
|
nuclear@1
|
263 // Free the enumerator and udev objects
|
nuclear@1
|
264 udev_enumerate_unref(devices);
|
nuclear@1
|
265
|
nuclear@1
|
266 return true;
|
nuclear@1
|
267 }
|
nuclear@1
|
268
|
nuclear@1
|
269 //-----------------------------------------------------------------------------
|
nuclear@1
|
270 OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
|
nuclear@1
|
271 {
|
nuclear@1
|
272 Ptr<Linux::HIDDevice> device = *new Linux::HIDDevice(this);
|
nuclear@1
|
273
|
nuclear@1
|
274 if (device->HIDInitialize(path))
|
nuclear@1
|
275 {
|
nuclear@1
|
276 device->AddRef();
|
nuclear@1
|
277 return device;
|
nuclear@1
|
278 }
|
nuclear@1
|
279
|
nuclear@1
|
280 return NULL;
|
nuclear@1
|
281 }
|
nuclear@1
|
282
|
nuclear@1
|
283 //-----------------------------------------------------------------------------
|
nuclear@1
|
284 bool HIDDeviceManager::getFullDesc(udev_device* device, HIDDeviceDesc* desc)
|
nuclear@1
|
285 {
|
nuclear@1
|
286
|
nuclear@1
|
287 if (!initVendorProductVersion(device, desc))
|
nuclear@1
|
288 {
|
nuclear@1
|
289 return false;
|
nuclear@1
|
290 }
|
nuclear@1
|
291
|
nuclear@1
|
292 if (!getStringProperty(device, "serial", &(desc->SerialNumber)))
|
nuclear@1
|
293 {
|
nuclear@1
|
294 return false;
|
nuclear@1
|
295 }
|
nuclear@1
|
296
|
nuclear@1
|
297 getStringProperty(device, "manufacturer", &(desc->Manufacturer));
|
nuclear@1
|
298 getStringProperty(device, "product", &(desc->Product));
|
nuclear@1
|
299
|
nuclear@1
|
300 return true;
|
nuclear@1
|
301 }
|
nuclear@1
|
302
|
nuclear@1
|
303 //-----------------------------------------------------------------------------
|
nuclear@1
|
304 bool HIDDeviceManager::GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc)
|
nuclear@1
|
305 {
|
nuclear@1
|
306 if (!initializeManager())
|
nuclear@1
|
307 {
|
nuclear@1
|
308 return false;
|
nuclear@1
|
309 }
|
nuclear@1
|
310
|
nuclear@1
|
311 // Search for the udev device from the given pathname so we can
|
nuclear@1
|
312 // have a handle to query device properties
|
nuclear@1
|
313
|
nuclear@1
|
314 udev_enumerate* devices = udev_enumerate_new(UdevInstance);
|
nuclear@1
|
315 udev_enumerate_add_match_subsystem(devices, "hidraw");
|
nuclear@1
|
316 udev_enumerate_scan_devices(devices);
|
nuclear@1
|
317
|
nuclear@1
|
318 udev_list_entry* entry = udev_enumerate_get_list_entry(devices);
|
nuclear@1
|
319
|
nuclear@1
|
320 bool success = false;
|
nuclear@1
|
321 // Search for the device with the matching path
|
nuclear@1
|
322 while (entry != NULL)
|
nuclear@1
|
323 {
|
nuclear@1
|
324 // Get the device file name
|
nuclear@1
|
325 const char* sysfs_path = udev_list_entry_get_name(entry);
|
nuclear@1
|
326 udev_device* hid; // The device's HID udev node.
|
nuclear@1
|
327 hid = udev_device_new_from_syspath(UdevInstance, sysfs_path);
|
nuclear@1
|
328 const char* path = udev_device_get_devnode(hid);
|
nuclear@1
|
329
|
nuclear@1
|
330 if (OVR_strcmp(dev_path, path) == 0)
|
nuclear@1
|
331 { // Found the device so lets collect the device descriptor
|
nuclear@1
|
332
|
nuclear@1
|
333 // Get the USB device
|
nuclear@1
|
334 hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
|
nuclear@1
|
335 if (hid)
|
nuclear@1
|
336 {
|
nuclear@1
|
337 desc->Path = dev_path;
|
nuclear@1
|
338 success = getFullDesc(hid, desc);
|
nuclear@1
|
339 }
|
nuclear@1
|
340
|
nuclear@1
|
341 }
|
nuclear@1
|
342
|
nuclear@1
|
343 udev_device_unref(hid);
|
nuclear@1
|
344 entry = udev_list_entry_get_next(entry);
|
nuclear@1
|
345 }
|
nuclear@1
|
346
|
nuclear@1
|
347 // Free the enumerator
|
nuclear@1
|
348 udev_enumerate_unref(devices);
|
nuclear@1
|
349
|
nuclear@1
|
350 return success;
|
nuclear@1
|
351 }
|
nuclear@1
|
352
|
nuclear@1
|
353 //-----------------------------------------------------------------------------
|
nuclear@1
|
354 void HIDDeviceManager::OnEvent(int i, int fd)
|
nuclear@1
|
355 {
|
nuclear@1
|
356 // There is a device status change
|
nuclear@1
|
357 udev_device* hid = udev_monitor_receive_device(HIDMonitor);
|
nuclear@1
|
358 if (hid)
|
nuclear@1
|
359 {
|
nuclear@1
|
360 const char* dev_path = udev_device_get_devnode(hid);
|
nuclear@1
|
361 const char* action = udev_device_get_action(hid);
|
nuclear@1
|
362
|
nuclear@1
|
363 HIDDeviceDesc device_info;
|
nuclear@1
|
364 device_info.Path = dev_path;
|
nuclear@1
|
365
|
nuclear@1
|
366 MessageType notify_type;
|
nuclear@1
|
367 if (OVR_strcmp(action, "add") == 0)
|
nuclear@1
|
368 {
|
nuclear@1
|
369 notify_type = Message_DeviceAdded;
|
nuclear@1
|
370
|
nuclear@1
|
371 // Retrieve the device info. This can only be done on a connected
|
nuclear@1
|
372 // device and is invalid for a disconnected device
|
nuclear@1
|
373
|
nuclear@1
|
374 // Get the USB device
|
nuclear@1
|
375 hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device");
|
nuclear@1
|
376 if (!hid)
|
nuclear@1
|
377 {
|
nuclear@1
|
378 return;
|
nuclear@1
|
379 }
|
nuclear@1
|
380
|
nuclear@1
|
381 getFullDesc(hid, &device_info);
|
nuclear@1
|
382 }
|
nuclear@1
|
383 else if (OVR_strcmp(action, "remove") == 0)
|
nuclear@1
|
384 {
|
nuclear@1
|
385 notify_type = Message_DeviceRemoved;
|
nuclear@1
|
386 }
|
nuclear@1
|
387 else
|
nuclear@1
|
388 {
|
nuclear@1
|
389 return;
|
nuclear@1
|
390 }
|
nuclear@1
|
391
|
nuclear@1
|
392 bool error = false;
|
nuclear@1
|
393 bool deviceFound = false;
|
nuclear@1
|
394 for (UPInt i = 0; i < NotificationDevices.GetSize(); i++)
|
nuclear@1
|
395 {
|
nuclear@1
|
396 if (NotificationDevices[i] &&
|
nuclear@1
|
397 NotificationDevices[i]->OnDeviceNotification(notify_type, &device_info, &error))
|
nuclear@1
|
398 {
|
nuclear@1
|
399 // The notification was for an existing device
|
nuclear@1
|
400 deviceFound = true;
|
nuclear@1
|
401 break;
|
nuclear@1
|
402 }
|
nuclear@1
|
403 }
|
nuclear@1
|
404
|
nuclear@1
|
405 if (notify_type == Message_DeviceAdded && !deviceFound)
|
nuclear@1
|
406 {
|
nuclear@1
|
407 DevManager->DetectHIDDevice(device_info);
|
nuclear@1
|
408 }
|
nuclear@1
|
409
|
nuclear@1
|
410 udev_device_unref(hid);
|
nuclear@1
|
411 }
|
nuclear@1
|
412 }
|
nuclear@1
|
413
|
nuclear@1
|
414 //=============================================================================
|
nuclear@1
|
415 // Linux::HIDDevice
|
nuclear@1
|
416 //=============================================================================
|
nuclear@1
|
417 HIDDevice::HIDDevice(HIDDeviceManager* manager)
|
nuclear@1
|
418 : HIDManager(manager), InMinimalMode(false)
|
nuclear@1
|
419 {
|
nuclear@1
|
420 DeviceHandle = -1;
|
nuclear@1
|
421 }
|
nuclear@1
|
422
|
nuclear@1
|
423 //-----------------------------------------------------------------------------
|
nuclear@1
|
424 // This is a minimal constructor used during enumeration for us to pass
|
nuclear@1
|
425 // a HIDDevice to the visit function (so that it can query feature reports).
|
nuclear@1
|
426 HIDDevice::HIDDevice(HIDDeviceManager* manager, int device_handle)
|
nuclear@1
|
427 : HIDManager(manager), DeviceHandle(device_handle), InMinimalMode(true)
|
nuclear@1
|
428 {
|
nuclear@1
|
429 }
|
nuclear@1
|
430
|
nuclear@1
|
431 //-----------------------------------------------------------------------------
|
nuclear@1
|
432 HIDDevice::~HIDDevice()
|
nuclear@1
|
433 {
|
nuclear@1
|
434 if (!InMinimalMode)
|
nuclear@1
|
435 {
|
nuclear@1
|
436 HIDShutdown();
|
nuclear@1
|
437 }
|
nuclear@1
|
438 }
|
nuclear@1
|
439
|
nuclear@1
|
440 //-----------------------------------------------------------------------------
|
nuclear@1
|
441 bool HIDDevice::HIDInitialize(const String& path)
|
nuclear@1
|
442 {
|
nuclear@1
|
443 const char* hid_path = path.ToCStr();
|
nuclear@1
|
444 if (!openDevice(hid_path))
|
nuclear@1
|
445 {
|
nuclear@1
|
446 LogText("OVR::Linux::HIDDevice - Failed to open HIDDevice: %s", hid_path);
|
nuclear@1
|
447 return false;
|
nuclear@1
|
448 }
|
nuclear@1
|
449
|
nuclear@1
|
450 HIDManager->DevManager->pThread->AddTicksNotifier(this);
|
nuclear@1
|
451 HIDManager->AddNotificationDevice(this);
|
nuclear@1
|
452
|
nuclear@1
|
453 LogText("OVR::Linux::HIDDevice - Opened '%s'\n"
|
nuclear@1
|
454 " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
|
nuclear@1
|
455 DevDesc.Path.ToCStr(),
|
nuclear@1
|
456 DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
|
nuclear@1
|
457 DevDesc.SerialNumber.ToCStr());
|
nuclear@1
|
458
|
nuclear@1
|
459 return true;
|
nuclear@1
|
460 }
|
nuclear@1
|
461
|
nuclear@1
|
462 //-----------------------------------------------------------------------------
|
nuclear@1
|
463 bool HIDDevice::initInfo()
|
nuclear@1
|
464 {
|
nuclear@1
|
465 // Device must have been successfully opened.
|
nuclear@1
|
466 OVR_ASSERT(DeviceHandle >= 0);
|
nuclear@1
|
467
|
nuclear@1
|
468 int desc_size = 0;
|
nuclear@1
|
469 hidraw_report_descriptor rpt_desc;
|
nuclear@1
|
470 memset(&rpt_desc, 0, sizeof(rpt_desc));
|
nuclear@1
|
471
|
nuclear@1
|
472 // get report descriptor size
|
nuclear@1
|
473 int r = ioctl(DeviceHandle, HIDIOCGRDESCSIZE, &desc_size);
|
nuclear@1
|
474 if (r < 0)
|
nuclear@1
|
475 {
|
nuclear@1
|
476 OVR_ASSERT_LOG(false, ("Failed to get report descriptor size."));
|
nuclear@1
|
477 return false;
|
nuclear@1
|
478 }
|
nuclear@1
|
479
|
nuclear@1
|
480 // Get the report descriptor
|
nuclear@1
|
481 rpt_desc.size = desc_size;
|
nuclear@1
|
482 r = ioctl(DeviceHandle, HIDIOCGRDESC, &rpt_desc);
|
nuclear@1
|
483 if (r < 0)
|
nuclear@1
|
484 {
|
nuclear@1
|
485 OVR_ASSERT_LOG(false, ("Failed to get report descriptor."));
|
nuclear@1
|
486 return false;
|
nuclear@1
|
487 }
|
nuclear@1
|
488
|
nuclear@1
|
489 /*
|
nuclear@1
|
490 // Get report lengths.
|
nuclear@1
|
491 SInt32 bufferLength;
|
nuclear@1
|
492 bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength);
|
nuclear@1
|
493 OVR_ASSERT(getResult);
|
nuclear@1
|
494 InputReportBufferLength = (UInt16) bufferLength;
|
nuclear@1
|
495
|
nuclear@1
|
496 getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength);
|
nuclear@1
|
497 OVR_ASSERT(getResult);
|
nuclear@1
|
498 OutputReportBufferLength = (UInt16) bufferLength;
|
nuclear@1
|
499
|
nuclear@1
|
500 getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength);
|
nuclear@1
|
501 OVR_ASSERT(getResult);
|
nuclear@1
|
502 FeatureReportBufferLength = (UInt16) bufferLength;
|
nuclear@1
|
503
|
nuclear@1
|
504
|
nuclear@1
|
505 if (ReadBufferSize < InputReportBufferLength)
|
nuclear@1
|
506 {
|
nuclear@1
|
507 OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
|
nuclear@1
|
508 return false;
|
nuclear@1
|
509 }
|
nuclear@1
|
510
|
nuclear@1
|
511 // Get device desc.
|
nuclear@1
|
512 if (!HIDManager->getFullDesc(Device, &DevDesc))
|
nuclear@1
|
513 {
|
nuclear@1
|
514 OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
|
nuclear@1
|
515 return false;
|
nuclear@1
|
516 }
|
nuclear@1
|
517
|
nuclear@1
|
518 return true;
|
nuclear@1
|
519 */
|
nuclear@1
|
520
|
nuclear@1
|
521 // Get report lengths.
|
nuclear@1
|
522 // TODO: hard-coded for now. Need to interpret these values from the report descriptor
|
nuclear@1
|
523 InputReportBufferLength = 62;
|
nuclear@1
|
524 OutputReportBufferLength = 0;
|
nuclear@1
|
525 FeatureReportBufferLength = 69;
|
nuclear@1
|
526
|
nuclear@1
|
527 if (ReadBufferSize < InputReportBufferLength)
|
nuclear@1
|
528 {
|
nuclear@1
|
529 OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
|
nuclear@1
|
530 return false;
|
nuclear@1
|
531 }
|
nuclear@1
|
532
|
nuclear@1
|
533 return true;
|
nuclear@1
|
534 }
|
nuclear@1
|
535
|
nuclear@1
|
536 //-----------------------------------------------------------------------------
|
nuclear@1
|
537 bool HIDDevice::openDevice(const char* device_path)
|
nuclear@1
|
538 {
|
nuclear@1
|
539 // First fill out the device descriptor
|
nuclear@1
|
540 if (!HIDManager->GetDescriptorFromPath(device_path, &DevDesc))
|
nuclear@1
|
541 {
|
nuclear@1
|
542 return false;
|
nuclear@1
|
543 }
|
nuclear@1
|
544
|
nuclear@1
|
545 // Now open the device
|
nuclear@1
|
546 DeviceHandle = open(device_path, O_RDWR);
|
nuclear@1
|
547 if (DeviceHandle < 0)
|
nuclear@1
|
548 {
|
nuclear@1
|
549 OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", errno));
|
nuclear@1
|
550 DeviceHandle = -1;
|
nuclear@1
|
551 return false;
|
nuclear@1
|
552 }
|
nuclear@1
|
553
|
nuclear@1
|
554 // fill out some values from the feature report descriptor
|
nuclear@1
|
555 if (!initInfo())
|
nuclear@1
|
556 {
|
nuclear@1
|
557 OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info."));
|
nuclear@1
|
558
|
nuclear@1
|
559 close(DeviceHandle);
|
nuclear@1
|
560 DeviceHandle = -1;
|
nuclear@1
|
561 return false;
|
nuclear@1
|
562 }
|
nuclear@1
|
563
|
nuclear@1
|
564 // Add the device to the polling list
|
nuclear@1
|
565 if (!HIDManager->DevManager->pThread->AddSelectFd(this, DeviceHandle))
|
nuclear@1
|
566 {
|
nuclear@1
|
567 OVR_ASSERT_LOG(false, ("Failed to initialize polling for HIDDevice."));
|
nuclear@1
|
568
|
nuclear@1
|
569 close(DeviceHandle);
|
nuclear@1
|
570 DeviceHandle = -1;
|
nuclear@1
|
571 return false;
|
nuclear@1
|
572 }
|
nuclear@1
|
573
|
nuclear@1
|
574 return true;
|
nuclear@1
|
575 }
|
nuclear@1
|
576
|
nuclear@1
|
577 //-----------------------------------------------------------------------------
|
nuclear@1
|
578 void HIDDevice::HIDShutdown()
|
nuclear@1
|
579 {
|
nuclear@1
|
580
|
nuclear@1
|
581 HIDManager->DevManager->pThread->RemoveTicksNotifier(this);
|
nuclear@1
|
582 HIDManager->RemoveNotificationDevice(this);
|
nuclear@1
|
583
|
nuclear@1
|
584 if (DeviceHandle >= 0) // Device may already have been closed if unplugged.
|
nuclear@1
|
585 {
|
nuclear@1
|
586 closeDevice(false);
|
nuclear@1
|
587 }
|
nuclear@1
|
588
|
nuclear@1
|
589 LogText("OVR::Linux::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr());
|
nuclear@1
|
590 }
|
nuclear@1
|
591
|
nuclear@1
|
592 //-----------------------------------------------------------------------------
|
nuclear@1
|
593 void HIDDevice::closeDevice(bool wasUnplugged)
|
nuclear@1
|
594 {
|
nuclear@1
|
595 OVR_ASSERT(DeviceHandle >= 0);
|
nuclear@1
|
596
|
nuclear@1
|
597
|
nuclear@1
|
598 HIDManager->DevManager->pThread->RemoveSelectFd(this, DeviceHandle);
|
nuclear@1
|
599
|
nuclear@1
|
600 close(DeviceHandle); // close the file handle
|
nuclear@1
|
601 DeviceHandle = -1;
|
nuclear@1
|
602
|
nuclear@1
|
603 LogText("OVR::Linux::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr());
|
nuclear@1
|
604 }
|
nuclear@1
|
605
|
nuclear@1
|
606 //-----------------------------------------------------------------------------
|
nuclear@1
|
607 void HIDDevice::closeDeviceOnIOError()
|
nuclear@1
|
608 {
|
nuclear@1
|
609 LogText("OVR::Linux::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr());
|
nuclear@1
|
610 closeDevice(false);
|
nuclear@1
|
611 }
|
nuclear@1
|
612
|
nuclear@1
|
613 //-----------------------------------------------------------------------------
|
nuclear@1
|
614 bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
|
nuclear@1
|
615 {
|
nuclear@1
|
616
|
nuclear@1
|
617 if (DeviceHandle < 0)
|
nuclear@1
|
618 return false;
|
nuclear@1
|
619
|
nuclear@1
|
620 UByte reportID = data[0];
|
nuclear@1
|
621
|
nuclear@1
|
622 if (reportID == 0)
|
nuclear@1
|
623 {
|
nuclear@1
|
624 // Not using reports so remove from data packet.
|
nuclear@1
|
625 data++;
|
nuclear@1
|
626 length--;
|
nuclear@1
|
627 }
|
nuclear@1
|
628
|
nuclear@1
|
629 int r = ioctl(DeviceHandle, HIDIOCSFEATURE(length), data);
|
nuclear@1
|
630 return (r >= 0);
|
nuclear@1
|
631 }
|
nuclear@1
|
632
|
nuclear@1
|
633 //-----------------------------------------------------------------------------
|
nuclear@1
|
634 bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
|
nuclear@1
|
635 {
|
nuclear@1
|
636 if (DeviceHandle < 0)
|
nuclear@1
|
637 return false;
|
nuclear@1
|
638
|
nuclear@1
|
639 int r = ioctl(DeviceHandle, HIDIOCGFEATURE(length), data);
|
nuclear@1
|
640 return (r >= 0);
|
nuclear@1
|
641 }
|
nuclear@1
|
642
|
nuclear@1
|
643 //-----------------------------------------------------------------------------
|
nuclear@1
|
644 UInt64 HIDDevice::OnTicks(UInt64 ticksMks)
|
nuclear@1
|
645 {
|
nuclear@1
|
646 if (Handler)
|
nuclear@1
|
647 {
|
nuclear@1
|
648 return Handler->OnTicks(ticksMks);
|
nuclear@1
|
649 }
|
nuclear@1
|
650
|
nuclear@1
|
651 return DeviceManagerThread::Notifier::OnTicks(ticksMks);
|
nuclear@1
|
652 }
|
nuclear@1
|
653
|
nuclear@1
|
654 //-----------------------------------------------------------------------------
|
nuclear@1
|
655 void HIDDevice::OnEvent(int i, int fd)
|
nuclear@1
|
656 {
|
nuclear@1
|
657 // We have data to read from the device
|
nuclear@1
|
658 int bytes = read(fd, ReadBuffer, ReadBufferSize);
|
nuclear@1
|
659 if (bytes >= 0)
|
nuclear@1
|
660 {
|
nuclear@1
|
661 // TODO: I need to handle partial messages and package reconstruction
|
nuclear@1
|
662 if (Handler)
|
nuclear@1
|
663 {
|
nuclear@1
|
664 Handler->OnInputReport(ReadBuffer, bytes);
|
nuclear@1
|
665 }
|
nuclear@1
|
666 }
|
nuclear@1
|
667 else
|
nuclear@1
|
668 { // Close the device on read error.
|
nuclear@1
|
669 closeDeviceOnIOError();
|
nuclear@1
|
670 }
|
nuclear@1
|
671 }
|
nuclear@1
|
672
|
nuclear@1
|
673 //-----------------------------------------------------------------------------
|
nuclear@1
|
674 bool HIDDevice::OnDeviceNotification(MessageType messageType,
|
nuclear@1
|
675 HIDDeviceDesc* device_info,
|
nuclear@1
|
676 bool* error)
|
nuclear@1
|
677 {
|
nuclear@1
|
678 const char* device_path = device_info->Path.ToCStr();
|
nuclear@1
|
679
|
nuclear@1
|
680 if (messageType == Message_DeviceAdded && DeviceHandle < 0)
|
nuclear@1
|
681 {
|
nuclear@1
|
682 // Is this the correct device?
|
nuclear@1
|
683 if (!(device_info->VendorId == DevDesc.VendorId
|
nuclear@1
|
684 && device_info->ProductId == DevDesc.ProductId
|
nuclear@1
|
685 && device_info->SerialNumber == DevDesc.SerialNumber))
|
nuclear@1
|
686 {
|
nuclear@1
|
687 return false;
|
nuclear@1
|
688 }
|
nuclear@1
|
689
|
nuclear@1
|
690 // A closed device has been re-added. Try to reopen.
|
nuclear@1
|
691 if (!openDevice(device_path))
|
nuclear@1
|
692 {
|
nuclear@1
|
693 LogError("OVR::Linux::HIDDevice - Failed to reopen a device '%s' that was re-added.\n",
|
nuclear@1
|
694 device_path);
|
nuclear@1
|
695 *error = true;
|
nuclear@1
|
696 return true;
|
nuclear@1
|
697 }
|
nuclear@1
|
698
|
nuclear@1
|
699 LogText("OVR::Linux::HIDDevice - Reopened device '%s'\n", device_path);
|
nuclear@1
|
700
|
nuclear@1
|
701 if (Handler)
|
nuclear@1
|
702 {
|
nuclear@1
|
703 Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceAdded);
|
nuclear@1
|
704 }
|
nuclear@1
|
705 }
|
nuclear@1
|
706 else if (messageType == Message_DeviceRemoved)
|
nuclear@1
|
707 {
|
nuclear@1
|
708 // Is this the correct device?
|
nuclear@1
|
709 // For disconnected device, the device description will be invalid so
|
nuclear@1
|
710 // checking the path is the only way to match them
|
nuclear@1
|
711 if (DevDesc.Path.CompareNoCase(device_path) != 0)
|
nuclear@1
|
712 {
|
nuclear@1
|
713 return false;
|
nuclear@1
|
714 }
|
nuclear@1
|
715
|
nuclear@1
|
716 if (DeviceHandle >= 0)
|
nuclear@1
|
717 {
|
nuclear@1
|
718 closeDevice(true);
|
nuclear@1
|
719 }
|
nuclear@1
|
720
|
nuclear@1
|
721 if (Handler)
|
nuclear@1
|
722 {
|
nuclear@1
|
723 Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceRemoved);
|
nuclear@1
|
724 }
|
nuclear@1
|
725 }
|
nuclear@1
|
726 else
|
nuclear@1
|
727 {
|
nuclear@1
|
728 OVR_ASSERT(0);
|
nuclear@1
|
729 }
|
nuclear@1
|
730
|
nuclear@1
|
731 *error = false;
|
nuclear@1
|
732 return true;
|
nuclear@1
|
733 }
|
nuclear@1
|
734
|
nuclear@1
|
735 //-----------------------------------------------------------------------------
|
nuclear@1
|
736 HIDDeviceManager* HIDDeviceManager::CreateInternal(Linux::DeviceManager* devManager)
|
nuclear@1
|
737 {
|
nuclear@1
|
738
|
nuclear@1
|
739 if (!System::IsInitialized())
|
nuclear@1
|
740 {
|
nuclear@1
|
741 // Use custom message, since Log is not yet installed.
|
nuclear@1
|
742 OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
|
nuclear@1
|
743 LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
|
nuclear@1
|
744 return 0;
|
nuclear@1
|
745 }
|
nuclear@1
|
746
|
nuclear@1
|
747 Ptr<Linux::HIDDeviceManager> manager = *new Linux::HIDDeviceManager(devManager);
|
nuclear@1
|
748
|
nuclear@1
|
749 if (manager)
|
nuclear@1
|
750 {
|
nuclear@1
|
751 if (manager->Initialize())
|
nuclear@1
|
752 {
|
nuclear@1
|
753 manager->AddRef();
|
nuclear@1
|
754 }
|
nuclear@1
|
755 else
|
nuclear@1
|
756 {
|
nuclear@1
|
757 manager.Clear();
|
nuclear@1
|
758 }
|
nuclear@1
|
759 }
|
nuclear@1
|
760
|
nuclear@1
|
761 return manager.GetPtr();
|
nuclear@1
|
762 }
|
nuclear@1
|
763
|
nuclear@1
|
764 } // namespace Linux
|
nuclear@1
|
765
|
nuclear@1
|
766 //-------------------------------------------------------------------------------------
|
nuclear@1
|
767 // ***** Creation
|
nuclear@1
|
768
|
nuclear@1
|
769 // Creates a new HIDDeviceManager and initializes OVR.
|
nuclear@1
|
770 HIDDeviceManager* HIDDeviceManager::Create()
|
nuclear@1
|
771 {
|
nuclear@1
|
772 OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet."));
|
nuclear@1
|
773
|
nuclear@1
|
774 if (!System::IsInitialized())
|
nuclear@1
|
775 {
|
nuclear@1
|
776 // Use custom message, since Log is not yet installed.
|
nuclear@1
|
777 OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
|
nuclear@1
|
778 LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
|
nuclear@1
|
779 return 0;
|
nuclear@1
|
780 }
|
nuclear@1
|
781
|
nuclear@1
|
782 Ptr<Linux::HIDDeviceManager> manager = *new Linux::HIDDeviceManager(NULL);
|
nuclear@1
|
783
|
nuclear@1
|
784 if (manager)
|
nuclear@1
|
785 {
|
nuclear@1
|
786 if (manager->Initialize())
|
nuclear@1
|
787 {
|
nuclear@1
|
788 manager->AddRef();
|
nuclear@1
|
789 }
|
nuclear@1
|
790 else
|
nuclear@1
|
791 {
|
nuclear@1
|
792 manager.Clear();
|
nuclear@1
|
793 }
|
nuclear@1
|
794 }
|
nuclear@1
|
795
|
nuclear@1
|
796 return manager.GetPtr();
|
nuclear@1
|
797 }
|
nuclear@1
|
798
|
nuclear@1
|
799 } // namespace OVR
|