1/*
2 *
3 * @APPLE_LICENSE_HEADER_START@
4 *
5 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25#include "IOHIDEventServiceClass.h"
26#include "IOHIDEventServiceUserClient.h"
27#include "IOHIDEventData.h"
28#include <IOKit/hid/IOHIDUsageTables.h>
29#include <IOKit/hid/IOHIDServiceKeys.h>
30#include <IOKit/hid/IOHIDKeys.h>
31
32#if TARGET_OS_EMBEDDED // {
33#include <IOKit/hid/AppleEmbeddedHIDKeys.h>
34#endif // } TARGET_OS_EMBEDDED
35
36__BEGIN_DECLS
37#include <asl.h>
38#include <mach/mach.h>
39#include <mach/mach_interface.h>
40#include <IOKit/iokitmig.h>
41#include <IOKit/IOMessage.h>
42#include <mach/mach_time.h>
43__END_DECLS
44
45#define QUEUE_LOCK(service)     pthread_mutex_lock(&service->_queueLock)
46#define QUEUE_UNLOCK(service)   pthread_mutex_unlock(&service->_queueLock)
47#define QUEUE_WAIT(service)     while (service->_queueBusy) { pthread_cond_wait(&service->_queueCondition, &service->_queueLock); }
48#define QUEUE_SIGNAL(service)   pthread_cond_signal(&service->_queueCondition)
49
50//===========================================================================
51// Static Helper Declarations
52//===========================================================================
53static IOReturn MergeDictionaries(CFDictionaryRef srcDict, CFMutableDictionaryRef * pDstDict);
54
55
56//===========================================================================
57// CFPlugIn Static Assignments
58//===========================================================================
59IOCFPlugInInterface IOHIDEventServiceClass::sIOCFPlugInInterfaceV1 =
60{
61    0,
62    &IOHIDIUnknown::genericQueryInterface,
63    &IOHIDIUnknown::genericAddRef,
64    &IOHIDIUnknown::genericRelease,
65    1, 0,	// version/revision
66    &IOHIDEventServiceClass::_probe,
67    &IOHIDEventServiceClass::_start,
68    &IOHIDEventServiceClass::_stop
69};
70
71IOHIDServiceInterface2 IOHIDEventServiceClass::sIOHIDServiceInterface2 =
72{
73    0,
74    &IOHIDIUnknown::genericQueryInterface,
75    &IOHIDIUnknown::genericAddRef,
76    &IOHIDIUnknown::genericRelease,
77    &IOHIDEventServiceClass::_open,
78    &IOHIDEventServiceClass::_close,
79    &IOHIDEventServiceClass::_copyProperty,
80    &IOHIDEventServiceClass::_setProperty,
81    &IOHIDEventServiceClass::_setEventCallback,
82    &IOHIDEventServiceClass::_scheduleWithRunLoop,
83    &IOHIDEventServiceClass::_unscheduleFromRunLoop,
84    &IOHIDEventServiceClass::_copyEvent,
85    &IOHIDEventServiceClass::_setElementValue
86};
87
88//===========================================================================
89// CONSTRUCTOR / DESTRUCTOR methods
90//===========================================================================
91//---------------------------------------------------------------------------
92// IOHIDEventServiceClass
93//---------------------------------------------------------------------------
94IOHIDEventServiceClass::IOHIDEventServiceClass() : IOHIDIUnknown(&sIOCFPlugInInterfaceV1)
95{
96    _hidService.pseudoVTable    = NULL;
97    _hidService.obj             = this;
98
99    _service                    = MACH_PORT_NULL;
100    _connect                    = MACH_PORT_NULL;
101    _isOpen                     = FALSE;
102
103    _asyncPort                  = MACH_PORT_NULL;
104    _asyncCFMachPort            = NULL;
105    _asyncEventSource           = NULL;
106
107    _serviceProperties          = NULL;
108    _servicePreferences         = NULL;
109    _eventCallback              = NULL;
110    _eventTarget                = NULL;
111    _eventRefcon                = NULL;
112
113    _queueMappedMemory          = NULL;
114    _queueMappedMemorySize      = 0;
115
116    _queueBusy                  = FALSE;
117
118    pthread_mutex_init(&_queueLock, NULL);
119    pthread_cond_init(&_queueCondition, NULL);
120}
121
122//---------------------------------------------------------------------------
123// ~IOHIDEventServiceClass
124//---------------------------------------------------------------------------
125IOHIDEventServiceClass::~IOHIDEventServiceClass()
126{
127    QUEUE_LOCK(this);
128
129    QUEUE_WAIT(this);
130
131    // finished with the shared memory
132    if (_queueMappedMemory)
133    {
134#if !__LP64__
135        vm_address_t        mappedMem = (vm_address_t)_queueMappedMemory;
136#else
137        mach_vm_address_t   mappedMem = (mach_vm_address_t)_queueMappedMemory;
138#endif
139        IOConnectUnmapMemory (  _connect,
140                                0,
141                                mach_task_self(),
142                                mappedMem);
143        _queueMappedMemory = NULL;
144        _queueMappedMemorySize = 0;
145    }
146    QUEUE_UNLOCK(this);
147
148    if (_connect) {
149        IOServiceClose(_connect);
150        _connect = MACH_PORT_NULL;
151    }
152    if (_service) {
153        IOObjectRelease(_service);
154        _service = MACH_PORT_NULL;
155    }
156
157    if (_serviceProperties) {
158        CFRelease(_serviceProperties);
159        _serviceProperties = NULL;
160    }
161
162    if (_servicePreferences) {
163        CFRelease(_servicePreferences);
164        _servicePreferences = NULL;
165    }
166
167    if (_asyncEventSource) {
168        CFRelease(_asyncEventSource);
169        _asyncEventSource = NULL;
170    }
171
172    if ( _asyncCFMachPort ) {
173        CFMachPortInvalidate(_asyncCFMachPort);
174        CFRelease(_asyncCFMachPort);
175        _asyncCFMachPort = NULL;
176    }
177
178    if ( _asyncPort ) {
179        mach_port_mod_refs(mach_task_self(), _asyncPort, MACH_PORT_RIGHT_RECEIVE, -1);
180        _asyncPort = MACH_PORT_NULL;
181    }
182
183    pthread_mutex_destroy(&_queueLock);
184    pthread_cond_destroy(&_queueCondition);
185}
186
187//===========================================================================
188// IOCFPlugInInterface methods
189//===========================================================================
190IOReturn IOHIDEventServiceClass::_probe(void *self, CFDictionaryRef propertyTable, io_service_t service, SInt32 *order)
191{
192    return getThis(self)->probe(propertyTable, service, order);
193}
194
195IOReturn IOHIDEventServiceClass::_start(void *self, CFDictionaryRef propertyTable, io_service_t service)
196{
197    return getThis(self)->start(propertyTable, service);
198}
199
200IOReturn IOHIDEventServiceClass::_stop(void *self)
201{
202    return getThis(self)->stop();
203}
204
205boolean_t IOHIDEventServiceClass::_open(void * self, IOOptionBits options)
206{
207    return getThis(self)->open(options);
208}
209
210void IOHIDEventServiceClass::_close(void * self, IOOptionBits options)
211{
212    getThis(self)->close(options);
213}
214
215CFTypeRef IOHIDEventServiceClass::_copyProperty(void * self, CFStringRef key)
216{
217    return getThis(self)->copyProperty(key);
218}
219
220boolean_t IOHIDEventServiceClass::_setProperty(void * self, CFStringRef key, CFTypeRef property)
221{
222    return getThis(self)->setProperty(key, property);
223}
224
225IOHIDEventRef IOHIDEventServiceClass::_copyEvent(void *self, IOHIDEventType type, IOHIDEventRef matching, IOOptionBits options)
226{
227    return getThis(self)->copyEvent(type, matching, options);
228}
229
230IOReturn IOHIDEventServiceClass::_setElementValue(void *self, uint32_t usagePage, uint32_t usage, uint32_t value)
231{
232    return getThis(self)->setElementValue(usagePage, usage, value);
233}
234
235void IOHIDEventServiceClass::_setEventCallback(void * self, IOHIDServiceEventCallback callback, void * target, void * refcon)
236{
237    getThis(self)->setEventCallback(callback, target, refcon);
238}
239
240void IOHIDEventServiceClass::_scheduleWithRunLoop(void *self, CFRunLoopRef runLoop, CFStringRef runLoopMode)
241{
242    return getThis(self)->scheduleWithRunLoop(runLoop, runLoopMode);
243}
244
245void IOHIDEventServiceClass::_unscheduleFromRunLoop(void *self, CFRunLoopRef runLoop, CFStringRef runLoopMode)
246{
247    return getThis(self)->unscheduleFromRunLoop(runLoop, runLoopMode);
248}
249
250//------------------------------------------------------------------------------
251// IOHIDEventServiceClass::_queueEventSourceCallback
252//------------------------------------------------------------------------------
253void IOHIDEventServiceClass::_queueEventSourceCallback(
254                                            CFMachPortRef               cfPort,
255                                            mach_msg_header_t *         msg __unused,
256                                            CFIndex                     size __unused,
257                                            void *                      info)
258{
259    IOHIDEventServiceClass *eventService = (IOHIDEventServiceClass *)info;
260    IOReturn ret = kIOReturnSuccess;
261
262    QUEUE_LOCK(eventService);
263
264    QUEUE_WAIT(eventService);
265
266    eventService->_queueBusy = TRUE;
267
268    do {
269        if ( !eventService->_queueMappedMemory )
270            break;
271
272        // check entry size
273        IODataQueueEntry *  nextEntry;
274        uint32_t            dataSize;
275
276        // if queue empty, then stop
277        while ((nextEntry = IODataQueuePeek(eventService->_queueMappedMemory))) {
278            // RY: cfPort==NULL means we're draining
279            if ( cfPort ) {
280                IOHIDEventRef event = IOHIDEventCreateWithBytes(kCFAllocatorDefault, (const UInt8*)&(nextEntry->data), nextEntry->size);
281
282                if ( event ) {
283                    QUEUE_UNLOCK(eventService);
284                    eventService->dispatchHIDEvent(event);
285                    QUEUE_LOCK(eventService);
286                    CFRelease(event);
287                }
288            }
289
290            // dequeue the item
291            dataSize = 0;
292            IODataQueueDequeue(eventService->_queueMappedMemory, NULL, &dataSize);
293        }
294    } while ( 0 );
295
296    eventService->_queueBusy = FALSE;
297
298    QUEUE_UNLOCK(eventService);
299
300    QUEUE_SIGNAL(eventService);
301}
302
303
304//------------------------------------------------------------------------------
305// IOHIDEventServiceClass::dispatchHIDEvent
306//------------------------------------------------------------------------------
307void IOHIDEventServiceClass::dispatchHIDEvent(IOHIDEventRef event, IOOptionBits options)
308{
309    if ( !_eventCallback )
310        return;
311
312    (*_eventCallback)(_eventTarget, _eventRefcon, (void *)&_hidService, event, options);
313}
314
315
316
317// Public Methods
318//---------------------------------------------------------------------------
319// IOHIDEventServiceClass::alloc
320//---------------------------------------------------------------------------
321IOCFPlugInInterface ** IOHIDEventServiceClass::alloc()
322{
323    IOHIDEventServiceClass * self = new IOHIDEventServiceClass;
324
325    return self ? (IOCFPlugInInterface **) &self->iunknown.pseudoVTable : NULL;
326}
327
328//---------------------------------------------------------------------------
329// IOHIDEventServiceClass::queryInterface
330//---------------------------------------------------------------------------
331HRESULT IOHIDEventServiceClass::queryInterface(REFIID iid, void **ppv)
332{
333    CFUUIDRef uuid = CFUUIDCreateFromUUIDBytes(NULL, iid);
334    HRESULT res = S_OK;
335
336    if (CFEqual(uuid, IUnknownUUID) || CFEqual(uuid, kIOCFPlugInInterfaceID))
337    {
338        *ppv = &iunknown;
339        addRef();
340    }
341    else if (CFEqual(uuid, kIOHIDServiceInterface2ID))
342    {
343        _hidService.pseudoVTable    = (IUnknownVTbl *)  &sIOHIDServiceInterface2;
344        _hidService.obj             = this;
345        *ppv = &_hidService;
346        addRef();
347    }
348    else {
349        *ppv = 0;
350    }
351
352    if (!*ppv)
353        res = E_NOINTERFACE;
354
355    CFRelease(uuid);
356    return res;
357}
358
359//---------------------------------------------------------------------------
360// IOHIDEventServiceClass::probe
361//---------------------------------------------------------------------------
362IOReturn IOHIDEventServiceClass::probe(CFDictionaryRef propertyTable __unused, io_service_t service, SInt32 * order __unused)
363{
364    if (!service || !IOObjectConformsTo(service, "IOHIDEventService"))
365        return kIOReturnBadArgument;
366
367    return kIOReturnSuccess;
368}
369
370#define GET_AND_SET_SERVICE_PROPERTY(reg,regKey,dict,propKey)                                               \
371{                                                                                                   \
372    CFTypeRef typeRef = IORegistryEntryCreateCFProperty(reg,regKey, kCFAllocatorDefault, kNilOptions);\
373    if (typeRef)                                                                                    \
374    {                                                                                               \
375        CFDictionarySetValue(dict,propKey,typeRef);                                                 \
376        CFRelease(typeRef);                                                                         \
377    }                                                                                               \
378}
379
380#define GET_AND_SET_PROPERTY(prop,regKey,dict,propKey)                                              \
381{                                                                                                   \
382    CFTypeRef typeRef = CFDictionaryGetValue(prop,regKey);                                          \
383    if (typeRef)                                                                                    \
384        CFDictionarySetValue(dict,propKey,typeRef);                                                 \
385}
386
387
388//---------------------------------------------------------------------------
389// IOHIDEventServiceClass::start
390//---------------------------------------------------------------------------
391IOReturn IOHIDEventServiceClass::start(CFDictionaryRef propertyTable __unused, io_service_t service)
392{
393    IOReturn                ret             = kIOReturnError;
394    HRESULT                 plugInResult 	= S_OK;
395    SInt32                  score           = 0;
396    CFMutableDictionaryRef  serviceProps    = NULL;
397
398    do {
399        _service = service;
400        IOObjectRetain(_service);
401
402        _serviceProperties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
403
404        if ( !_serviceProperties ) {
405            ret = kIOReturnNoMemory;
406            break;
407        }
408
409        IORegistryEntryCreateCFProperties(service, &serviceProps, kCFAllocatorDefault, 0);
410
411        if ( !serviceProps )
412            break;
413
414        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDTransportKey), _serviceProperties, CFSTR(kIOHIDServiceTransportKey));
415        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDVendorIDKey), _serviceProperties, CFSTR(kIOHIDServiceVendorIDKey));
416        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDVendorIDSourceKey), _serviceProperties, CFSTR(kIOHIDServiceVendorIDSourceKey));
417        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDProductIDKey), _serviceProperties, CFSTR(kIOHIDServiceProductIDKey));
418        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDVersionNumberKey), _serviceProperties, CFSTR(kIOHIDServiceVersionNumberKey));
419        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDManufacturerKey), _serviceProperties, CFSTR(kIOHIDServiceManufacturerKey));
420        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDProductKey), _serviceProperties, CFSTR(kIOHIDServiceProductKey));
421        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDSerialNumberKey), _serviceProperties, CFSTR(kIOHIDServiceSerialNumberKey));
422        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDCountryCodeKey), _serviceProperties, CFSTR(kIOHIDServiceCountryCodeKey));
423        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDLocationIDKey), _serviceProperties, CFSTR(kIOHIDServiceLocationIDKey));
424        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDPrimaryUsagePageKey), _serviceProperties, CFSTR(kIOHIDServicePrimaryUsagePageKey));
425        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDPrimaryUsageKey), _serviceProperties, CFSTR(kIOHIDServicePrimaryUsageKey));
426        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDDeviceUsagePairsKey), _serviceProperties, CFSTR(kIOHIDServiceDeviceUsagePairsKey));
427// This should be considered a dymanic property
428//        GET_AND_SET_PROPERTY(serviceProps, CFSTR(kIOHIDReportIntervalKey), _serviceProperties, CFSTR(kIOHIDServiceReportIntervalKey));
429
430        CFRelease(serviceProps);
431
432        /*
433        // Get properties, but do so via IORegistryEntryCreateCFProperty instead
434        // of IORegistryEntryCreateCFProperties to avoid pulling in more that we
435        // need and increasing footprint
436        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDTransportKey), _serviceProperties, CFSTR(kIOHIDServiceTransportKey));
437        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDVendorIDKey), _serviceProperties, CFSTR(kIOHIDServiceVendorIDKey));
438        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDVendorIDSourceKey), _serviceProperties, CFSTR(kIOHIDServiceVendorIDSourceKey));
439        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDProductIDKey), _serviceProperties, CFSTR(kIOHIDServiceProductIDKey));
440        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDVersionNumberKey), _serviceProperties, CFSTR(kIOHIDServiceVersionNumberKey));
441        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDManufacturerKey), _serviceProperties, CFSTR(kIOHIDServiceManufacturerKey));
442        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDProductKey), _serviceProperties, CFSTR(kIOHIDServiceProductKey));
443        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDSerialNumberKey), _serviceProperties, CFSTR(kIOHIDServiceSerialNumberKey));
444        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDCountryCodeKey), _serviceProperties, CFSTR(kIOHIDServiceCountryCodeKey));
445        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDLocationIDKey), _serviceProperties, CFSTR(kIOHIDServiceLocationIDKey));
446        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDPrimaryUsagePageKey), _serviceProperties, CFSTR(kIOHIDServicePrimaryUsagePageKey));
447        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDPrimaryUsageKey), _serviceProperties, CFSTR(kIOHIDServicePrimaryUsageKey));
448// This should be considered a dymanic property
449//        GET_AND_SET_SERVICE_PROPERTY(service, CFSTR(kIOHIDReportIntervalKey), _serviceProperties, CFSTR(kIOHIDServiceReportIntervalKey));
450        */
451
452        // Establish connection with device
453        ret = IOServiceOpen(_service, mach_task_self(), 0, &_connect);
454        if (ret != kIOReturnSuccess || !_connect)
455            break;
456
457        // allocate the memory
458        QUEUE_LOCK(this);
459
460#if !__LP64__
461        vm_address_t        address = nil;
462        vm_size_t           size    = 0;
463#else
464        mach_vm_address_t   address = nil;
465        mach_vm_size_t      size    = 0;
466#endif
467        ret = IOConnectMapMemory (	_connect,
468                                    0,
469                                    mach_task_self(),
470                                    &address,
471                                    &size,
472                                    kIOMapAnywhere	);
473        if (ret != kIOReturnSuccess)
474            return false;
475
476        _queueMappedMemory = (IODataQueueMemory *) address;
477        _queueMappedMemorySize = size;
478
479        if ( !_queueMappedMemory || !_queueMappedMemorySize )
480            break;
481
482        _queueBusy = FALSE;
483
484        QUEUE_UNLOCK(this);
485
486        QUEUE_SIGNAL(this);
487
488        return kIOReturnSuccess;
489
490    } while (0);
491
492    if ( _service ) {
493        IOObjectRelease(_service);
494        _service = NULL;
495    }
496
497    return ret;
498}
499
500//---------------------------------------------------------------------------
501// IOHIDEventServiceClass::stop
502//---------------------------------------------------------------------------
503IOReturn IOHIDEventServiceClass::stop()
504{
505    return kIOReturnSuccess;
506}
507
508//---------------------------------------------------------------------------
509// IOHIDEventServiceClass::open
510//---------------------------------------------------------------------------
511boolean_t IOHIDEventServiceClass::open(IOOptionBits options)
512{
513    uint32_t len = 0;
514    uint64_t input = options;
515    IOReturn kr;
516    bool     ret = true;
517
518    QUEUE_LOCK(this);
519
520    if ( !_isOpen ) {
521
522        do {
523            kr = IOConnectCallScalarMethod(_connect, kIOHIDEventServiceUserClientOpen, &input, 1, 0, &len);;
524            if ( kr != kIOReturnSuccess ) {
525                ret = false;
526                break;
527            }
528
529            _isOpen = true;
530
531        } while ( 0 );
532    }
533
534    QUEUE_UNLOCK(this);
535
536    return ret;
537}
538
539//---------------------------------------------------------------------------
540// IOHIDEventServiceClass::close
541//---------------------------------------------------------------------------
542void IOHIDEventServiceClass::close(IOOptionBits options)
543{
544    uint32_t len = 0;
545    uint64_t input = options;
546
547    QUEUE_LOCK(this);
548
549    if ( _isOpen ) {
550        (void) IOConnectCallScalarMethod(_connect, kIOHIDEventServiceUserClientClose, &input, 1, 0, &len);
551
552		// drain the queue just in case
553		QUEUE_UNLOCK(this);
554
555		if ( _eventCallback )
556			_queueEventSourceCallback(NULL, NULL, 0, this);
557
558		QUEUE_LOCK(this);
559
560        _isOpen = false;
561    }
562
563    QUEUE_UNLOCK(this);
564}
565
566//---------------------------------------------------------------------------
567// IOHIDEventServiceClass::copyProperty
568//---------------------------------------------------------------------------
569CFTypeRef IOHIDEventServiceClass::copyProperty(CFStringRef key)
570{
571    CFTypeRef value = CFDictionaryGetValue(_serviceProperties, key);
572
573    if ( value ) {
574        CFRetain(value);
575    } else {
576        value = IORegistryEntrySearchCFProperty(_service, kIOServicePlane, key, kCFAllocatorDefault, kIORegistryIterateRecursively| kIORegistryIterateParents);
577    }
578
579    return value;
580}
581
582//---------------------------------------------------------------------------
583// IOHIDEventServiceClass::createFixedProperties
584//---------------------------------------------------------------------------
585CFDictionaryRef IOHIDEventServiceClass::createFixedProperties(CFDictionaryRef floatProperties)
586{
587    CFMutableDictionaryRef  newProperties;
588    CFIndex                 count, index;
589
590    count = CFDictionaryGetCount(floatProperties);
591    if ( !count )
592        return NULL;
593
594    newProperties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
595    if ( !newProperties )
596        return NULL;
597
598    CFTypeRef   values[count];
599    CFTypeRef   keys[count];
600
601    CFDictionaryGetKeysAndValues(floatProperties, keys, values);
602
603    for ( index=0; index<count; index++) {
604        CFTypeRef   value       = values[index];
605        CFTypeRef   newValue    = NULL;
606
607        if ( (CFNumberGetTypeID() == CFGetTypeID(value)) && CFNumberIsFloatType((CFNumberRef)value) ) {
608            double      floatValue  = 0.0;
609            IOFixed     fixedValue  = 0;
610
611            CFNumberGetValue((CFNumberRef)value, kCFNumberDoubleType, &floatValue);
612
613            fixedValue = floatValue * 65535;
614
615            value = newValue = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &fixedValue);
616        }
617
618        CFDictionarySetValue(newProperties, keys[index], value);
619
620        if ( newValue )
621            CFRelease(newValue);
622    }
623
624    return newProperties;
625}
626
627//---------------------------------------------------------------------------
628// IOHIDEventServiceClass::setProperty
629//---------------------------------------------------------------------------
630boolean_t IOHIDEventServiceClass::setProperty(CFStringRef key, CFTypeRef property)
631{
632    CFDictionaryRef floatProperties = NULL;
633    boolean_t       retVal;
634
635#if TARGET_OS_EMBEDDED // {
636    // RY: Convert these floating point properties to IOFixed. Limiting to accel shake but can get apply to others as well
637    if ( CFEqual(CFSTR(kIOHIDAccelerometerShakeKey), key) && (CFDictionaryGetTypeID() == CFGetTypeID(property)) ) {
638        property = floatProperties = createFixedProperties((CFDictionaryRef)property);
639    }
640#endif // } TARGET_OS_EMBEDDED
641
642    retVal = (IORegistryEntrySetCFProperty(_service, key, property) == kIOReturnSuccess);
643
644    if ( floatProperties )
645        CFRelease(floatProperties);
646
647    return retVal;
648}
649
650//---------------------------------------------------------------------------
651// IOHIDEventServiceClass::copyEvent
652//---------------------------------------------------------------------------
653IOHIDEventRef IOHIDEventServiceClass::copyEvent(IOHIDEventType eventType, IOHIDEventRef matching, IOOptionBits options)
654{
655    const UInt8 *       inputData       = NULL;
656    size_t              inputDataSize   = 0;
657    UInt8 *             outputData      = NULL;
658    size_t              outputDataSize  = 0;
659    size_t              eventDataSize   = 0;
660    CFDataRef           fieldData       = NULL;
661    CFMutableDataRef    eventData       = NULL;
662    IOHIDEventRef       event           = NULL;
663    IOReturn            ret             = kIOReturnSuccess;
664
665    if ( matching ) {
666        fieldData = IOHIDEventCreateData(kCFAllocatorDefault, matching);
667
668        if ( fieldData ) {
669            inputData       = CFDataGetBytePtr(fieldData);
670            inputDataSize   = CFDataGetLength(fieldData);
671        }
672    }
673
674    do {
675        // Grab the actual event from the user client
676        uint64_t input[2];
677
678        input[0] = eventType;
679        input[1] = options;
680
681        IOHIDEventGetQueueElementSize(eventType, outputDataSize);
682        if ( !outputDataSize )
683            break;
684
685        eventData = CFDataCreateMutable(kCFAllocatorDefault, outputDataSize);
686        if ( !eventData )
687            break;
688
689        outputData = CFDataGetMutableBytePtr(eventData);
690
691        if ( !outputData )
692            break;
693
694        CFDataSetLength(eventData, outputDataSize);
695        bzero(outputData, outputDataSize);
696
697        ret = IOConnectCallMethod(_connect, kIOHIDEventServiceUserClientCopyEvent, input, 2, inputData, inputDataSize, NULL, NULL, outputData, &outputDataSize);
698        if ( ret != kIOReturnSuccess || !outputDataSize)
699            break;
700
701        CFDataSetLength(eventData, outputDataSize);
702
703        event = IOHIDEventCreateWithData(kCFAllocatorDefault, eventData);
704
705    } while ( 0 );
706
707    if ( fieldData )
708        CFRelease(fieldData);
709
710    if ( eventData )
711        CFRelease(eventData);
712
713    return event;
714}
715
716//---------------------------------------------------------------------------
717// IOHIDEventServiceClass::setElementValue
718//---------------------------------------------------------------------------
719IOReturn IOHIDEventServiceClass::setElementValue(uint32_t usagePage, uint32_t usage, uint32_t value)
720{
721    uint64_t input[3] = {usagePage, usage, value};
722
723    return IOConnectCallMethod(_connect, kIOHIDEventServiceUserClientSetElementValue, input, 3, NULL, 0, NULL, NULL, NULL, NULL);
724}
725
726
727//---------------------------------------------------------------------------
728// IOHIDEventServiceClass::setEventCallback
729//---------------------------------------------------------------------------
730void IOHIDEventServiceClass::setEventCallback(IOHIDServiceEventCallback callback, void * target, void * refcon)
731{
732    _eventCallback = callback;
733    _eventTarget = target;
734    _eventRefcon = refcon;
735}
736
737//---------------------------------------------------------------------------
738// IOHIDEventServiceClass::scheduleWithRunLoop
739//---------------------------------------------------------------------------
740void IOHIDEventServiceClass::scheduleWithRunLoop(CFRunLoopRef runLoop, CFStringRef runLoopMode)
741{
742    if ( !_asyncPort ) {
743        _asyncPort = IODataQueueAllocateNotificationPort();
744        if (!_asyncPort)
745            return;
746
747        IOReturn ret = IOConnectSetNotificationPort(_connect, 0, _asyncPort, NULL);
748        if ( kIOReturnSuccess != ret )
749            return;
750    }
751
752    if ( !_asyncCFMachPort ) {
753        CFMachPortContext   context;
754        Boolean             shouldFreeInfo = FALSE;
755
756        context.version = 1;
757        context.info = this;
758        context.retain = NULL;
759        context.release = NULL;
760        context.copyDescription = NULL;
761
762        _asyncCFMachPort = CFMachPortCreateWithPort(NULL, _asyncPort,
763                    (CFMachPortCallBack) IOHIDEventServiceClass::_queueEventSourceCallback,
764                    &context, &shouldFreeInfo);
765
766        if ( shouldFreeInfo ) {
767            // The CFMachPort we got might not work, but we'll proceed with it anyway.
768            asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s received an unexpected reused CFMachPort", __func__);
769        }
770
771        if (!_asyncCFMachPort)
772            return;
773    }
774
775    if ( !_asyncEventSource ) {
776
777        _asyncEventSource = CFMachPortCreateRunLoopSource(NULL, _asyncCFMachPort, 0);
778
779        if ( !_asyncEventSource )
780            return;
781    }
782    CFRunLoopAddSource(runLoop, _asyncEventSource, runLoopMode);
783
784    // kick him for good measure
785    if ( _queueMappedMemory )
786        CFRunLoopSourceSignal(_asyncEventSource);
787}
788
789//---------------------------------------------------------------------------
790// IOHIDEventServiceClass::unscheduleFromRunLoop
791//---------------------------------------------------------------------------
792void IOHIDEventServiceClass::unscheduleFromRunLoop(CFRunLoopRef runLoop, CFStringRef runLoopMode)
793{
794    CFRunLoopRemoveSource(runLoop, _asyncEventSource, runLoopMode);
795}
796
797//===========================================================================
798// Static Helper Definitions
799//===========================================================================
800IOReturn MergeDictionaries(CFDictionaryRef srcDict, CFMutableDictionaryRef * pDstDict)
801{
802    uint32_t        count;
803    CFTypeRef *     values;
804    CFStringRef *   keys;
805
806    if ( !pDstDict || !srcDict || !(count = CFDictionaryGetCount(srcDict)))
807        return kIOReturnBadArgument;
808
809    if ( !*pDstDict ||
810        !(*pDstDict = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
811        return kIOReturnNoMemory;
812
813    values  = (CFTypeRef *)malloc(sizeof(CFTypeRef) * count);
814    keys    = (CFStringRef *)malloc(sizeof(CFStringRef) * count);
815
816    for ( uint32_t i=0; i<count; i++)
817        CFDictionarySetValue(*pDstDict, keys[i], values[i]);
818
819    free(values);
820    free(keys);
821
822    return kIOReturnSuccess;
823}
824