oculus1

annotate libovr/Src/osx/OVR_OSX_HIDDevice.cpp @ 23:0c76f70fb7e9

merged
author John Tsiombikas <nuclear@member.fsf.org>
date Sat, 28 Sep 2013 04:13:33 +0300
parents
children
rev   line source
nuclear@1 1 /************************************************************************************
nuclear@1 2 Filename : OVR_OSX_HIDDevice.cpp
nuclear@1 3 Content : OSX 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_OSX_HIDDevice.h"
nuclear@1 16
nuclear@1 17 #include <IOKit/usb/IOUSBLib.h>
nuclear@1 18
nuclear@1 19 namespace OVR { namespace OSX {
nuclear@1 20
nuclear@1 21 static const UInt32 MAX_QUEUED_INPUT_REPORTS = 5;
nuclear@1 22
nuclear@1 23 //-------------------------------------------------------------------------------------
nuclear@1 24 // **** OSX::DeviceManager
nuclear@1 25
nuclear@1 26 HIDDeviceManager::HIDDeviceManager(DeviceManager* manager)
nuclear@1 27 : DevManager(manager)
nuclear@1 28 {
nuclear@1 29 HIDManager = NULL;
nuclear@1 30 }
nuclear@1 31
nuclear@1 32 HIDDeviceManager::~HIDDeviceManager()
nuclear@1 33 {
nuclear@1 34 }
nuclear@1 35
nuclear@1 36 CFRunLoopRef HIDDeviceManager::getRunLoop()
nuclear@1 37 {
nuclear@1 38 if (DevManager != NULL)
nuclear@1 39 {
nuclear@1 40 return DevManager->pThread->GetRunLoop();
nuclear@1 41 }
nuclear@1 42
nuclear@1 43 return CFRunLoopGetCurrent();
nuclear@1 44 }
nuclear@1 45
nuclear@1 46 bool HIDDeviceManager::initializeManager()
nuclear@1 47 {
nuclear@1 48 if (HIDManager != NULL)
nuclear@1 49 {
nuclear@1 50 return true;
nuclear@1 51 }
nuclear@1 52
nuclear@1 53 HIDManager = IOHIDManagerCreate(kCFAllocatorDefault, kIOHIDOptionsTypeNone);
nuclear@1 54
nuclear@1 55 if (!HIDManager)
nuclear@1 56 {
nuclear@1 57 return false;
nuclear@1 58 }
nuclear@1 59
nuclear@1 60 // Create a Matching Dictionary
nuclear@1 61 CFMutableDictionaryRef matchDict =
nuclear@1 62 CFDictionaryCreateMutable(kCFAllocatorDefault,
nuclear@1 63 2,
nuclear@1 64 &kCFTypeDictionaryKeyCallBacks,
nuclear@1 65 &kCFTypeDictionaryValueCallBacks);
nuclear@1 66
nuclear@1 67 // Specify a device manufacturer in the Matching Dictionary
nuclear@1 68 UInt32 vendorId = Oculus_VendorId;
nuclear@1 69 CFNumberRef vendorIdRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &vendorId);
nuclear@1 70 CFDictionarySetValue(matchDict,
nuclear@1 71 CFSTR(kIOHIDVendorIDKey),
nuclear@1 72 vendorIdRef);
nuclear@1 73 // Register the Matching Dictionary to the HID Manager
nuclear@1 74 IOHIDManagerSetDeviceMatching(HIDManager, matchDict);
nuclear@1 75 CFRelease(vendorIdRef);
nuclear@1 76 CFRelease(matchDict);
nuclear@1 77
nuclear@1 78 // Register a callback for USB device detection with the HID Manager
nuclear@1 79 IOHIDManagerRegisterDeviceMatchingCallback(HIDManager, &staticDeviceMatchingCallback, this);
nuclear@1 80
nuclear@1 81 IOHIDManagerScheduleWithRunLoop(HIDManager, getRunLoop(), kCFRunLoopDefaultMode);
nuclear@1 82
nuclear@1 83 return true;
nuclear@1 84 }
nuclear@1 85
nuclear@1 86 bool HIDDeviceManager::Initialize()
nuclear@1 87 {
nuclear@1 88 return initializeManager();
nuclear@1 89 }
nuclear@1 90
nuclear@1 91 void HIDDeviceManager::Shutdown()
nuclear@1 92 {
nuclear@1 93 OVR_ASSERT_LOG(HIDManager, ("Should have called 'Initialize' before 'Shutdown'."));
nuclear@1 94 CFRelease(HIDManager);
nuclear@1 95
nuclear@1 96 LogText("OVR::OSX::HIDDeviceManager - shutting down.\n");
nuclear@1 97 }
nuclear@1 98
nuclear@1 99 bool HIDDeviceManager::getIntProperty(IOHIDDeviceRef device, CFStringRef propertyName, SInt32* pResult)
nuclear@1 100 {
nuclear@1 101
nuclear@1 102 CFTypeRef ref = IOHIDDeviceGetProperty(device, propertyName);
nuclear@1 103
nuclear@1 104 if (!ref)
nuclear@1 105 {
nuclear@1 106 return false;
nuclear@1 107 }
nuclear@1 108
nuclear@1 109 if (CFGetTypeID(ref) != CFNumberGetTypeID())
nuclear@1 110 {
nuclear@1 111 return false;
nuclear@1 112 }
nuclear@1 113
nuclear@1 114 CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, pResult);
nuclear@1 115
nuclear@1 116 return true;
nuclear@1 117 }
nuclear@1 118
nuclear@1 119 bool HIDDeviceManager::initVendorProductVersion(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
nuclear@1 120 {
nuclear@1 121
nuclear@1 122 if (!getVendorId(device, &(pDevDesc->VendorId)))
nuclear@1 123 {
nuclear@1 124 return false;
nuclear@1 125 }
nuclear@1 126
nuclear@1 127 if (!getProductId(device, &(pDevDesc->ProductId)))
nuclear@1 128 {
nuclear@1 129 return false;
nuclear@1 130 }
nuclear@1 131
nuclear@1 132 return true;
nuclear@1 133 }
nuclear@1 134
nuclear@1 135 bool HIDDeviceManager::initUsage(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
nuclear@1 136 {
nuclear@1 137
nuclear@1 138 SInt32 result;
nuclear@1 139
nuclear@1 140 if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsagePageKey), &result))
nuclear@1 141 {
nuclear@1 142 return false;
nuclear@1 143 }
nuclear@1 144
nuclear@1 145 pDevDesc->UsagePage = result;
nuclear@1 146
nuclear@1 147
nuclear@1 148 if (!getIntProperty(device, CFSTR(kIOHIDPrimaryUsageKey), &result))
nuclear@1 149 {
nuclear@1 150 return false;
nuclear@1 151 }
nuclear@1 152
nuclear@1 153 pDevDesc->Usage = result;
nuclear@1 154
nuclear@1 155 return true;
nuclear@1 156 }
nuclear@1 157
nuclear@1 158 bool HIDDeviceManager::initSerialNumber(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
nuclear@1 159 {
nuclear@1 160 return getSerialNumberString(device, &(pDevDesc->SerialNumber));
nuclear@1 161 }
nuclear@1 162
nuclear@1 163 bool HIDDeviceManager::initStrings(IOHIDDeviceRef device, HIDDeviceDesc* pDevDesc)
nuclear@1 164 {
nuclear@1 165
nuclear@1 166 // Regardless of whether they fail we'll try and get the remaining.
nuclear@1 167 getStringProperty(device, CFSTR(kIOHIDManufacturerKey), &(pDevDesc->Manufacturer));
nuclear@1 168 getStringProperty(device, CFSTR(kIOHIDProductKey), &(pDevDesc->Product));
nuclear@1 169
nuclear@1 170 return true;
nuclear@1 171 }
nuclear@1 172
nuclear@1 173 bool HIDDeviceManager::getStringProperty(IOHIDDeviceRef device,
nuclear@1 174 CFStringRef propertyName,
nuclear@1 175 String* pResult)
nuclear@1 176 {
nuclear@1 177
nuclear@1 178 CFStringRef str = (CFStringRef) IOHIDDeviceGetProperty(device, propertyName);
nuclear@1 179
nuclear@1 180 if (!str)
nuclear@1 181 {
nuclear@1 182 return false;
nuclear@1 183 }
nuclear@1 184
nuclear@1 185 CFIndex length = CFStringGetLength(str);
nuclear@1 186 CFRange range = CFRangeMake(0, length);
nuclear@1 187
nuclear@1 188 // Test the conversion first to get required buffer size.
nuclear@1 189 CFIndex bufferLength;
nuclear@1 190 CFIndex numberOfChars = CFStringGetBytes(str,
nuclear@1 191 range,
nuclear@1 192 kCFStringEncodingUTF8,
nuclear@1 193 (char) '?',
nuclear@1 194 FALSE,
nuclear@1 195 NULL,
nuclear@1 196 0,
nuclear@1 197 &bufferLength);
nuclear@1 198
nuclear@1 199 if (numberOfChars == 0)
nuclear@1 200 {
nuclear@1 201 return false;
nuclear@1 202 }
nuclear@1 203
nuclear@1 204 // Now allocate buffer.
nuclear@1 205 char* buffer = new char[bufferLength+1];
nuclear@1 206
nuclear@1 207 numberOfChars = CFStringGetBytes(str,
nuclear@1 208 range,
nuclear@1 209 kCFStringEncodingUTF8,
nuclear@1 210 (char) '?',
nuclear@1 211 FALSE,
nuclear@1 212 (UInt8*) buffer,
nuclear@1 213 bufferLength,
nuclear@1 214 NULL);
nuclear@1 215 OVR_ASSERT_LOG(numberOfChars != 0, ("CFStringGetBytes failed."));
nuclear@1 216
nuclear@1 217 buffer[bufferLength] = '\0';
nuclear@1 218 *pResult = String(buffer);
nuclear@1 219
nuclear@1 220 return true;
nuclear@1 221 }
nuclear@1 222
nuclear@1 223 bool HIDDeviceManager::getVendorId(IOHIDDeviceRef device, UInt16* pResult)
nuclear@1 224 {
nuclear@1 225 SInt32 result;
nuclear@1 226
nuclear@1 227 if (!getIntProperty(device, CFSTR(kIOHIDVendorIDKey), &result))
nuclear@1 228 {
nuclear@1 229 return false;
nuclear@1 230 }
nuclear@1 231
nuclear@1 232 *pResult = result;
nuclear@1 233
nuclear@1 234 return true;
nuclear@1 235 }
nuclear@1 236
nuclear@1 237 bool HIDDeviceManager::getProductId(IOHIDDeviceRef device, UInt16* pResult)
nuclear@1 238 {
nuclear@1 239 SInt32 result;
nuclear@1 240
nuclear@1 241 if (!getIntProperty(device, CFSTR(kIOHIDProductIDKey), &result))
nuclear@1 242 {
nuclear@1 243 return false;
nuclear@1 244 }
nuclear@1 245
nuclear@1 246 *pResult = result;
nuclear@1 247
nuclear@1 248 return true;
nuclear@1 249 }
nuclear@1 250
nuclear@1 251 bool HIDDeviceManager::getLocationId(IOHIDDeviceRef device, SInt32* pResult)
nuclear@1 252 {
nuclear@1 253 SInt32 result;
nuclear@1 254
nuclear@1 255 if (!getIntProperty(device, CFSTR(kIOHIDLocationIDKey), &result))
nuclear@1 256 {
nuclear@1 257 return false;
nuclear@1 258 }
nuclear@1 259
nuclear@1 260 *pResult = result;
nuclear@1 261
nuclear@1 262 return true;
nuclear@1 263 }
nuclear@1 264
nuclear@1 265 bool HIDDeviceManager::getSerialNumberString(IOHIDDeviceRef device, String* pResult)
nuclear@1 266 {
nuclear@1 267
nuclear@1 268 if (!getStringProperty(device, CFSTR(kIOHIDSerialNumberKey), pResult))
nuclear@1 269 {
nuclear@1 270 return false;
nuclear@1 271 }
nuclear@1 272
nuclear@1 273 return true;
nuclear@1 274 }
nuclear@1 275
nuclear@1 276 bool HIDDeviceManager::getPath(IOHIDDeviceRef device, String* pPath)
nuclear@1 277 {
nuclear@1 278
nuclear@1 279 String transport;
nuclear@1 280 if (!getStringProperty(device, CFSTR(kIOHIDTransportKey), &transport))
nuclear@1 281 {
nuclear@1 282 return false;
nuclear@1 283 }
nuclear@1 284
nuclear@1 285 UInt16 vendorId;
nuclear@1 286 if (!getVendorId(device, &vendorId))
nuclear@1 287 {
nuclear@1 288 return false;
nuclear@1 289 }
nuclear@1 290
nuclear@1 291 UInt16 productId;
nuclear@1 292 if (!getProductId(device, &productId))
nuclear@1 293 {
nuclear@1 294 return false;
nuclear@1 295 }
nuclear@1 296
nuclear@1 297 String serialNumber;
nuclear@1 298 if (!getSerialNumberString(device, &serialNumber))
nuclear@1 299 {
nuclear@1 300 return false;
nuclear@1 301 }
nuclear@1 302
nuclear@1 303
nuclear@1 304 StringBuffer buffer;
nuclear@1 305 buffer.AppendFormat("%s:vid=%04hx:pid=%04hx:ser=%s",
nuclear@1 306 transport.ToCStr(),
nuclear@1 307 vendorId,
nuclear@1 308 productId,
nuclear@1 309 serialNumber.ToCStr());
nuclear@1 310
nuclear@1 311 *pPath = String(buffer);
nuclear@1 312
nuclear@1 313 return true;
nuclear@1 314 }
nuclear@1 315
nuclear@1 316 bool HIDDeviceManager::Enumerate(HIDEnumerateVisitor* enumVisitor)
nuclear@1 317 {
nuclear@1 318 if (!initializeManager())
nuclear@1 319 {
nuclear@1 320 return false;
nuclear@1 321 }
nuclear@1 322
nuclear@1 323
nuclear@1 324 CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager);
nuclear@1 325 if (!deviceSet)
nuclear@1 326 return false;
nuclear@1 327
nuclear@1 328 CFIndex deviceCount = CFSetGetCount(deviceSet);
nuclear@1 329
nuclear@1 330 // Allocate a block of memory and read the set into it.
nuclear@1 331 IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount);
nuclear@1 332 CFSetGetValues(deviceSet, (const void **) devices);
nuclear@1 333
nuclear@1 334
nuclear@1 335 // Iterate over devices.
nuclear@1 336 for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++)
nuclear@1 337 {
nuclear@1 338 IOHIDDeviceRef hidDev = devices[deviceIndex];
nuclear@1 339
nuclear@1 340 if (!hidDev)
nuclear@1 341 {
nuclear@1 342 continue;
nuclear@1 343 }
nuclear@1 344
nuclear@1 345 HIDDeviceDesc devDesc;
nuclear@1 346
nuclear@1 347 if (getPath(hidDev, &(devDesc.Path)) &&
nuclear@1 348 initVendorProductVersion(hidDev, &devDesc) &&
nuclear@1 349 enumVisitor->MatchVendorProduct(devDesc.VendorId, devDesc.ProductId) &&
nuclear@1 350 initUsage(hidDev, &devDesc))
nuclear@1 351 {
nuclear@1 352 initStrings(hidDev, &devDesc);
nuclear@1 353 initSerialNumber(hidDev, &devDesc);
nuclear@1 354
nuclear@1 355 // Look for the device to check if it is already opened.
nuclear@1 356 Ptr<DeviceCreateDesc> existingDevice = DevManager->FindHIDDevice(devDesc);
nuclear@1 357 // if device exists and it is opened then most likely the CreateHIDFile
nuclear@1 358 // will fail; therefore, we just set Enumerated to 'true' and continue.
nuclear@1 359 if (existingDevice && existingDevice->pDevice)
nuclear@1 360 {
nuclear@1 361 existingDevice->Enumerated = true;
nuclear@1 362 continue;
nuclear@1 363 }
nuclear@1 364
nuclear@1 365 // Construct minimal device that the visitor callback can get feature reports from.
nuclear@1 366 OSX::HIDDevice device(this, hidDev);
nuclear@1 367
nuclear@1 368 enumVisitor->Visit(device, devDesc);
nuclear@1 369 }
nuclear@1 370 }
nuclear@1 371
nuclear@1 372 OVR_FREE(devices);
nuclear@1 373 CFRelease(deviceSet);
nuclear@1 374
nuclear@1 375 return true;
nuclear@1 376 }
nuclear@1 377
nuclear@1 378 OVR::HIDDevice* HIDDeviceManager::Open(const String& path)
nuclear@1 379 {
nuclear@1 380
nuclear@1 381 Ptr<OSX::HIDDevice> device = *new OSX::HIDDevice(this);
nuclear@1 382
nuclear@1 383 if (!device->HIDInitialize(path))
nuclear@1 384 {
nuclear@1 385 return NULL;
nuclear@1 386 }
nuclear@1 387
nuclear@1 388 device->AddRef();
nuclear@1 389
nuclear@1 390 return device;
nuclear@1 391 }
nuclear@1 392
nuclear@1 393 bool HIDDeviceManager::getFullDesc(IOHIDDeviceRef device, HIDDeviceDesc* desc)
nuclear@1 394 {
nuclear@1 395
nuclear@1 396 if (!initVendorProductVersion(device, desc))
nuclear@1 397 {
nuclear@1 398 return false;
nuclear@1 399 }
nuclear@1 400
nuclear@1 401 if (!initUsage(device, desc))
nuclear@1 402 {
nuclear@1 403 return false;
nuclear@1 404 }
nuclear@1 405
nuclear@1 406 if (!initSerialNumber(device, desc))
nuclear@1 407 {
nuclear@1 408 return false;
nuclear@1 409 }
nuclear@1 410
nuclear@1 411 initStrings(device, desc);
nuclear@1 412
nuclear@1 413 return true;
nuclear@1 414 }
nuclear@1 415
nuclear@1 416 // New USB device specified in the matching dictionary has been added (callback function)
nuclear@1 417 void HIDDeviceManager::staticDeviceMatchingCallback(void *inContext,
nuclear@1 418 IOReturn inResult,
nuclear@1 419 void *inSender,
nuclear@1 420 IOHIDDeviceRef inIOHIDDeviceRef)
nuclear@1 421 {
nuclear@1 422 HIDDeviceManager* hidMgr = static_cast<HIDDeviceManager*>(inContext);
nuclear@1 423 HIDDeviceDesc hidDevDesc;
nuclear@1 424 hidMgr->getPath(inIOHIDDeviceRef, &hidDevDesc.Path);
nuclear@1 425 hidMgr->getFullDesc(inIOHIDDeviceRef, &hidDevDesc);
nuclear@1 426
nuclear@1 427 hidMgr->DevManager->DetectHIDDevice(hidDevDesc);
nuclear@1 428 }
nuclear@1 429
nuclear@1 430 //-------------------------------------------------------------------------------------
nuclear@1 431 // **** OSX::HIDDevice
nuclear@1 432
nuclear@1 433 HIDDevice::HIDDevice(HIDDeviceManager* manager)
nuclear@1 434 : HIDManager(manager), InMinimalMode(false)
nuclear@1 435 {
nuclear@1 436 Device = NULL;
nuclear@1 437 RepluggedNotificationPort = 0;
nuclear@1 438 }
nuclear@1 439
nuclear@1 440 // This is a minimal constructor used during enumeration for us to pass
nuclear@1 441 // a HIDDevice to the visit function (so that it can query feature reports).
nuclear@1 442 HIDDevice::HIDDevice(HIDDeviceManager* manager, IOHIDDeviceRef device)
nuclear@1 443 : HIDManager(manager), Device(device), InMinimalMode(true)
nuclear@1 444 {
nuclear@1 445 RepluggedNotificationPort = 0;
nuclear@1 446 }
nuclear@1 447
nuclear@1 448 HIDDevice::~HIDDevice()
nuclear@1 449 {
nuclear@1 450 if (!InMinimalMode)
nuclear@1 451 {
nuclear@1 452 HIDShutdown();
nuclear@1 453 }
nuclear@1 454 }
nuclear@1 455
nuclear@1 456 bool HIDDevice::HIDInitialize(const String& path)
nuclear@1 457 {
nuclear@1 458
nuclear@1 459 DevDesc.Path = path;
nuclear@1 460
nuclear@1 461 if (!openDevice())
nuclear@1 462 {
nuclear@1 463 LogText("OVR::OSX::HIDDevice - Failed to open HIDDevice: %s", path.ToCStr());
nuclear@1 464 return false;
nuclear@1 465 }
nuclear@1 466
nuclear@1 467 // Setup notification for when a device is unplugged and plugged back in.
nuclear@1 468 if (!setupDevicePluggedInNotification())
nuclear@1 469 {
nuclear@1 470 LogText("OVR::OSX::HIDDevice - Failed to setup notification for when device plugged back in.");
nuclear@1 471 closeDevice(false);
nuclear@1 472 return false;
nuclear@1 473 }
nuclear@1 474
nuclear@1 475 HIDManager->DevManager->pThread->AddTicksNotifier(this);
nuclear@1 476
nuclear@1 477
nuclear@1 478 LogText("OVR::OSX::HIDDevice - Opened '%s'\n"
nuclear@1 479 " Manufacturer:'%s' Product:'%s' Serial#:'%s'\n",
nuclear@1 480 DevDesc.Path.ToCStr(),
nuclear@1 481 DevDesc.Manufacturer.ToCStr(), DevDesc.Product.ToCStr(),
nuclear@1 482 DevDesc.SerialNumber.ToCStr());
nuclear@1 483
nuclear@1 484 return true;
nuclear@1 485 }
nuclear@1 486
nuclear@1 487 bool HIDDevice::initInfo()
nuclear@1 488 {
nuclear@1 489 // Device must have been successfully opened.
nuclear@1 490 OVR_ASSERT(Device);
nuclear@1 491
nuclear@1 492
nuclear@1 493 // Get report lengths.
nuclear@1 494 SInt32 bufferLength;
nuclear@1 495 bool getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxInputReportSizeKey), &bufferLength);
nuclear@1 496 OVR_ASSERT(getResult);
nuclear@1 497 InputReportBufferLength = (UInt16) bufferLength;
nuclear@1 498
nuclear@1 499 getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxOutputReportSizeKey), &bufferLength);
nuclear@1 500 OVR_ASSERT(getResult);
nuclear@1 501 OutputReportBufferLength = (UInt16) bufferLength;
nuclear@1 502
nuclear@1 503 getResult = HIDManager->getIntProperty(Device, CFSTR(kIOHIDMaxFeatureReportSizeKey), &bufferLength);
nuclear@1 504 OVR_ASSERT(getResult);
nuclear@1 505 FeatureReportBufferLength = (UInt16) bufferLength;
nuclear@1 506
nuclear@1 507
nuclear@1 508 if (ReadBufferSize < InputReportBufferLength)
nuclear@1 509 {
nuclear@1 510 OVR_ASSERT_LOG(false, ("Input report buffer length is bigger than read buffer."));
nuclear@1 511 return false;
nuclear@1 512 }
nuclear@1 513
nuclear@1 514 // Get device desc.
nuclear@1 515 if (!HIDManager->getFullDesc(Device, &DevDesc))
nuclear@1 516 {
nuclear@1 517 OVR_ASSERT_LOG(false, ("Failed to get device desc while initializing device."));
nuclear@1 518 return false;
nuclear@1 519 }
nuclear@1 520
nuclear@1 521 return true;
nuclear@1 522 }
nuclear@1 523
nuclear@1 524 void HIDDevice::staticDeviceAddedCallback(void* pContext, io_iterator_t iterator)
nuclear@1 525 {
nuclear@1 526 HIDDevice* pDevice = (HIDDevice*) pContext;
nuclear@1 527 pDevice->deviceAddedCallback(iterator);
nuclear@1 528 }
nuclear@1 529
nuclear@1 530 void HIDDevice::deviceAddedCallback(io_iterator_t iterator)
nuclear@1 531 {
nuclear@1 532
nuclear@1 533 if (Device == NULL)
nuclear@1 534 {
nuclear@1 535 if (openDevice())
nuclear@1 536 {
nuclear@1 537 LogText("OVR::OSX::HIDDevice - Reopened device : %s", DevDesc.Path.ToCStr());
nuclear@1 538
nuclear@1 539 Ptr<DeviceCreateDesc> existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc);
nuclear@1 540 if (existingHIDDev && existingHIDDev->pDevice)
nuclear@1 541 {
nuclear@1 542 HIDManager->DevManager->CallOnDeviceAdded(existingHIDDev);
nuclear@1 543 }
nuclear@1 544 }
nuclear@1 545 }
nuclear@1 546
nuclear@1 547 // Reset callback.
nuclear@1 548 while (IOIteratorNext(iterator))
nuclear@1 549 ;
nuclear@1 550 }
nuclear@1 551
nuclear@1 552 bool HIDDevice::openDevice()
nuclear@1 553 {
nuclear@1 554
nuclear@1 555 // Have to iterate through devices again to generate paths.
nuclear@1 556 CFSetRef deviceSet = IOHIDManagerCopyDevices(HIDManager->HIDManager);
nuclear@1 557 CFIndex deviceCount = CFSetGetCount(deviceSet);
nuclear@1 558
nuclear@1 559 // Allocate a block of memory and read the set into it.
nuclear@1 560 IOHIDDeviceRef* devices = (IOHIDDeviceRef*) OVR_ALLOC(sizeof(IOHIDDeviceRef) * deviceCount);
nuclear@1 561 CFSetGetValues(deviceSet, (const void **) devices);
nuclear@1 562
nuclear@1 563
nuclear@1 564 // Iterate over devices.
nuclear@1 565 IOHIDDeviceRef device = NULL;
nuclear@1 566
nuclear@1 567 for (CFIndex deviceIndex = 0; deviceIndex < deviceCount; deviceIndex++)
nuclear@1 568 {
nuclear@1 569 IOHIDDeviceRef tmpDevice = devices[deviceIndex];
nuclear@1 570
nuclear@1 571 if (!tmpDevice)
nuclear@1 572 {
nuclear@1 573 continue;
nuclear@1 574 }
nuclear@1 575
nuclear@1 576 String path;
nuclear@1 577 if (!HIDManager->getPath(tmpDevice, &path))
nuclear@1 578 {
nuclear@1 579 continue;
nuclear@1 580 }
nuclear@1 581
nuclear@1 582 if (path == DevDesc.Path)
nuclear@1 583 {
nuclear@1 584 device = tmpDevice;
nuclear@1 585 break;
nuclear@1 586 }
nuclear@1 587 }
nuclear@1 588
nuclear@1 589
nuclear@1 590 OVR_FREE(devices);
nuclear@1 591
nuclear@1 592 if (!device)
nuclear@1 593 {
nuclear@1 594 CFRelease(deviceSet);
nuclear@1 595 return false;
nuclear@1 596 }
nuclear@1 597
nuclear@1 598 // Attempt to open device.
nuclear@1 599 if (IOHIDDeviceOpen(device, kIOHIDOptionsTypeSeizeDevice)
nuclear@1 600 != kIOReturnSuccess)
nuclear@1 601 {
nuclear@1 602 CFRelease(deviceSet);
nuclear@1 603 return false;
nuclear@1 604 }
nuclear@1 605
nuclear@1 606 // Retain the device before we release the set.
nuclear@1 607 CFRetain(device);
nuclear@1 608 CFRelease(deviceSet);
nuclear@1 609
nuclear@1 610
nuclear@1 611 Device = device;
nuclear@1 612
nuclear@1 613
nuclear@1 614 if (!initInfo())
nuclear@1 615 {
nuclear@1 616 IOHIDDeviceClose(Device, kIOHIDOptionsTypeSeizeDevice);
nuclear@1 617 CFRelease(Device);
nuclear@1 618 Device = NULL;
nuclear@1 619 return false;
nuclear@1 620 }
nuclear@1 621
nuclear@1 622
nuclear@1 623 // Setup the Run Loop and callbacks.
nuclear@1 624 IOHIDDeviceScheduleWithRunLoop(Device,
nuclear@1 625 HIDManager->getRunLoop(),
nuclear@1 626 kCFRunLoopDefaultMode);
nuclear@1 627
nuclear@1 628 IOHIDDeviceRegisterInputReportCallback(Device,
nuclear@1 629 ReadBuffer,
nuclear@1 630 ReadBufferSize,
nuclear@1 631 staticHIDReportCallback,
nuclear@1 632 this);
nuclear@1 633
nuclear@1 634 IOHIDDeviceRegisterRemovalCallback(Device,
nuclear@1 635 staticDeviceRemovedCallback,
nuclear@1 636 this);
nuclear@1 637
nuclear@1 638 return true;
nuclear@1 639 }
nuclear@1 640
nuclear@1 641 void HIDDevice::HIDShutdown()
nuclear@1 642 {
nuclear@1 643
nuclear@1 644 HIDManager->DevManager->pThread->RemoveTicksNotifier(this);
nuclear@1 645
nuclear@1 646 if (Device != NULL) // Device may already have been closed if unplugged.
nuclear@1 647 {
nuclear@1 648 closeDevice(false);
nuclear@1 649 }
nuclear@1 650
nuclear@1 651 IOObjectRelease(RepluggedNotification);
nuclear@1 652 if (RepluggedNotificationPort)
nuclear@1 653 IONotificationPortDestroy(RepluggedNotificationPort);
nuclear@1 654
nuclear@1 655 LogText("OVR::OSX::HIDDevice - HIDShutdown '%s'\n", DevDesc.Path.ToCStr());
nuclear@1 656 }
nuclear@1 657
nuclear@1 658 bool HIDDevice::setupDevicePluggedInNotification()
nuclear@1 659 {
nuclear@1 660
nuclear@1 661 // Setup notification when devices are plugged in.
nuclear@1 662 RepluggedNotificationPort = IONotificationPortCreate(kIOMasterPortDefault);
nuclear@1 663
nuclear@1 664 CFRunLoopSourceRef notificationRunLoopSource =
nuclear@1 665 IONotificationPortGetRunLoopSource(RepluggedNotificationPort);
nuclear@1 666
nuclear@1 667 CFRunLoopAddSource(HIDManager->getRunLoop(),
nuclear@1 668 notificationRunLoopSource,
nuclear@1 669 kCFRunLoopDefaultMode);
nuclear@1 670
nuclear@1 671 CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOUSBDeviceClassName);
nuclear@1 672
nuclear@1 673 // Have to specify vendorId and productId. Doesn't seem to accept additional
nuclear@1 674 // things like serial number.
nuclear@1 675 SInt32 vendorId = DevDesc.VendorId;
nuclear@1 676 CFNumberRef numberRef = CFNumberCreate(kCFAllocatorDefault,
nuclear@1 677 kCFNumberSInt32Type,
nuclear@1 678 &vendorId);
nuclear@1 679 CFDictionarySetValue(matchingDict, CFSTR(kUSBVendorID), numberRef);
nuclear@1 680 CFRelease(numberRef);
nuclear@1 681
nuclear@1 682 SInt32 deviceProductId = DevDesc.ProductId;
nuclear@1 683 numberRef = CFNumberCreate(kCFAllocatorDefault,
nuclear@1 684 kCFNumberSInt32Type,
nuclear@1 685 &deviceProductId);
nuclear@1 686 CFDictionarySetValue(matchingDict, CFSTR(kUSBProductID), numberRef);
nuclear@1 687 CFRelease(numberRef);
nuclear@1 688
nuclear@1 689 kern_return_t result =
nuclear@1 690 IOServiceAddMatchingNotification(RepluggedNotificationPort,
nuclear@1 691 kIOMatchedNotification,
nuclear@1 692 matchingDict,
nuclear@1 693 staticDeviceAddedCallback,
nuclear@1 694 this,
nuclear@1 695 &RepluggedNotification);
nuclear@1 696
nuclear@1 697 if (result != KERN_SUCCESS)
nuclear@1 698 {
nuclear@1 699 CFRelease(RepluggedNotificationPort);
nuclear@1 700 RepluggedNotificationPort = 0;
nuclear@1 701 return false;
nuclear@1 702 }
nuclear@1 703
nuclear@1 704 // Iterate through to arm.
nuclear@1 705 while (IOIteratorNext(RepluggedNotification))
nuclear@1 706 {
nuclear@1 707 }
nuclear@1 708
nuclear@1 709 return true;
nuclear@1 710 }
nuclear@1 711
nuclear@1 712 void HIDDevice::closeDevice(bool wasUnplugged)
nuclear@1 713 {
nuclear@1 714 OVR_ASSERT(Device != NULL);
nuclear@1 715
nuclear@1 716 if (!wasUnplugged)
nuclear@1 717 {
nuclear@1 718 // Clear the registered callbacks.
nuclear@1 719 IOHIDDeviceRegisterInputReportCallback(Device,
nuclear@1 720 ReadBuffer,
nuclear@1 721 InputReportBufferLength,
nuclear@1 722 NULL,
nuclear@1 723 this);
nuclear@1 724
nuclear@1 725 IOHIDDeviceRegisterRemovalCallback(Device, NULL, this);
nuclear@1 726
nuclear@1 727 IOHIDDeviceUnscheduleFromRunLoop(Device,
nuclear@1 728 HIDManager->getRunLoop(),
nuclear@1 729 kCFRunLoopDefaultMode);
nuclear@1 730 IOHIDDeviceClose(Device, kIOHIDOptionsTypeNone);
nuclear@1 731 }
nuclear@1 732
nuclear@1 733 CFRelease(Device);
nuclear@1 734 Device = NULL;
nuclear@1 735
nuclear@1 736 LogText("OVR::OSX::HIDDevice - HID Device Closed '%s'\n", DevDesc.Path.ToCStr());
nuclear@1 737 }
nuclear@1 738
nuclear@1 739 void HIDDevice::staticHIDReportCallback(void* pContext,
nuclear@1 740 IOReturn result,
nuclear@1 741 void* pSender,
nuclear@1 742 IOHIDReportType reportType,
nuclear@1 743 uint32_t reportId,
nuclear@1 744 uint8_t* pReport,
nuclear@1 745 CFIndex reportLength)
nuclear@1 746 {
nuclear@1 747 HIDDevice* pDevice = (HIDDevice*) pContext;
nuclear@1 748 return pDevice->hidReportCallback(pReport, (UInt32)reportLength);
nuclear@1 749 }
nuclear@1 750
nuclear@1 751 void HIDDevice::hidReportCallback(UByte* pData, UInt32 length)
nuclear@1 752 {
nuclear@1 753
nuclear@1 754 // We got data.
nuclear@1 755 if (Handler)
nuclear@1 756 {
nuclear@1 757 Handler->OnInputReport(pData, length);
nuclear@1 758 }
nuclear@1 759 }
nuclear@1 760
nuclear@1 761 void HIDDevice::staticDeviceRemovedCallback(void* pContext, IOReturn result, void* pSender)
nuclear@1 762 {
nuclear@1 763 HIDDevice* pDevice = (HIDDevice*) pContext;
nuclear@1 764 pDevice->deviceRemovedCallback();
nuclear@1 765 }
nuclear@1 766
nuclear@1 767 void HIDDevice::deviceRemovedCallback()
nuclear@1 768 {
nuclear@1 769 Ptr<HIDDevice> _this(this); // prevent from release
nuclear@1 770
nuclear@1 771 Ptr<DeviceCreateDesc> existingHIDDev = HIDManager->DevManager->FindHIDDevice(DevDesc);
nuclear@1 772 if (existingHIDDev && existingHIDDev->pDevice)
nuclear@1 773 {
nuclear@1 774 HIDManager->DevManager->CallOnDeviceRemoved(existingHIDDev);
nuclear@1 775 }
nuclear@1 776 closeDevice(true);
nuclear@1 777 }
nuclear@1 778
nuclear@1 779 CFStringRef HIDDevice::generateRunLoopModeString(IOHIDDeviceRef device)
nuclear@1 780 {
nuclear@1 781 const UInt32 safeBuffSize = 256;
nuclear@1 782 char nameBuff[safeBuffSize];
nuclear@1 783 OVR_sprintf(nameBuff, safeBuffSize, "%016lX", device);
nuclear@1 784
nuclear@1 785 return CFStringCreateWithCString(NULL, nameBuff, kCFStringEncodingASCII);
nuclear@1 786 }
nuclear@1 787
nuclear@1 788 bool HIDDevice::SetFeatureReport(UByte* data, UInt32 length)
nuclear@1 789 {
nuclear@1 790
nuclear@1 791 if (!Device)
nuclear@1 792 return false;
nuclear@1 793
nuclear@1 794 UByte reportID = data[0];
nuclear@1 795
nuclear@1 796 if (reportID == 0)
nuclear@1 797 {
nuclear@1 798 // Not using reports so remove from data packet.
nuclear@1 799 data++;
nuclear@1 800 length--;
nuclear@1 801 }
nuclear@1 802
nuclear@1 803 IOReturn result = IOHIDDeviceSetReport( Device,
nuclear@1 804 kIOHIDReportTypeFeature,
nuclear@1 805 reportID,
nuclear@1 806 data,
nuclear@1 807 length);
nuclear@1 808
nuclear@1 809 return (result == kIOReturnSuccess);
nuclear@1 810 }
nuclear@1 811
nuclear@1 812 bool HIDDevice::GetFeatureReport(UByte* data, UInt32 length)
nuclear@1 813 {
nuclear@1 814 if (!Device)
nuclear@1 815 return false;
nuclear@1 816
nuclear@1 817 CFIndex bufferLength = length;
nuclear@1 818
nuclear@1 819 // Report id is in first byte of the buffer.
nuclear@1 820 IOReturn result = IOHIDDeviceGetReport(Device, kIOHIDReportTypeFeature, data[0], data, &bufferLength);
nuclear@1 821
nuclear@1 822 return (result == kIOReturnSuccess);
nuclear@1 823 }
nuclear@1 824
nuclear@1 825 UInt64 HIDDevice::OnTicks(UInt64 ticksMks)
nuclear@1 826 {
nuclear@1 827
nuclear@1 828 if (Handler)
nuclear@1 829 {
nuclear@1 830 return Handler->OnTicks(ticksMks);
nuclear@1 831 }
nuclear@1 832
nuclear@1 833 return DeviceManagerThread::Notifier::OnTicks(ticksMks);
nuclear@1 834 }
nuclear@1 835
nuclear@1 836 HIDDeviceManager* HIDDeviceManager::CreateInternal(OSX::DeviceManager* devManager)
nuclear@1 837 {
nuclear@1 838
nuclear@1 839 if (!System::IsInitialized())
nuclear@1 840 {
nuclear@1 841 // Use custom message, since Log is not yet installed.
nuclear@1 842 OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
nuclear@1 843 LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
nuclear@1 844 return 0;
nuclear@1 845 }
nuclear@1 846
nuclear@1 847 Ptr<OSX::HIDDeviceManager> manager = *new OSX::HIDDeviceManager(devManager);
nuclear@1 848
nuclear@1 849 if (manager)
nuclear@1 850 {
nuclear@1 851 if (manager->Initialize())
nuclear@1 852 {
nuclear@1 853 manager->AddRef();
nuclear@1 854 }
nuclear@1 855 else
nuclear@1 856 {
nuclear@1 857 manager.Clear();
nuclear@1 858 }
nuclear@1 859 }
nuclear@1 860
nuclear@1 861 return manager.GetPtr();
nuclear@1 862 }
nuclear@1 863
nuclear@1 864 } // namespace OSX
nuclear@1 865
nuclear@1 866 //-------------------------------------------------------------------------------------
nuclear@1 867 // ***** Creation
nuclear@1 868
nuclear@1 869 // Creates a new HIDDeviceManager and initializes OVR.
nuclear@1 870 HIDDeviceManager* HIDDeviceManager::Create()
nuclear@1 871 {
nuclear@1 872 OVR_ASSERT_LOG(false, ("Standalone mode not implemented yet."));
nuclear@1 873
nuclear@1 874 if (!System::IsInitialized())
nuclear@1 875 {
nuclear@1 876 // Use custom message, since Log is not yet installed.
nuclear@1 877 OVR_DEBUG_STATEMENT(Log::GetDefaultLog()->
nuclear@1 878 LogMessage(Log_Debug, "HIDDeviceManager::Create failed - OVR::System not initialized"); );
nuclear@1 879 return 0;
nuclear@1 880 }
nuclear@1 881
nuclear@1 882 Ptr<OSX::HIDDeviceManager> manager = *new OSX::HIDDeviceManager(NULL);
nuclear@1 883
nuclear@1 884 if (manager)
nuclear@1 885 {
nuclear@1 886 if (manager->Initialize())
nuclear@1 887 {
nuclear@1 888 manager->AddRef();
nuclear@1 889 }
nuclear@1 890 else
nuclear@1 891 {
nuclear@1 892 manager.Clear();
nuclear@1 893 }
nuclear@1 894 }
nuclear@1 895
nuclear@1 896 return manager.GetPtr();
nuclear@1 897 }
nuclear@1 898
nuclear@1 899 } // namespace OVR