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