nuclear@1: /************************************************************************************ nuclear@1: Filename : OVR_OSX_HIDDevice.cpp nuclear@1: Content : OSX 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_OSX_HIDDevice.h" nuclear@1: nuclear@1: #include nuclear@1: nuclear@1: namespace OVR { namespace OSX { nuclear@1: nuclear@1: static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5; nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // **** OSX::DeviceManager nuclear@1: nuclear@1: HIDDeviceManager::HIDDeviceManager(DeviceManager* manager) nuclear@1: : DevManager(manager) nuclear@1: { nuclear@1: HIDManager = NULL; nuclear@1: } nuclear@1: nuclear@1: HIDDeviceManager::~HIDDeviceManager() nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: CFRunLoopRef HIDDeviceManager::getRunLoop() nuclear@1: { nuclear@1: if (DevManager != NULL) nuclear@1: { nuclear@1: return DevManager->pThread->GetRunLoop(); nuclear@1: } nuclear@1: nuclear@1: return CFRunLoopGetCurrent(); nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::initializeManager() nuclear@1: { nuclear@1: if (HIDManager != NULL) nuclear@1: { nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone); nuclear@1: nuclear@1: if (!HIDManager) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Create a Matching Dictionary nuclear@1: CFMutableDictionaryRef matchDict = nuclear@1: CFDictionaryCreateMutable(kCFAllocatorDefault, nuclear@1: 2, nuclear@1: &kCFTypeDictionaryKeyCallBacks, nuclear@1: &kCFTypeDictionaryValueCallBacks); nuclear@1: nuclear@1: // Specify a device manufacturer in the Matching Dictionary nuclear@1: UInt32 vendorId = Oculus_VendorId; nuclear@1: CFNumberRef vendorIdRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendorId); nuclear@1: CFDictionarySetValue(matchDict, nuclear@1: CFSTR(kIOHIDVendorIDKey), nuclear@1: vendorIdRef); nuclear@1: // Register the Matching Dictionary to the HID Manager nuclear@1: IOHIDManagerSetDeviceMatching(HIDManager, matchDict); nuclear@1: CFRelease(vendorIdRef); nuclear@1: CFRelease(matchDict); nuclear@1: nuclear@1: // Register a callback for USB device detection with the HID Manager nuclear@1: IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &staticDeviceMatchingCallback, this); nuclear@1: nuclear@1: IOHIDManagerScheduleWithRunLoop(HIDManager, getRunLoop(), kCFRunLoopDefaultMode); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::Initialize() nuclear@1: { nuclear@1: return initializeManager(); nuclear@1: } nuclear@1: nuclear@1: void HIDDeviceManager::Shutdown() nuclear@1: { nuclear@1: OVR_ASSERT_LOG(HIDManager, ("Should have called 'Initialize' before 'Shutdown'.")); nuclear@1: CFRelease(HIDManager); nuclear@1: nuclear@1: LogText("OVR::OSX::HIDDeviceManager - shutting down.\n"); nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getIntProperty(IOHIDDeviceRef device, CFStringRef propertyName, SInt32* pResult) nuclear@1: { nuclear@1: nuclear@1: CFTypeRef ref = IOHIDDeviceGetProperty(device, propertyName); nuclear@1: nuclear@1: if (!ref) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: if (CFGetTypeID(ref) != CFNumberGetTypeID()) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, pResult); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) nuclear@1: { nuclear@1: nuclear@1: if (!getVendorId(device, &(pDevDesc->VendorId))) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: if (!getProductId(device, &(pDevDesc->ProductId))) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) nuclear@1: { nuclear@1: nuclear@1: SInt32 result; nuclear@1: nuclear@1: if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), &result)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: pDevDesc->UsagePage = result; nuclear@1: nuclear@1: nuclear@1: if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), &result)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: pDevDesc->Usage = result; nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) nuclear@1: { nuclear@1: return getSerialNumberString(device, &(pDevDesc->SerialNumber)); nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc) nuclear@1: { nuclear@1: nuclear@1: // Regardless of whether they fail we'll try and get the remaining. nuclear@1: getStringProperty(device, CFSTR(kIOHIDManufacturerKey), &(pDevDesc->Manufacturer)); nuclear@1: getStringProperty(device, CFSTR(kIOHIDProductKey), &(pDevDesc->Product)); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getStringProperty(IOHIDDeviceRef device, nuclear@1: CFStringRef propertyName, nuclear@1: String* pResult) nuclear@1: { nuclear@1: nuclear@1: CFStringRef str = (CFStringRef) IOHIDDeviceGetProperty(device, propertyName); nuclear@1: nuclear@1: if (!str) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: CFIndex length = CFStringGetLength(str); nuclear@1: CFRange range = CFRangeMake(0, length); nuclear@1: nuclear@1: // Test the conversion first to get required buffer size. nuclear@1: CFIndex bufferLength; nuclear@1: CFIndex numberOfChars = CFStringGetBytes(str, nuclear@1: range, nuclear@1: kCFStringEncodingUTF8, nuclear@1: (char) '?', nuclear@1: FALSE, nuclear@1: NULL, nuclear@1: 0, nuclear@1: &bufferLength); nuclear@1: nuclear@1: if (numberOfChars == 0) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Now allocate buffer. nuclear@1: char* buffer = new char[bufferLength+1]; nuclear@1: nuclear@1: numberOfChars = CFStringGetBytes(str, nuclear@1: range, nuclear@1: kCFStringEncodingUTF8, nuclear@1: (char) '?', nuclear@1: FALSE, nuclear@1: (UInt8*) buffer, nuclear@1: bufferLength, nuclear@1: NULL); nuclear@1: OVR_ASSERT_LOG(numberOfChars != 0, ("CFStringGetBytes failed.")); nuclear@1: nuclear@1: buffer[bufferLength] = '\0'; nuclear@1: *pResult = String(buffer); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getVendorId(IOHIDDeviceRef device, UInt16* pResult) nuclear@1: { nuclear@1: SInt32 result; nuclear@1: nuclear@1: if (!getIntProperty(device, CFSTR(kIOHIDVendorIDKey), &result)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: *pResult = result; nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getProductId(IOHIDDeviceRef device, UInt16* pResult) nuclear@1: { nuclear@1: SInt32 result; nuclear@1: nuclear@1: if (!getIntProperty(device, CFSTR(kIOHIDProductIDKey), &result)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: *pResult = result; nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getLocationId(IOHIDDeviceRef device, SInt32* pResult) nuclear@1: { nuclear@1: SInt32 result; nuclear@1: nuclear@1: if (!getIntProperty(device, CFSTR(kIOHIDLocationIDKey), &result)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: *pResult = result; nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getSerialNumberString(IOHIDDeviceRef device, String* pResult) nuclear@1: { nuclear@1: nuclear@1: if (!getStringProperty(device, CFSTR(kIOHIDSerialNumberKey), pResult)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getPath(IOHIDDeviceRef device, String* pPath) nuclear@1: { nuclear@1: nuclear@1: String transport; nuclear@1: if (!getStringProperty(device, CFSTR(kIOHIDTransportKey), &transport)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: UInt16 vendorId; nuclear@1: if (!getVendorId(device, &vendorId)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: UInt16 productId; nuclear@1: if (!getProductId(device, &productId)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: String serialNumber; nuclear@1: if (!getSerialNumberString(device, &serialNumber)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: StringBuffer buffer; nuclear@1: buffer.AppendFormat("%s:vid=%04hx:pid=%04hx:ser=%s", nuclear@1: transport.ToCStr(), nuclear@1: vendorId, nuclear@1: productId, nuclear@1: serialNumber.ToCStr()); nuclear@1: nuclear@1: *pPath = String(buffer); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor) nuclear@1: { nuclear@1: if (!initializeManager()) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager); nuclear@1: if (!deviceSet) nuclear@1: return false; nuclear@1: nuclear@1: CFIndex deviceCount = CFSetGetCount(deviceSet); nuclear@1: nuclear@1: // Allocate a block of memory and read the set into it. nuclear@1: IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); nuclear@1: CFSetGetValues(deviceSet, (const void **) devices); nuclear@1: nuclear@1: nuclear@1: // Iterate over devices. nuclear@1: for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) nuclear@1: { nuclear@1: IOHIDDeviceRef hidDev = devices[deviceIndex]; nuclear@1: nuclear@1: if (!hidDev) nuclear@1: { nuclear@1: continue; nuclear@1: } nuclear@1: nuclear@1: HIDDeviceDesc devDesc; nuclear@1: nuclear@1: if (getPath(hidDev, &(devDesc.Path)) && nuclear@1: initVendorProductVersion(hidDev, &devDesc) && nuclear@1: enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) && nuclear@1: initUsage(hidDev, &devDesc)) nuclear@1: { nuclear@1: initStrings(hidDev, &devDesc); nuclear@1: initSerialNumber(hidDev, &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 CreateHIDFile 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: continue; nuclear@1: } nuclear@1: nuclear@1: // Construct minimal device that the visitor callback can get feature reports from. nuclear@1: OSX::HIDDevice device(this, hidDev); nuclear@1: nuclear@1: enumVisitor->Visit(device, devDesc); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: OVR_FREE(devices); nuclear@1: CFRelease(deviceSet); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: OVR::HIDDevice* HIDDeviceManager::Open(const String& path) nuclear@1: { nuclear@1: nuclear@1: Ptr device = *new OSX::HIDDevice(this); nuclear@1: nuclear@1: if (!device->HIDInitialize(path)) nuclear@1: { nuclear@1: return NULL; nuclear@1: } nuclear@1: nuclear@1: device->AddRef(); nuclear@1: nuclear@1: return device; nuclear@1: } nuclear@1: nuclear@1: bool HIDDeviceManager::getFullDesc(IOHIDDeviceRef 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 (!initUsage(device, desc)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: if (!initSerialNumber(device, desc)) nuclear@1: { nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: initStrings(device, desc); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: // New USB device specified in the matching dictionary has been added (callback function) nuclear@1: void HIDDeviceManager::staticDeviceMatchingCallback(void *inContext, nuclear@1: IOReturn inResult, nuclear@1: void *inSender, nuclear@1: IOHIDDeviceRef inIOHIDDeviceRef) nuclear@1: { nuclear@1: HIDDeviceManager* hidMgr = static_cast(inContext); nuclear@1: HIDDeviceDesc hidDevDesc; nuclear@1: hidMgr->getPath(inIOHIDDeviceRef, &hidDevDesc.Path); nuclear@1: hidMgr->getFullDesc(inIOHIDDeviceRef, &hidDevDesc); nuclear@1: nuclear@1: hidMgr->DevManager->DetectHIDDevice(hidDevDesc); nuclear@1: } nuclear@1: nuclear@1: //------------------------------------------------------------------------------------- nuclear@1: // **** OSX::HIDDevice nuclear@1: nuclear@1: HIDDevice::HIDDevice(HIDDeviceManager* manager) nuclear@1: : HIDManager(manager), InMinimalMode(false) nuclear@1: { nuclear@1: Device = NULL; nuclear@1: RepluggedNotificationPort = 0; 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, IOHIDDeviceRef device) nuclear@1: : HIDManager(manager), Device(device), InMinimalMode(true) nuclear@1: { nuclear@1: RepluggedNotificationPort = 0; 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: bool HIDDevice::HIDInitialize(const String& path) nuclear@1: { nuclear@1: nuclear@1: DevDesc.Path = path; nuclear@1: nuclear@1: if (!openDevice()) nuclear@1: { nuclear@1: LogText("OVR::OSX::HIDDevice - Failed to open HIDDevice: %s", path.ToCStr()); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Setup notification for when a device is unplugged and plugged back in. nuclear@1: if (!setupDevicePluggedInNotification()) nuclear@1: { nuclear@1: LogText("OVR::OSX::HIDDevice - Failed to setup notification for when device plugged back in."); nuclear@1: closeDevice(false); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: HIDManager->DevManager->pThread->AddTicksNotifier(this); nuclear@1: nuclear@1: nuclear@1: LogText("OVR::OSX::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: bool HIDDevice::initInfo() nuclear@1: { nuclear@1: // Device must have been successfully opened. nuclear@1: OVR_ASSERT(Device); 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: void HIDDevice::staticDeviceAddedCallback(void* pContext, io_iterator_t iterator) nuclear@1: { nuclear@1: HIDDevice* pDevice = (HIDDevice*) pContext; nuclear@1: pDevice->deviceAddedCallback(iterator); nuclear@1: } nuclear@1: nuclear@1: void HIDDevice::deviceAddedCallback(io_iterator_t iterator) nuclear@1: { nuclear@1: nuclear@1: if (Device == NULL) nuclear@1: { nuclear@1: if (openDevice()) nuclear@1: { nuclear@1: LogText("OVR::OSX::HIDDevice - Reopened device : %s", DevDesc.Path.ToCStr()); nuclear@1: nuclear@1: Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc); nuclear@1: if (existingHIDDev && existingHIDDev->pDevice) nuclear@1: { nuclear@1: HIDManager->DevManager->CallOnDeviceAdded(existingHIDDev); nuclear@1: } nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: // Reset callback. nuclear@1: while (IOIteratorNext(iterator)) nuclear@1: ; nuclear@1: } nuclear@1: nuclear@1: bool HIDDevice::openDevice() nuclear@1: { nuclear@1: nuclear@1: // Have to iterate through devices again to generate paths. nuclear@1: CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager->HIDManager); nuclear@1: CFIndex deviceCount = CFSetGetCount(deviceSet); nuclear@1: nuclear@1: // Allocate a block of memory and read the set into it. nuclear@1: IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount); nuclear@1: CFSetGetValues(deviceSet, (const void **) devices); nuclear@1: nuclear@1: nuclear@1: // Iterate over devices. nuclear@1: IOHIDDeviceRef device = NULL; nuclear@1: nuclear@1: for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++) nuclear@1: { nuclear@1: IOHIDDeviceRef tmpDevice = devices[deviceIndex]; nuclear@1: nuclear@1: if (!tmpDevice) nuclear@1: { nuclear@1: continue; nuclear@1: } nuclear@1: nuclear@1: String path; nuclear@1: if (!HIDManager->getPath(tmpDevice, &path)) nuclear@1: { nuclear@1: continue; nuclear@1: } nuclear@1: nuclear@1: if (path == DevDesc.Path) nuclear@1: { nuclear@1: device = tmpDevice; nuclear@1: break; nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: nuclear@1: OVR_FREE(devices); nuclear@1: nuclear@1: if (!device) nuclear@1: { nuclear@1: CFRelease(deviceSet); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Attempt to open device. nuclear@1: if (IOHIDDeviceOpen(device, kIOHIDOptionsTypeSeizeDevice) nuclear@1: != kIOReturnSuccess) nuclear@1: { nuclear@1: CFRelease(deviceSet); nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Retain the device before we release the set. nuclear@1: CFRetain(device); nuclear@1: CFRelease(deviceSet); nuclear@1: nuclear@1: nuclear@1: Device = device; nuclear@1: nuclear@1: nuclear@1: if (!initInfo()) nuclear@1: { nuclear@1: IOHIDDeviceClose(Device, kIOHIDOptionsTypeSeizeDevice); nuclear@1: CFRelease(Device); nuclear@1: Device = NULL; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: nuclear@1: // Setup the Run Loop and callbacks. nuclear@1: IOHIDDeviceScheduleWithRunLoop(Device, nuclear@1: HIDManager->getRunLoop(), nuclear@1: kCFRunLoopDefaultMode); nuclear@1: nuclear@1: IOHIDDeviceRegisterInputReportCallback(Device, nuclear@1: ReadBuffer, nuclear@1: ReadBufferSize, nuclear@1: staticHIDReportCallback, nuclear@1: this); nuclear@1: nuclear@1: IOHIDDeviceRegisterRemovalCallback(Device, nuclear@1: staticDeviceRemovedCallback, nuclear@1: this); nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: void HIDDevice::HIDShutdown() nuclear@1: { nuclear@1: nuclear@1: HIDManager->DevManager->pThread->RemoveTicksNotifier(this); nuclear@1: nuclear@1: if (Device != NULL) // Device may already have been closed if unplugged. nuclear@1: { nuclear@1: closeDevice(false); nuclear@1: } nuclear@1: nuclear@1: IOObjectRelease(RepluggedNotification); nuclear@1: if (RepluggedNotificationPort) nuclear@1: IONotificationPortDestroy(RepluggedNotificationPort); nuclear@1: nuclear@1: LogText("OVR::OSX::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr()); nuclear@1: } nuclear@1: nuclear@1: bool HIDDevice::setupDevicePluggedInNotification() nuclear@1: { nuclear@1: nuclear@1: // Setup notification when devices are plugged in. nuclear@1: RepluggedNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); nuclear@1: nuclear@1: CFRunLoopSourceRef notificationRunLoopSource = nuclear@1: IONotificationPortGetRunLoopSource(RepluggedNotificationPort); nuclear@1: nuclear@1: CFRunLoopAddSource(HIDManager->getRunLoop(), nuclear@1: notificationRunLoopSource, nuclear@1: kCFRunLoopDefaultMode); nuclear@1: nuclear@1: CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName); nuclear@1: nuclear@1: // Have to specify vendorId and productId. Doesn't seem to accept additional nuclear@1: // things like serial number. nuclear@1: SInt32 vendorId = DevDesc.VendorId; nuclear@1: CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault, nuclear@1: kCFNumberSInt32Type, nuclear@1: &vendorId); nuclear@1: CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef); nuclear@1: CFRelease(numberRef); nuclear@1: nuclear@1: SInt32 deviceProductId = DevDesc.ProductId; nuclear@1: numberRef = CFNumberCreate(kCFAllocatorDefault, nuclear@1: kCFNumberSInt32Type, nuclear@1: &deviceProductId); nuclear@1: CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef); nuclear@1: CFRelease(numberRef); nuclear@1: nuclear@1: kern_return_t result = nuclear@1: IOServiceAddMatchingNotification(RepluggedNotificationPort, nuclear@1: kIOMatchedNotification, nuclear@1: matchingDict, nuclear@1: staticDeviceAddedCallback, nuclear@1: this, nuclear@1: &RepluggedNotification); nuclear@1: nuclear@1: if (result != KERN_SUCCESS) nuclear@1: { nuclear@1: CFRelease(RepluggedNotificationPort); nuclear@1: RepluggedNotificationPort = 0; nuclear@1: return false; nuclear@1: } nuclear@1: nuclear@1: // Iterate through to arm. nuclear@1: while (IOIteratorNext(RepluggedNotification)) nuclear@1: { nuclear@1: } nuclear@1: nuclear@1: return true; nuclear@1: } nuclear@1: nuclear@1: void HIDDevice::closeDevice(bool wasUnplugged) nuclear@1: { nuclear@1: OVR_ASSERT(Device != NULL); nuclear@1: nuclear@1: if (!wasUnplugged) nuclear@1: { nuclear@1: // Clear the registered callbacks. nuclear@1: IOHIDDeviceRegisterInputReportCallback(Device, nuclear@1: ReadBuffer, nuclear@1: InputReportBufferLength, nuclear@1: NULL, nuclear@1: this); nuclear@1: nuclear@1: IOHIDDeviceRegisterRemovalCallback(Device, NULL, this); nuclear@1: nuclear@1: IOHIDDeviceUnscheduleFromRunLoop(Device, nuclear@1: HIDManager->getRunLoop(), nuclear@1: kCFRunLoopDefaultMode); nuclear@1: IOHIDDeviceClose(Device, kIOHIDOptionsTypeNone); nuclear@1: } nuclear@1: nuclear@1: CFRelease(Device); nuclear@1: Device = NULL; nuclear@1: nuclear@1: LogText("OVR::OSX::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr()); nuclear@1: } nuclear@1: nuclear@1: void HIDDevice::staticHIDReportCallback(void* pContext, nuclear@1: IOReturn result, nuclear@1: void* pSender, nuclear@1: IOHIDReportType reportType, nuclear@1: uint32_t reportId, nuclear@1: uint8_t* pReport, nuclear@1: CFIndex reportLength) nuclear@1: { nuclear@1: HIDDevice* pDevice = (HIDDevice*) pContext; nuclear@1: return pDevice->hidReportCallback(pReport, (UInt32)reportLength); nuclear@1: } nuclear@1: nuclear@1: void HIDDevice::hidReportCallback(UByte* pData, UInt32 length) nuclear@1: { nuclear@1: nuclear@1: // We got data. nuclear@1: if (Handler) nuclear@1: { nuclear@1: Handler->OnInputReport(pData, length); nuclear@1: } nuclear@1: } nuclear@1: nuclear@1: void HIDDevice::staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender) nuclear@1: { nuclear@1: HIDDevice* pDevice = (HIDDevice*) pContext; nuclear@1: pDevice->deviceRemovedCallback(); nuclear@1: } nuclear@1: nuclear@1: void HIDDevice::deviceRemovedCallback() nuclear@1: { nuclear@1: Ptr _this(this); // prevent from release nuclear@1: nuclear@1: Ptr existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc); nuclear@1: if (existingHIDDev && existingHIDDev->pDevice) nuclear@1: { nuclear@1: HIDManager->DevManager->CallOnDeviceRemoved(existingHIDDev); nuclear@1: } nuclear@1: closeDevice(true); nuclear@1: } nuclear@1: nuclear@1: CFStringRef HIDDevice::generateRunLoopModeString(IOHIDDeviceRef device) nuclear@1: { nuclear@1: const UInt32 safeBuffSize = 256; nuclear@1: char nameBuff[safeBuffSize]; nuclear@1: OVR_sprintf(nameBuff, safeBuffSize, "%016lX", device); nuclear@1: nuclear@1: return CFStringCreateWithCString(NULL, nameBuff, kCFStringEncodingASCII); nuclear@1: } nuclear@1: nuclear@1: bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length) nuclear@1: { nuclear@1: nuclear@1: if (!Device) 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: IOReturn result = IOHIDDeviceSetReport( Device, nuclear@1: kIOHIDReportTypeFeature, nuclear@1: reportID, nuclear@1: data, nuclear@1: length); nuclear@1: nuclear@1: return (result == kIOReturnSuccess); nuclear@1: } nuclear@1: nuclear@1: bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length) nuclear@1: { nuclear@1: if (!Device) nuclear@1: return false; nuclear@1: nuclear@1: CFIndex bufferLength = length; nuclear@1: nuclear@1: // Report id is in first byte of the buffer. nuclear@1: IOReturn result = IOHIDDeviceGetReport(Device, kIOHIDReportTypeFeature, data[0], data, &bufferLength); nuclear@1: nuclear@1: return (result == kIOReturnSuccess); nuclear@1: } nuclear@1: nuclear@1: UInt64 HIDDevice::OnTicks(UInt64 ticksMks) nuclear@1: { 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: HIDDeviceManager* HIDDeviceManager::CreateInternal(OSX::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 OSX::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 OSX 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 OSX::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