00001
00002 #include "AppleRemote.h"
00003
00004 #include <stdio.h>
00005 #include <unistd.h>
00006 #include <stdlib.h>
00007 #include <ctype.h>
00008 #include <sys/errno.h>
00009 #include <sys/sysctl.h>
00010 #include <sysexits.h>
00011 #include <mach/mach.h>
00012 #include <mach/mach_error.h>
00013 #include <IOKit/IOKitLib.h>
00014 #include <IOKit/IOCFPlugIn.h>
00015 #include <IOKit/hid/IOHIDLib.h>
00016 #include <IOKit/hid/IOHIDKeys.h>
00017 #include <CoreFoundation/CoreFoundation.h>
00018 #include <CoreServices/CoreServices.h>
00019
00020 #include <sstream>
00021
00022 #include "mythlogging.h"
00023
00024 AppleRemote* AppleRemote::_instance = 0;
00025
00026
00027 #define REMOTE_SWITCH_COOKIE 19
00028 #define REMOTE_COOKIE_STR "19_"
00029 #define ATV_COOKIE_STR "17_9_280_"
00030 #define LONG_PRESS_COUNT 10
00031 #define KEY_RESPONSE_TIME 150
00032
00033 #define LOC QString("AppleRemote::")
00034
00035 typedef struct _ATV_IR_EVENT
00036 {
00037 UInt32 time_ms32;
00038 UInt32 time_ls32;
00039 UInt32 unknown1;
00040 UInt32 keycode;
00041 UInt32 unknown2;
00042 } ATV_IR_EVENT;
00043
00044 static io_object_t _findAppleRemoteDevice(const char *devName);
00045
00046 AppleRemote::Listener::~Listener()
00047 {
00048 }
00049
00050 AppleRemote * AppleRemote::Get()
00051 {
00052 if (_instance == 0)
00053 _instance = new AppleRemote();
00054
00055 return _instance;
00056 }
00057
00058 AppleRemote::~AppleRemote()
00059 {
00060 stopListening();
00061 if (mUsingNewAtv)
00062 delete mCallbackTimer;
00063 }
00064
00065 bool AppleRemote::isListeningToRemote()
00066 {
00067 return (hidDeviceInterface != NULL && !cookies.empty() && queue != NULL);
00068 }
00069
00070 void AppleRemote::setListener(AppleRemote::Listener* listener)
00071 {
00072 _listener = listener;
00073 }
00074
00075 void AppleRemote::startListening()
00076 {
00077 if (queue != NULL)
00078 return;
00079
00080 io_object_t hidDevice = _findAppleRemoteDevice("AppleIRController");
00081
00082 if (!hidDevice)
00083 hidDevice = _findAppleRemoteDevice("AppleTVIRReceiver");
00084
00085 if (!hidDevice ||
00086 !_createDeviceInterface(hidDevice) ||
00087 !_initCookies() || !_openDevice())
00088 {
00089 LOG(VB_GENERAL, LOG_ERR, LOC + "startListening() failed");
00090 stopListening();
00091 return;
00092 }
00093
00094 IOObjectRelease(hidDevice);
00095 }
00096
00097 void AppleRemote::stopListening()
00098 {
00099 if (queue != NULL)
00100 {
00101 (*queue)->stop(queue);
00102 (*queue)->dispose(queue);
00103 (*queue)->Release(queue);
00104 queue = NULL;
00105 }
00106
00107 if (!cookies.empty())
00108 cookies.clear();
00109
00110 if (hidDeviceInterface != NULL)
00111 {
00112 (*hidDeviceInterface)->close(hidDeviceInterface);
00113 (*hidDeviceInterface)->Release(hidDeviceInterface);
00114 hidDeviceInterface = NULL;
00115 }
00116 }
00117
00118 void AppleRemote::run()
00119 {
00120 RunProlog();
00121 CFRunLoopRun();
00122 exec();
00123 CFRunLoopStop(CFRunLoopGetCurrent());
00124 RunEpilog();
00125 }
00126
00127
00128 static float GetATVversion()
00129 {
00130 SInt32 macVersion;
00131 size_t len = 512;
00132 char hw_model[512] = "unknown";
00133
00134
00135 Gestalt(gestaltSystemVersion, &macVersion);
00136
00137 if ( macVersion > 0x1040 )
00138 return 0.0;
00139
00140 sysctlbyname("hw.model", &hw_model, &len, NULL, 0);
00141
00142 float version = 0.0;
00143 if ( strstr(hw_model,"AppleTV1,1") )
00144 {
00145
00146 FILE *inpipe = popen("sw_vers -buildVersion", "r");
00147 char linebuf[1000];
00148 if (inpipe && fgets(linebuf, sizeof(linebuf) - 1, inpipe) )
00149 {
00150 if ( strstr(linebuf,"8N5107") ) version = 1.0;
00151 else if (strstr(linebuf,"8N5239") ) version = 1.1;
00152 else if (strstr(linebuf,"8N5400") ) version = 2.0;
00153 else if (strstr(linebuf,"8N5455") ) version = 2.01;
00154 else if (strstr(linebuf,"8N5461") ) version = 2.02;
00155 else if (strstr(linebuf,"8N5519") ) version = 2.1;
00156 else if (strstr(linebuf,"8N5622") ) version = 2.2;
00157 else
00158 version = 2.3;
00159 }
00160 if (inpipe)
00161 pclose(inpipe);
00162 }
00163 return version;
00164 }
00165
00166
00167 AppleRemote::AppleRemote() : MThread("AppleRemote"),
00168 openInExclusiveMode(true),
00169 hidDeviceInterface(0),
00170 queue(0),
00171 remoteId(0),
00172 _listener(0),
00173 mUsingNewAtv(false),
00174 mLastEvent(AppleRemote::Undefined),
00175 mEventCount(0),
00176 mKeyIsDown(false)
00177 {
00178 if ( GetATVversion() > 2.2 )
00179 {
00180 LOG(VB_GENERAL, LOG_INFO,
00181 LOC + "AppleRemote() detected Apple TV > v2.3");
00182 mUsingNewAtv = true;
00183 mCallbackTimer = new QTimer();
00184 QObject::connect(mCallbackTimer, SIGNAL(timeout()),
00185 (const QObject*)this, SLOT(TimeoutHandler()));
00186 mCallbackTimer->setSingleShot(true);
00187 mCallbackTimer->setInterval(KEY_RESPONSE_TIME);
00188 }
00189
00190 _initCookieMap();
00191 }
00192
00202 void AppleRemote::_initCookieMap()
00203 {
00204
00205 cookieToButtonMapping["14_12_11_6_5_"] = Up;
00206 cookieToButtonMapping["14_13_11_6_5_"] = Down;
00207 cookieToButtonMapping["14_7_6_5_14_7_6_5_"] = Menu;
00208 cookieToButtonMapping["14_8_6_5_14_8_6_5_"] = Select;
00209 cookieToButtonMapping["14_9_6_5_14_9_6_5_"] = Right;
00210 cookieToButtonMapping["14_10_6_5_14_10_6_5_"] = Left;
00211 cookieToButtonMapping["14_6_5_4_2_"] = RightHold;
00212 cookieToButtonMapping["14_6_5_3_2_"] = LeftHold;
00213 cookieToButtonMapping["14_6_5_14_6_5_"] = MenuHold;
00214 cookieToButtonMapping["18_14_6_5_18_14_6_5_"] = PlayHold;
00215 cookieToButtonMapping["19_"] = ControlSwitched;
00216
00217
00218 cookieToButtonMapping["31_29_28_18_"] = Up;
00219 cookieToButtonMapping["31_30_28_18_"] = Down;
00220 cookieToButtonMapping["31_20_18_31_20_18_"] = Menu;
00221 cookieToButtonMapping["31_21_18_31_21_18_"] = Select;
00222 cookieToButtonMapping["31_22_18_31_22_18_"] = Right;
00223 cookieToButtonMapping["31_23_18_31_23_18_"] = Left;
00224 cookieToButtonMapping["31_18_4_2_"] = RightHold;
00225 cookieToButtonMapping["31_18_3_2_"] = LeftHold;
00226 cookieToButtonMapping["31_18_31_18_"] = MenuHold;
00227 cookieToButtonMapping["35_31_18_35_31_18_"] = PlayHold;
00228 cookieToButtonMapping["39_"] = ControlSwitched;
00229
00230
00231 cookieToButtonMapping["14_12_11_6_"] = Up;
00232 cookieToButtonMapping["14_13_11_6_"] = Down;
00233 cookieToButtonMapping["14_7_6_14_7_6_"] = Menu;
00234 cookieToButtonMapping["14_8_6_14_8_6_"] = Select;
00235 cookieToButtonMapping["14_9_6_14_9_6_"] = Right;
00236 cookieToButtonMapping["14_10_6_14_10_6_"] = Left;
00237 cookieToButtonMapping["14_6_4_2_"] = RightHold;
00238 cookieToButtonMapping["14_6_3_2_"] = LeftHold;
00239 cookieToButtonMapping["14_6_14_6_"] = MenuHold;
00240 cookieToButtonMapping["18_14_6_18_14_6_"] = PlayHold;
00241
00242
00243 cookieToButtonMapping["15_13_12_"] = Up;
00244 cookieToButtonMapping["15_14_12_"] = Down;
00245 cookieToButtonMapping["15_8_15_8_"] = Menu;
00246 cookieToButtonMapping["15_9_15_9_"] = Select;
00247 cookieToButtonMapping["15_10_15_10_"] = Right;
00248 cookieToButtonMapping["15_11_15_11_"] = Left;
00249 cookieToButtonMapping["15_5_3_"] = RightHold;
00250 cookieToButtonMapping["15_4_3_"] = LeftHold;
00251 cookieToButtonMapping["15_6_15_6_"] = MenuHold;
00252 cookieToButtonMapping["19_15_19_15_"] = PlayHold;
00253
00254
00255 cookieToButtonMapping["17_9_280_80"] = Up;
00256 cookieToButtonMapping["17_9_280_48"] = Down;
00257 cookieToButtonMapping["17_9_280_64"] = Menu;
00258 cookieToButtonMapping["17_9_280_32"] = Select;
00259 cookieToButtonMapping["17_9_280_96"] = Right;
00260 cookieToButtonMapping["17_9_280_16"] = Left;
00261
00262
00263 cookieToButtonMapping["33_31_30_21_20_2_"] = Up;
00264 cookieToButtonMapping["33_32_30_21_20_2_"] = Down;
00265 cookieToButtonMapping["33_22_21_20_2_33_22_21_20_2_"] = Menu;
00266 cookieToButtonMapping["33_23_21_20_2_33_23_21_20_2_"] = Select;
00267 cookieToButtonMapping["33_24_21_20_2_33_24_21_20_2_"] = Right;
00268 cookieToButtonMapping["33_25_21_20_2_33_25_21_20_2_"] = Left;
00269 cookieToButtonMapping["33_21_20_14_12_2_"] = RightHold;
00270 cookieToButtonMapping["33_21_20_13_12_2_"] = LeftHold;
00271 cookieToButtonMapping["33_21_20_2_33_21_20_2_"] = MenuHold;
00272 cookieToButtonMapping["37_33_21_20_2_37_33_21_20_2_"] = PlayHold;
00273
00274
00275 cookieToButtonMapping["33_21_20_8_2_33_21_20_8_2_"] = PlayPause;
00276 cookieToButtonMapping["33_21_20_3_2_33_21_20_3_2_"] = Select;
00277 cookieToButtonMapping["33_21_20_11_2_33_21_20_11_2_"] = PlayHold;
00278 }
00279
00280 static io_object_t _findAppleRemoteDevice(const char *devName)
00281 {
00282 CFMutableDictionaryRef hidMatchDictionary = 0;
00283 io_iterator_t hidObjectIterator = 0;
00284 io_object_t hidDevice = 0;
00285 IOReturn ioReturnValue;
00286
00287
00288 hidMatchDictionary = IOServiceMatching(devName);
00289
00290
00291 ioReturnValue = IOServiceGetMatchingServices(kIOMasterPortDefault,
00292 hidMatchDictionary,
00293 &hidObjectIterator);
00294
00295 if ((ioReturnValue == kIOReturnSuccess) && (hidObjectIterator != 0))
00296 hidDevice = IOIteratorNext(hidObjectIterator);
00297 else
00298 LOG(VB_GENERAL, LOG_ERR, LOC +
00299 QString("_findAppleRemoteDevice(%1) failed").arg(devName));
00300
00301
00302
00303 hidMatchDictionary = 0;
00304 return hidDevice;
00305 }
00306
00307 bool AppleRemote::_initCookies()
00308 {
00309 IOHIDDeviceInterface122** handle;
00310 CFArrayRef elements;
00311 IOReturn success;
00312
00313 handle = (IOHIDDeviceInterface122**)hidDeviceInterface;
00314 success = (*handle)->copyMatchingElements(handle,
00315 NULL,
00316 (CFArrayRef*)&elements);
00317
00318 if (success == kIOReturnSuccess)
00319 {
00320 for (CFIndex i = 0; i < CFArrayGetCount(elements); ++i)
00321 {
00322 CFDictionaryRef element;
00323 CFTypeRef object;
00324 long number;
00325 IOHIDElementCookie cookie;
00326
00327 element = (CFDictionaryRef)CFArrayGetValueAtIndex(elements, i);
00328 object = CFDictionaryGetValue(element,
00329 CFSTR(kIOHIDElementCookieKey));
00330
00331 if (object == 0 || CFGetTypeID(object) != CFNumberGetTypeID())
00332 continue;
00333
00334 if (!CFNumberGetValue((CFNumberRef)object,
00335 kCFNumberLongType, &number))
00336 continue;
00337
00338 cookie = (IOHIDElementCookie)number;
00339
00340 cookies.push_back((int)cookie);
00341 }
00342 return true;
00343 }
00344 return false;
00345 }
00346
00347 bool AppleRemote::_createDeviceInterface(io_object_t hidDevice)
00348 {
00349 IOReturn ioReturnValue;
00350 IOCFPlugInInterface** plugInInterface = NULL;
00351 SInt32 score = 0;
00352
00353
00354 ioReturnValue
00355 = IOCreatePlugInInterfaceForService(hidDevice,
00356 kIOHIDDeviceUserClientTypeID,
00357 kIOCFPlugInInterfaceID,
00358 &plugInInterface, &score);
00359
00360 if ((kIOReturnSuccess == ioReturnValue) &&
00361 plugInInterface && *plugInInterface)
00362 {
00363 HRESULT plugInResult = (*plugInInterface)->QueryInterface
00364 (plugInInterface,
00365 CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),
00366 (LPVOID*) (&hidDeviceInterface));
00367
00368 if (plugInResult != S_OK)
00369 LOG(VB_GENERAL, LOG_ERR, LOC + "_createDeviceInterface() failed");
00370
00371 (*plugInInterface)->Release(plugInInterface);
00372 }
00373 return hidDeviceInterface != 0;
00374 }
00375
00376 bool AppleRemote::_openDevice()
00377 {
00378 CFRunLoopSourceRef eventSource;
00379 IOReturn ioReturnValue;
00380 IOHIDOptionsType openMode;
00381
00382
00383 if (openInExclusiveMode)
00384 openMode = kIOHIDOptionsTypeSeizeDevice;
00385 else
00386 openMode = kIOHIDOptionsTypeNone;
00387
00388 ioReturnValue = (*hidDeviceInterface)->open(hidDeviceInterface, openMode);
00389
00390 if (ioReturnValue != KERN_SUCCESS)
00391 {
00392 LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() failed");
00393 return false;
00394 }
00395 queue = (*hidDeviceInterface)->allocQueue(hidDeviceInterface);
00396 if (!queue)
00397 {
00398 LOG(VB_GENERAL, LOG_ERR, LOC +
00399 "_openDevice() - error allocating queue");
00400 return false;
00401 }
00402
00403 HRESULT result = (*queue)->create(queue, 0, 12);
00404 if (result != S_OK || !queue)
00405 LOG(VB_GENERAL, LOG_ERR, LOC + "_openDevice() - error creating queue");
00406
00407 for (std::vector<int>::iterator iter = cookies.begin();
00408 iter != cookies.end();
00409 ++iter)
00410 {
00411 IOHIDElementCookie cookie = (IOHIDElementCookie)(*iter);
00412 (*queue)->addElement(queue, cookie, 0);
00413 }
00414
00415 ioReturnValue = (*queue)->createAsyncEventSource(queue, &eventSource);
00416 if (ioReturnValue != KERN_SUCCESS)
00417 {
00418 LOG(VB_GENERAL, LOG_ERR, LOC +
00419 "_openDevice() - failed to create async event source");
00420 return false;
00421 }
00422
00423 ioReturnValue = (*queue)->setEventCallout(queue, QueueCallbackFunction,
00424 this, NULL);
00425 if (ioReturnValue != KERN_SUCCESS)
00426 {
00427 LOG(VB_GENERAL, LOG_ERR, LOC +
00428 "_openDevice() - error registering callback");
00429 return false;
00430 }
00431
00432 CFRunLoopAddSource(CFRunLoopGetCurrent(),
00433 eventSource, kCFRunLoopDefaultMode);
00434 (*queue)->start(queue);
00435 return true;
00436 }
00437
00438 void AppleRemote::QueueCallbackFunction(void* target, IOReturn result,
00439 void* refcon, void* sender)
00440 {
00441 AppleRemote* remote = static_cast<AppleRemote*>(target);
00442
00443 if (remote->mUsingNewAtv)
00444 remote->_queueCallbackATV23(result);
00445 else
00446 remote->_queueCallbackFunction(result, refcon, sender);
00447 }
00448
00449 void AppleRemote::_queueCallbackFunction(IOReturn result,
00450 void* , void* )
00451 {
00452 AbsoluteTime zeroTime = {0,0};
00453 SInt32 sumOfValues = 0;
00454 std::stringstream cookieString;
00455
00456 while (result == kIOReturnSuccess)
00457 {
00458 IOHIDEventStruct event;
00459
00460 result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
00461 if (result != kIOReturnSuccess)
00462 break;
00463
00464 if (REMOTE_SWITCH_COOKIE == (int)event.elementCookie)
00465 {
00466 remoteId=event.value;
00467 _handleEventWithCookieString(REMOTE_COOKIE_STR, 0);
00468 }
00469 else
00470 {
00471 sumOfValues+=event.value;
00472 cookieString << std::dec << (int)event.elementCookie << "_";
00473 }
00474 }
00475
00476 _handleEventWithCookieString(cookieString.str(), sumOfValues);
00477 }
00478
00479 void AppleRemote::_queueCallbackATV23(IOReturn result)
00480 {
00481 AbsoluteTime zeroTime = {0,0};
00482 SInt32 sumOfValues = 0;
00483 std::stringstream cookieString;
00484 UInt32 key_code = 0;
00485
00486
00487 if (mCallbackTimer->isActive())
00488 {
00489 mCallbackTimer->stop();
00490 }
00491
00492 while (result == kIOReturnSuccess)
00493 {
00494 IOHIDEventStruct event;
00495
00496 result = (*queue)->getNextEvent(queue, &event, zeroTime, 0);
00497 if (result != kIOReturnSuccess)
00498 continue;
00499
00500 if ( ((int)event.elementCookie == 280) && (event.longValueSize == 20))
00501 {
00502 ATV_IR_EVENT* atv_ir_event = (ATV_IR_EVENT*)event.longValue;
00503 key_code = atv_ir_event->keycode;
00504 }
00505
00506 if (((int)event.elementCookie) != 5 )
00507 {
00508 sumOfValues += event.value;
00509 cookieString << std::dec << (int)event.elementCookie << "_";
00510 }
00511 }
00512
00513 if (strcmp(cookieString.str().c_str(), ATV_COOKIE_STR) == 0)
00514 {
00515 cookieString << std::dec << (int) ( (key_code & 0x00007F00) >> 8);
00516
00517 sumOfValues = 1;
00518 _handleEventATV23(cookieString.str(), sumOfValues);
00519 }
00520 }
00521
00522 void AppleRemote::_handleEventWithCookieString(std::string cookieString,
00523 SInt32 sumOfValues)
00524 {
00525 std::map<std::string,AppleRemote::Event>::iterator ii;
00526
00527 ii = cookieToButtonMapping.find(cookieString);
00528 if (ii != cookieToButtonMapping.end() && _listener)
00529 {
00530 AppleRemote::Event buttonid = ii->second;
00531 if (_listener)
00532 _listener->appleRemoteButton(buttonid, sumOfValues>0);
00533 }
00534 }
00535
00536
00537
00538
00539 void AppleRemote::_handleEventATV23(std::string cookieString,
00540 SInt32 sumOfValues)
00541 {
00542 std::map<std::string,AppleRemote::Event>::iterator ii;
00543 ii = cookieToButtonMapping.find(cookieString);
00544
00545 if (ii != cookieToButtonMapping.end() )
00546 {
00547 AppleRemote::Event event = ii->second;
00548
00549 if (mLastEvent == Undefined)
00550 {
00551 mEventCount = 1;
00552
00553
00554
00555 }
00556 else if (event != mLastEvent)
00557 {
00558 mEventCount = 1;
00559 mKeyIsDown = true;
00560
00561 if (_listener)
00562 {
00563
00564
00565 if (mLastEvent == Up || mLastEvent == Down ||
00566 mLastEvent == LeftHold || mLastEvent == RightHold)
00567 {
00568 _listener->appleRemoteButton(mLastEvent,
00569 false);
00570 }
00571 _listener->appleRemoteButton(event, mKeyIsDown);
00572 }
00573 }
00574 else
00575 {
00576 AppleRemote::Event newEvent = Undefined;
00577
00578 ++mEventCount;
00579
00580
00581 switch (event)
00582 {
00583 case Right:
00584 newEvent = RightHold;
00585 break;
00586 case Left:
00587 newEvent = LeftHold;
00588 break;
00589 case Menu:
00590 newEvent = MenuHold;
00591 break;
00592 case Select:
00593 newEvent = PlayHold;
00594 break;
00595 default:
00596 newEvent = event;
00597 }
00598
00599 if (newEvent == event)
00600 {
00601 if (mKeyIsDown)
00602 {
00603 if (_listener)
00604 {
00605
00606
00607 if (mLastEvent == Up || mLastEvent == Down ||
00608 mLastEvent == LeftHold || mLastEvent == RightHold)
00609 {
00610 _listener->appleRemoteButton(mLastEvent, false);
00611 }
00612 }
00613 }
00614
00615 mKeyIsDown = true;
00616 if (_listener)
00617 {
00618 _listener->appleRemoteButton(newEvent, mKeyIsDown);
00619 }
00620 }
00621 else if (mEventCount == LONG_PRESS_COUNT)
00622 {
00623 mKeyIsDown = true;
00624 if (_listener)
00625 {
00626 _listener->appleRemoteButton(newEvent, mKeyIsDown);
00627 }
00628 }
00629 }
00630
00631 mLastEvent = event;
00632 mCallbackTimer->start();
00633 }
00634 }
00635
00636
00637 void AppleRemote::TimeoutHandler()
00638 {
00639 if (_listener)
00640 {
00641 _listener->appleRemoteButton(mLastEvent, !mKeyIsDown);
00642 }
00643
00644 mKeyIsDown = !mKeyIsDown;
00645
00646 if (!mKeyIsDown)
00647 {
00648 mEventCount = 0;
00649 mLastEvent = Undefined;
00650 }
00651 else
00652 {
00653
00654
00655
00656 if (mLastEvent == Up || mLastEvent == Down ||
00657 mLastEvent == LeftHold || mLastEvent == RightHold)
00658 {
00659 mCallbackTimer->start();
00660 }
00661 else
00662 {
00663 mKeyIsDown = false;
00664 mEventCount = 0;
00665 mLastEvent = Undefined;
00666 }
00667
00668 }
00669 }