nuclear@1: /************************************************************************************ nuclear@1: Filename : OVR_Linux_HIDDevice.cpp nuclear@1: Content : Linux HID device implementation. nuclear@1: Created : February 26, 2013 nuclear@1: Authors : Lee Cooper nuclear@1: nuclear@1: Copyright : Copyright 2013 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_Linux_HIDDevice.h" nuclear@1: nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include nuclear@1: #include "OVR_HIDDeviceImpl.h" nuclear@1: nuclear@1: namespace OVR { namespace Linux { nuclear@1: nuclear@1: static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5; nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // **** Linux::DeviceManager nuclear@1: //----------------------------------------------------------------------------- nuclear@1: HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) : DevManager(manager) nuclear@1: { nuclear@1: UdevInstance = NULL; nuclear@1: HIDMonitor = NULL; nuclear@1: HIDMonHandle = -1; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: HIDDeviceManager::~HIDDeviceManager() nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::initializeManager() nuclear@1: { nuclear@1: if (HIDMonitor) nuclear@1: { nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: // Create a udev_monitor handle to watch for device changes (hot-plug detection) nuclear@1: HIDMonitor = udev_monitor_new_from_netlink(UdevInstance, "udev"); nuclear@1: if (HIDMonitor == NULL) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: udev_monitor_filter_add_match_subsystem_devtype(HIDMonitor, "hidraw", NULL); // filter for hidraw only nuclear@1: nuclear@1: int err = udev_monitor_enable_receiving(HIDMonitor); nuclear@1: if (err) nuclear@1: { nuclear@1: udev_monitor_unref(HIDMonitor); nuclear@1: HIDMonitor = NULL; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Get the file descriptor (fd) for the monitor. nuclear@1: HIDMonHandle = udev_monitor_get_fd(HIDMonitor); nuclear@1: if (HIDMonHandle < 0) nuclear@1: { nuclear@1: udev_monitor_unref(HIDMonitor); nuclear@1: HIDMonitor = NULL; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // This file handle will be polled along-side with the device hid handles for changes nuclear@1: // Add the handle to the polling list nuclear@1: if (!DevManager->pThread->AddSelectFd(this, HIDMonHandle)) nuclear@1: { nuclear@1: close(HIDMonHandle); nuclear@1: HIDMonHandle = -1; nuclear@1: nuclear@1: udev_monitor_unref(HIDMonitor); nuclear@1: HIDMonitor = NULL; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::Initialize() nuclear@1: { nuclear@1: // Get a udev library handle. This handle must stay active during the nuclear@1: // duration the lifetime of device monitoring handles nuclear@1: UdevInstance = udev_new(); nuclear@1: if (!UdevInstance) nuclear@1: return false; nuclear@1: nuclear@1: return initializeManager(); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: void HIDDeviceManager::Shutdown() nuclear@1: { nuclear@1: OVR_ASSERT_LOG((UdevInstance), ("Should have called 'Initialize' before 'Shutdown'.")); nuclear@1: nuclear@1: if (HIDMonitor) nuclear@1: { nuclear@1: DevManager->pThread->RemoveSelectFd(this, HIDMonHandle); nuclear@1: close(HIDMonHandle); nuclear@1: HIDMonHandle = -1; nuclear@1: nuclear@1: udev_monitor_unref(HIDMonitor); nuclear@1: HIDMonitor = NULL; nuclear@1: } nuclear@1: nuclear@1: udev_unref(UdevInstance); // release the library nuclear@1: nuclear@1: LogText("OVR::Linux::HIDDeviceManager - shutting down.\n"); nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::AddNotificationDevice(HIDDevice* device) nuclear@1: { nuclear@1: NotificationDevices.PushBack(device); nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::RemoveNotificationDevice(HIDDevice* device) nuclear@1: { nuclear@1: for (UPInt i = 0; i < NotificationDevices.GetSize(); i++) nuclear@1: { nuclear@1: if (NotificationDevices[i] == device) nuclear@1: { nuclear@1: NotificationDevices.RemoveAt(i); nuclear@1: return true; nuclear@1: } nuclear@1: } nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::getIntProperty(udev_device* device, nuclear@1: const char* propertyName, nuclear@1: SInt32* pResult) nuclear@1: { nuclear@1: const char* str = udev_device_get_sysattr_value(device, propertyName); nuclear@1: if (str) nuclear@1: { nuclear@1: *pResult = strtol(str, NULL, 16); nuclear@1: return true; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: *pResult = 0; nuclear@1: return true; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::initVendorProductVersion(udev_device* device, HIDDeviceDesc* pDevDesc) nuclear@1: { nuclear@1: SInt32 result; nuclear@1: if (getIntProperty(device, "idVendor", &result)) nuclear@1: pDevDesc->VendorId = result; nuclear@1: else nuclear@1: return false; nuclear@1: nuclear@1: if (getIntProperty(device, "idProduct", &result)) nuclear@1: pDevDesc->ProductId = result; nuclear@1: else nuclear@1: return false; nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::getStringProperty(udev_device* device, nuclear@1: const char* propertyName, nuclear@1: OVR::String* pResult) nuclear@1: { nuclear@1: // Get the attribute in UTF8 nuclear@1: const char* str = udev_device_get_sysattr_value(device, propertyName); nuclear@1: if (str) nuclear@1: { // Copy the string into the return value nuclear@1: *pResult = String(str); nuclear@1: return true; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) nuclear@1: { nuclear@1: nuclear@1: if (!initializeManager()) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Get a list of hid devices nuclear@1: udev_enumerate* devices = udev_enumerate_new(UdevInstance); nuclear@1: udev_enumerate_add_match_subsystem(devices, "hidraw"); nuclear@1: udev_enumerate_scan_devices(devices); nuclear@1: nuclear@1: udev_list_entry* entry = udev_enumerate_get_list_entry(devices); nuclear@1: nuclear@1: // Search each device for the matching vid/pid nuclear@1: while (entry != NULL) nuclear@1: { nuclear@1: // Get the device file name nuclear@1: const char* sysfs_path = udev_list_entry_get_name(entry); nuclear@1: udev_device* hid; // The device's HID udev node. nuclear@1: hid = udev_device_new_from_syspath(UdevInstance, sysfs_path); nuclear@1: const char* dev_path = udev_device_get_devnode(hid); nuclear@1: nuclear@1: // Get the USB device nuclear@1: hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); nuclear@1: if (hid) nuclear@1: { nuclear@1: HIDDeviceDesc devDesc; nuclear@1: nuclear@1: // Check the VID/PID for a match nuclear@1: if (dev_path && nuclear@1: initVendorProductVersion(hid, &devDesc) && nuclear@1: enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId)) nuclear@1: { nuclear@1: devDesc.Path = dev_path; nuclear@1: getFullDesc(hid, &devDesc); nuclear@1: nuclear@1: // Look for the device to check if it is already opened. nuclear@1: Ptr existingDevice = DevManager->FindHIDDevice(devDesc); nuclear@1: // if device exists and it is opened then most likely the device open() nuclear@1: // will fail; therefore, we just set Enumerated to 'true' and continue. nuclear@1: if (existingDevice && existingDevice->pDevice) nuclear@1: { nuclear@1: existingDevice->Enumerated = true; nuclear@1: } nuclear@1: else nuclear@1: { // open the device temporarily for startup communication nuclear@1: int device_handle = open(dev_path, O_RDWR); nuclear@1: if (device_handle >= 0) nuclear@1: { nuclear@1: // Construct minimal device that the visitor callback can get feature reports from nuclear@1: Linux::HIDDevice device(this, device_handle); nuclear@1: enumVisitor->Visit(device, devDesc); nuclear@1: nuclear@1: close(device_handle); // close the file handle nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: udev_device_unref(hid); nuclear@1: entry = udev_list_entry_get_next(entry); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Free the enumerator and udev objects nuclear@1: udev_enumerate_unref(devices); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: OVR::HIDDevice* HIDDeviceManager::Open(const String& path) nuclear@1: { nuclear@1: Ptr device = *new Linux::HIDDevice(this); nuclear@1: nuclear@1: if (device->HIDInitialize(path)) nuclear@1: { nuclear@1: device->AddRef(); nuclear@1: return device; nuclear@1: } nuclear@1: nuclear@1: return NULL; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::getFullDesc(udev_device* device, HIDDeviceDesc* desc) nuclear@1: { nuclear@1: nuclear@1: if (!initVendorProductVersion(device, desc)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: if (!getStringProperty(device, "serial", &(desc->SerialNumber))) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: getStringProperty(device, "manufacturer", &(desc->Manufacturer)); nuclear@1: getStringProperty(device, "product", &(desc->Product)); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDeviceManager::GetDescriptorFromPath(const char* dev_path, HIDDeviceDesc* desc) nuclear@1: { nuclear@1: if (!initializeManager()) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Search for the udev device from the given pathname so we can nuclear@1: // have a handle to query device properties nuclear@1: nuclear@1: udev_enumerate* devices = udev_enumerate_new(UdevInstance); nuclear@1: udev_enumerate_add_match_subsystem(devices, "hidraw"); nuclear@1: udev_enumerate_scan_devices(devices); nuclear@1: nuclear@1: udev_list_entry* entry = udev_enumerate_get_list_entry(devices); nuclear@1: nuclear@1: bool success = false; nuclear@1: // Search for the device with the matching path nuclear@1: while (entry != NULL) nuclear@1: { nuclear@1: // Get the device file name nuclear@1: const char* sysfs_path = udev_list_entry_get_name(entry); nuclear@1: udev_device* hid; // The device's HID udev node. nuclear@1: hid = udev_device_new_from_syspath(UdevInstance, sysfs_path); nuclear@1: const char* path = udev_device_get_devnode(hid); nuclear@1: nuclear@1: if (OVR_strcmp(dev_path, path) == 0) nuclear@1: { // Found the device so lets collect the device descriptor nuclear@1: nuclear@1: // Get the USB device nuclear@1: hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); nuclear@1: if (hid) nuclear@1: { nuclear@1: desc->Path = dev_path; nuclear@1: success = getFullDesc(hid, desc); nuclear@1: } nuclear@1: nuclear@1: } nuclear@1: nuclear@1: udev_device_unref(hid); nuclear@1: entry = udev_list_entry_get_next(entry); nuclear@1: } nuclear@1: nuclear@1: // Free the enumerator nuclear@1: udev_enumerate_unref(devices); nuclear@1: nuclear@1: return success; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: void HIDDeviceManager::OnEvent(int i, int fd) nuclear@1: { nuclear@1: // There is a device status change nuclear@1: udev_device* hid = udev_monitor_receive_device(HIDMonitor); nuclear@1: if (hid) nuclear@1: { nuclear@1: const char* dev_path = udev_device_get_devnode(hid); nuclear@1: const char* action = udev_device_get_action(hid); nuclear@1: nuclear@1: HIDDeviceDesc device_info; nuclear@1: device_info.Path = dev_path; nuclear@1: nuclear@1: MessageType notify_type; nuclear@1: if (OVR_strcmp(action, "add") == 0) nuclear@1: { nuclear@1: notify_type = Message_DeviceAdded; nuclear@1: nuclear@1: // Retrieve the device info. This can only be done on a connected nuclear@1: // device and is invalid for a disconnected device nuclear@1: nuclear@1: // Get the USB device nuclear@1: hid = udev_device_get_parent_with_subsystem_devtype(hid, "usb", "usb_device"); nuclear@1: if (!hid) nuclear@1: { nuclear@1: return; nuclear@1: } nuclear@1: nuclear@1: getFullDesc(hid, &device_info); nuclear@1: } nuclear@1: else if (OVR_strcmp(action, "remove") == 0) nuclear@1: { nuclear@1: notify_type = Message_DeviceRemoved; nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: return; nuclear@1: } nuclear@1: nuclear@1: bool error = false; nuclear@1: bool deviceFound = false; nuclear@1: for (UPInt i = 0; i < NotificationDevices.GetSize(); i++) nuclear@1: { nuclear@1: if (NotificationDevices[i] && nuclear@1: NotificationDevices[i]->OnDeviceNotification(notify_type, &device_info, &error)) nuclear@1: { nuclear@1: // The notification was for an existing device nuclear@1: deviceFound = true; nuclear@1: break; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: if (notify_type == Message_DeviceAdded && !deviceFound) nuclear@1: { nuclear@1: DevManager->DetectHIDDevice(device_info); nuclear@1: } nuclear@1: nuclear@1: udev_device_unref(hid); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: //============================================================================= nuclear@1: // Linux::HIDDevice nuclear@1: //============================================================================= nuclear@1: HIDDevice::HIDDevice(HIDDeviceManager* manager) nuclear@1: : HIDManager(manager), InMinimalMode(false) nuclear@1: { nuclear@1: DeviceHandle = -1; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: // This is a minimal constructor used during enumeration for us to pass nuclear@1: // a HIDDevice to the visit function (so that it can query feature reports). nuclear@1: HIDDevice::HIDDevice(HIDDeviceManager* manager, int device_handle) nuclear@1: : HIDManager(manager), DeviceHandle(device_handle), InMinimalMode(true) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: HIDDevice::~HIDDevice() nuclear@1: { nuclear@1: if (!InMinimalMode) nuclear@1: { nuclear@1: HIDShutdown(); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDevice::HIDInitialize(const String& path) nuclear@1: { nuclear@1: const char* hid_path = path.ToCStr(); nuclear@1: if (!openDevice(hid_path)) nuclear@1: { nuclear@1: LogText("OVR::Linux::HIDDevice - Failed to open HIDDevice: %s", hid_path); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: HIDManager->DevManager->pThread->AddTicksNotifier(this); nuclear@1: HIDManager->AddNotificationDevice(this); nuclear@1: nuclear@1: LogText("OVR::Linux::HIDDevice - Opened '%s'\n" nuclear@1: " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n", nuclear@1: DevDesc.Path.ToCStr(), nuclear@1: DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(), nuclear@1: DevDesc.SerialNumber.ToCStr()); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDevice::initInfo() nuclear@1: { nuclear@1: // Device must have been successfully opened. nuclear@1: OVR_ASSERT(DeviceHandle >= 0); nuclear@1: nuclear@1: int desc_size = 0; nuclear@1: hidraw_report_descriptor rpt_desc; nuclear@1: memset(&rpt_desc, 0, sizeof(rpt_desc)); nuclear@1: nuclear@1: // get report descriptor size nuclear@1: int r = ioctl(DeviceHandle, HIDIOCGRDESCSIZE, &desc_size); nuclear@1: if (r < 0) nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Failed to get report descriptor size.")); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Get the report descriptor nuclear@1: rpt_desc.size = desc_size; nuclear@1: r = ioctl(DeviceHandle, HIDIOCGRDESC, &rpt_desc); nuclear@1: if (r < 0) nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Failed to get report descriptor.")); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: /* nuclear@1: // Get report lengths. nuclear@1: SInt32 bufferLength; nuclear@1: bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength); nuclear@1: OVR_ASSERT(getResult); nuclear@1: InputReportBufferLength = (UInt16) bufferLength; nuclear@1: nuclear@1: getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength); nuclear@1: OVR_ASSERT(getResult); nuclear@1: OutputReportBufferLength = (UInt16) bufferLength; nuclear@1: nuclear@1: getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength); nuclear@1: OVR_ASSERT(getResult); nuclear@1: FeatureReportBufferLength = (UInt16) bufferLength; nuclear@1: nuclear@1: nuclear@1: if (ReadBufferSize < InputReportBufferLength) nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Get device desc. nuclear@1: if (!HIDManager->getFullDesc(Device, &DevDesc)) nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device.")); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: */ nuclear@1: nuclear@1: // Get report lengths. nuclear@1: // TODO: hard-coded for now. Need to interpret these values from the report descriptor nuclear@1: InputReportBufferLength = 62; nuclear@1: OutputReportBufferLength = 0; nuclear@1: FeatureReportBufferLength = 69; nuclear@1: nuclear@1: if (ReadBufferSize < InputReportBufferLength) nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer.")); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDevice::openDevice(const char* device_path) nuclear@1: { nuclear@1: // First fill out the device descriptor nuclear@1: if (!HIDManager->GetDescriptorFromPath(device_path, &DevDesc)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Now open the device nuclear@1: DeviceHandle = open(device_path, O_RDWR); nuclear@1: if (DeviceHandle < 0) nuclear@1: { nuclear@1: OVR_DEBUG_LOG(("Failed 'CreateHIDFile' while opening device, error = 0x%X.", errno)); nuclear@1: DeviceHandle = -1; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // fill out some values from the feature report descriptor nuclear@1: if (!initInfo()) nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Failed to get HIDDevice info.")); nuclear@1: nuclear@1: close(DeviceHandle); nuclear@1: DeviceHandle = -1; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Add the device to the polling list nuclear@1: if (!HIDManager->DevManager->pThread->AddSelectFd(this, DeviceHandle)) nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Failed to initialize polling for HIDDevice.")); nuclear@1: nuclear@1: close(DeviceHandle); nuclear@1: DeviceHandle = -1; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: void HIDDevice::HIDShutdown() nuclear@1: { nuclear@1: nuclear@1: HIDManager->DevManager->pThread->RemoveTicksNotifier(this); nuclear@1: HIDManager->RemoveNotificationDevice(this); nuclear@1: nuclear@1: if (DeviceHandle >= 0) // Device may already have been closed if unplugged. nuclear@1: { nuclear@1: closeDevice(false); nuclear@1: } nuclear@1: nuclear@1: LogText("OVR::Linux::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr()); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: void HIDDevice::closeDevice(bool wasUnplugged) nuclear@1: { nuclear@1: OVR_ASSERT(DeviceHandle >= 0); nuclear@1: nuclear@1: nuclear@1: HIDManager->DevManager->pThread->RemoveSelectFd(this, DeviceHandle); nuclear@1: nuclear@1: close(DeviceHandle); // close the file handle nuclear@1: DeviceHandle = -1; nuclear@1: nuclear@1: LogText("OVR::Linux::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr()); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: void HIDDevice::closeDeviceOnIOError() nuclear@1: { nuclear@1: LogText("OVR::Linux::HIDDevice - Lost connection to '%s'\n", DevDesc.Path.ToCStr()); nuclear@1: closeDevice(false); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) nuclear@1: { nuclear@1: nuclear@1: if (DeviceHandle < 0) nuclear@1: return false; nuclear@1: nuclear@1: UByte reportID = data[0]; nuclear@1: nuclear@1: if (reportID == 0) nuclear@1: { nuclear@1: // Not using reports so remove from data packet. nuclear@1: data++; nuclear@1: length--; nuclear@1: } nuclear@1: nuclear@1: int r = ioctl(DeviceHandle, HIDIOCSFEATURE(length), data); nuclear@1: return (r >= 0); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) nuclear@1: { nuclear@1: if (DeviceHandle < 0) nuclear@1: return false; nuclear@1: nuclear@1: int r = ioctl(DeviceHandle, HIDIOCGFEATURE(length), data); nuclear@1: return (r >= 0); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: UInt64 HIDDevice::OnTicks(UInt64 ticksMks) nuclear@1: { nuclear@1: if (Handler) nuclear@1: { nuclear@1: return Handler->OnTicks(ticksMks); nuclear@1: } nuclear@1: nuclear@1: return DeviceManagerThread::Notifier::OnTicks(ticksMks); nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: void HIDDevice::OnEvent(int i, int fd) nuclear@1: { nuclear@1: // We have data to read from the device nuclear@1: int bytes = read(fd, ReadBuffer, ReadBufferSize); nuclear@1: if (bytes >= 0) nuclear@1: { nuclear@1: // TODO: I need to handle partial messages and package reconstruction nuclear@1: if (Handler) nuclear@1: { nuclear@1: Handler->OnInputReport(ReadBuffer, bytes); nuclear@1: } nuclear@1: } nuclear@1: else nuclear@1: { // Close the device on read error. nuclear@1: closeDeviceOnIOError(); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: bool HIDDevice::OnDeviceNotification(MessageType messageType, nuclear@1: HIDDeviceDesc* device_info, nuclear@1: bool* error) nuclear@1: { nuclear@1: const char* device_path = device_info->Path.ToCStr(); nuclear@1: nuclear@1: if (messageType == Message_DeviceAdded && DeviceHandle < 0) nuclear@1: { nuclear@1: // Is this the correct device? nuclear@1: if (!(device_info->VendorId == DevDesc.VendorId nuclear@1: && device_info->ProductId == DevDesc.ProductId nuclear@1: && device_info->SerialNumber == DevDesc.SerialNumber)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // A closed device has been re-added. Try to reopen. nuclear@1: if (!openDevice(device_path)) nuclear@1: { nuclear@1: LogError("OVR::Linux::HIDDevice - Failed to reopen a device '%s' that was re-added.\n", nuclear@1: device_path); nuclear@1: *error = true; nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: LogText("OVR::Linux::HIDDevice - Reopened device '%s'\n", device_path); nuclear@1: nuclear@1: if (Handler) nuclear@1: { nuclear@1: Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceAdded); nuclear@1: } nuclear@1: } nuclear@1: else if (messageType == Message_DeviceRemoved) nuclear@1: { nuclear@1: // Is this the correct device? nuclear@1: // For disconnected device, the device description will be invalid so nuclear@1: // checking the path is the only way to match them nuclear@1: if (DevDesc.Path.CompareNoCase(device_path) != 0) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: if (DeviceHandle >= 0) nuclear@1: { nuclear@1: closeDevice(true); nuclear@1: } nuclear@1: nuclear@1: if (Handler) nuclear@1: { nuclear@1: Handler->OnDeviceMessage(HIDHandler::HIDDeviceMessage_DeviceRemoved); nuclear@1: } nuclear@1: } nuclear@1: else nuclear@1: { nuclear@1: OVR_ASSERT(0); nuclear@1: } nuclear@1: nuclear@1: *error = false; nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: //----------------------------------------------------------------------------- nuclear@1: HIDDeviceManager* HIDDeviceManager::CreateInternal(Linux::DeviceManager* devManager) 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, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: Ptr manager = *new Linux::HIDDeviceManager(devManager); nuclear@1: nuclear@1: if (manager) nuclear@1: { nuclear@1: if (manager->Initialize()) 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: return manager.GetPtr(); nuclear@1: } nuclear@1: nuclear@1: } // namespace Linux nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // ***** Creation nuclear@1: nuclear@1: // Creates a new HIDDeviceManager and initializes OVR. nuclear@1: HIDDeviceManager* HIDDeviceManager::Create() nuclear@1: { nuclear@1: OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet.")); 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, "HIDDeviceManager::Create failed - OVR::System not initialized"); ); nuclear@1: return 0; nuclear@1: } nuclear@1: nuclear@1: Ptr manager = *new Linux::HIDDeviceManager(NULL); nuclear@1: nuclear@1: if (manager) nuclear@1: { nuclear@1: if (manager->Initialize()) 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: return manager.GetPtr(); nuclear@1: } nuclear@1: nuclear@1: } // namespace OVR