1/*
2 *
3 * @APPLE_LICENSE_HEADER_START@
4 *
5 * Copyright (c) 1999-2012 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 <AssertMacros.h>
26#include <pthread.h>
27#include <dispatch/dispatch.h>
28#include <mach/mach.h>
29#include <mach/mach_time.h>
30#include <CoreFoundation/CFRuntime.h>
31#include <CoreFoundation/CFBase.h>
32#include <IOKit/IOCFPlugIn.h>
33#include <IOKit/IOKitLib.h>
34#include <IOKit/IOCFSerialize.h>
35#include <IOKit/hid/IOHIDKeys.h>
36#include <IOKit/hid/IOHIDResourceUserClient.h>
37#include <IOKit/IODataQueueClient.h>
38#include "IOHIDUserDevice.h"
39#include <IOKit/IOKitLibPrivate.h>
40
41static IOHIDUserDeviceRef   __IOHIDUserDeviceCreate(
42                                    CFAllocatorRef          allocator,
43                                    CFAllocatorContext *    context __unused,
44                                    IOOptionBits            options);
45static void                 __IOHIDUserDeviceRelease( CFTypeRef object );
46static void                 __IOHIDUserDeviceRegister(void);
47static void                 __IOHIDUserDeviceQueueCallback(CFMachPortRef port, void *msg, CFIndex size, void *info);
48static void                 __IOHIDUserDeviceHandleReportAsyncCallback(void *refcon, IOReturn result);
49static Boolean              __IOHIDUserDeviceSetupAsyncSupport(IOHIDUserDeviceRef device);
50static IOReturn             __IOHIDUserDeviceStartDevice(IOHIDUserDeviceRef device, IOOptionBits options);
51
52
53typedef struct __IOHIDUserDevice
54{
55    CFRuntimeBase                   cfBase;   // base CFType information
56
57    io_service_t                    service;
58    io_connect_t                    connect;
59    CFDictionaryRef                 properties;
60    IOOptionBits                    options;
61
62    CFRunLoopRef                    runLoop;
63    CFStringRef                     runLoopMode;
64
65    dispatch_queue_t                dispatchQueue;
66
67    struct {
68        CFMachPortRef               port;
69        CFRunLoopSourceRef          source;
70        dispatch_source_t           dispatchSource;
71        IODataQueueMemory *         data;
72    } queue;
73
74    struct {
75        IONotificationPortRef       port;
76        CFRunLoopSourceRef          source;
77        IODataQueueMemory *         data;
78    } async;
79
80    struct {
81        IOHIDUserDeviceReportCallback   callback;
82        void *                          refcon;
83    } setReport, getReport;
84
85    struct {
86        IOHIDUserDeviceReportWithReturnLengthCallback   callback;
87        void *                                          refcon;
88    } getReportWithReturnLength;
89
90} __IOHIDUserDevice, *__IOHIDUserDeviceRef;
91
92static const CFRuntimeClass __IOHIDUserDeviceClass = {
93    0,                          // version
94    "IOHIDUserDevice",          // className
95    NULL,                       // init
96    NULL,                       // copy
97    __IOHIDUserDeviceRelease,   // finalize
98    NULL,                       // equal
99    NULL,                       // hash
100    NULL,                       // copyFormattingDesc
101    NULL,
102    NULL,
103    NULL
104};
105
106static pthread_once_t   __deviceTypeInit            = PTHREAD_ONCE_INIT;
107static CFTypeID         __kIOHIDUserDeviceTypeID    = _kCFRuntimeNotATypeID;
108static mach_port_t      __masterPort                = MACH_PORT_NULL;
109
110
111typedef struct __IOHIDDeviceHandleReportAsyncContext {
112    IOHIDUserDeviceHandleReportAsyncCallback   callback;
113    void *                          refcon;
114} IOHIDDeviceHandleReportAsyncContext;
115
116
117//------------------------------------------------------------------------------
118// __IOHIDUserDeviceRegister
119//------------------------------------------------------------------------------
120void __IOHIDUserDeviceRegister(void)
121{
122    IOMasterPort(bootstrap_port, &__masterPort);
123    __kIOHIDUserDeviceTypeID = _CFRuntimeRegisterClass(&__IOHIDUserDeviceClass);
124}
125
126//------------------------------------------------------------------------------
127// __IOHIDUserDeviceCreate
128//------------------------------------------------------------------------------
129IOHIDUserDeviceRef __IOHIDUserDeviceCreate(
130                                CFAllocatorRef              allocator,
131                                CFAllocatorContext *        context __unused,
132                                IOOptionBits                options)
133{
134    IOHIDUserDeviceRef  device = NULL;
135    void *          offset  = NULL;
136    uint32_t        size;
137
138    /* allocate service */
139    size  = sizeof(__IOHIDUserDevice) - sizeof(CFRuntimeBase);
140    device = (IOHIDUserDeviceRef)_CFRuntimeCreateInstance(allocator, IOHIDUserDeviceGetTypeID(), size, NULL);
141
142    if (!device)
143        return NULL;
144
145    offset = device;
146    bzero(offset + sizeof(CFRuntimeBase), size);
147
148    device->options = options;
149
150    return device;
151}
152
153//------------------------------------------------------------------------------
154// __IOHIDUserDeviceRelease
155//------------------------------------------------------------------------------
156void __IOHIDUserDeviceRelease( CFTypeRef object )
157{
158    IOHIDUserDeviceRef device = (IOHIDUserDeviceRef)object;
159
160    if ( device->queue.data )
161    {
162#if !__LP64__
163        vm_address_t        mappedMem = (vm_address_t)device->queue.data;
164#else
165        mach_vm_address_t   mappedMem = (mach_vm_address_t)device->queue.data;
166#endif
167        IOConnectUnmapMemory (  device->connect,
168                                0,
169                                mach_task_self(),
170                                mappedMem);
171        device->queue.data = NULL;
172    }
173
174    if ( device->queue.dispatchSource ) {
175        dispatch_release(device->queue.dispatchSource);
176        device->queue.dispatchSource = NULL;
177    }
178
179    if ( device->queue.source ) {
180        CFRelease(device->queue.source);
181        device->queue.source = NULL;
182    }
183
184    if ( device->queue.port ) {
185        mach_port_t port = CFMachPortGetPort(device->queue.port);
186
187        CFMachPortInvalidate(device->queue.port);
188        CFRelease(device->queue.port);
189
190        mach_port_mod_refs(mach_task_self(),
191                   port,
192                   MACH_PORT_RIGHT_RECEIVE,
193                   -1);
194
195        device->queue.port = NULL;
196    }
197
198    if ( device->async.port ) {
199        IONotificationPortDestroy(device->async.port);
200        device->async.port = NULL;
201    }
202
203    if ( device->properties ) {
204        CFRelease(device->properties);
205        device->properties = NULL;
206    }
207
208    if ( device->connect ) {
209        IOObjectRelease(device->connect);
210        device->connect = 0;
211    }
212
213    if ( device->service ) {
214        IOObjectRelease(device->service);
215        device->service = 0;
216    }
217
218}
219
220//------------------------------------------------------------------------------
221// IOHIDUserDeviceGetTypeID
222//------------------------------------------------------------------------------
223CFTypeID IOHIDUserDeviceGetTypeID(void)
224{
225    if ( _kCFRuntimeNotATypeID == __kIOHIDUserDeviceTypeID )
226        pthread_once(&__deviceTypeInit, __IOHIDUserDeviceRegister);
227
228    return __kIOHIDUserDeviceTypeID;
229}
230
231//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
232// __IOHIDUserDeviceStartDevice
233//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
234IOReturn __IOHIDUserDeviceStartDevice(IOHIDUserDeviceRef device, IOOptionBits options)
235{
236    CFDataRef   data = NULL;
237    IOReturn    kr;
238    uint64_t    input = options;
239
240    data = IOCFSerialize(device->properties, 0);
241    require_action(data, error, kr=kIOReturnNoMemory);
242
243    kr = IOConnectCallMethod(device->connect, kIOHIDResourceDeviceUserClientMethodCreate, &input, 1, CFDataGetBytePtr(data), CFDataGetLength(data), NULL, NULL, NULL, NULL);
244    require_noerr(kr, error);
245
246error:
247    if ( data )
248        CFRelease(data);
249
250    return kr;
251
252}
253
254//------------------------------------------------------------------------------
255// IOHIDUserDeviceCreate
256//------------------------------------------------------------------------------
257IOHIDUserDeviceRef IOHIDUserDeviceCreate(
258                                CFAllocatorRef                  allocator,
259                                CFDictionaryRef                 properties)
260{
261    return IOHIDUserDeviceCreateWithOptions(allocator, properties, 0);
262}
263
264//------------------------------------------------------------------------------
265// IOHIDUserDeviceCreateWithOptions
266//------------------------------------------------------------------------------
267IOHIDUserDeviceRef IOHIDUserDeviceCreateWithOptions(CFAllocatorRef allocator, CFDictionaryRef properties, IOOptionBits options)
268{
269    IOHIDUserDeviceRef  device = NULL;
270    IOHIDUserDeviceRef  result = NULL;
271    kern_return_t       kr;
272
273    require(properties, error);
274
275    device = __IOHIDUserDeviceCreate(allocator, NULL, options);
276    require(device, error);
277
278    device->properties = CFDictionaryCreateCopy(allocator, properties);
279    require(device->properties, error);
280
281    device->service = IOServiceGetMatchingService(__masterPort, IOServiceMatching("IOHIDResource"));
282    require(device->service, error);
283
284    kr = IOServiceOpen(device->service, mach_task_self(), kIOHIDResourceUserClientTypeDevice, &device->connect);
285    require_noerr(kr, error);
286
287    if ( (device->options & kIOHIDUserDeviceCreateOptionStartWhenScheduled) == 0 ) {
288        kr = __IOHIDUserDeviceStartDevice(device, device->options);
289        require_noerr(kr, error);
290    }
291
292    result = device;
293    CFRetain(result);
294
295error:
296
297    if ( device )
298        CFRelease(device);
299
300    return result;
301}
302
303//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
304// __IOHIDUserDeviceSetupAsyncSupport
305//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
306Boolean __IOHIDUserDeviceSetupAsyncSupport(IOHIDUserDeviceRef device)
307{
308    Boolean result;
309
310    if ( !device->queue.data ) {
311        IOReturn ret;
312    #if !__LP64__
313        vm_address_t        address = 0;
314        vm_size_t           size    = 0;
315    #else
316        mach_vm_address_t   address = 0;
317        mach_vm_size_t      size    = 0;
318    #endif
319
320        ret = IOConnectMapMemory(device->connect, 0, mach_task_self(), &address, &size, kIOMapAnywhere);
321        require_noerr_action(ret, exit, result=false);
322
323        device->queue.data =(IODataQueueMemory * )address;
324    }
325
326    if ( !device->queue.port ) {
327        mach_port_t port = IODataQueueAllocateNotificationPort();
328
329        if ( port != MACH_PORT_NULL ) {
330            CFMachPortContext context = {0, device, NULL, NULL, NULL};
331
332            device->queue.port = CFMachPortCreateWithPort(CFGetAllocator(device), port, __IOHIDUserDeviceQueueCallback, &context, FALSE);
333        }
334    }
335    require_action(device->queue.port, exit, result=false);
336
337    if ( !device->async.port ) {
338        device->async.port = IONotificationPortCreate(kIOMasterPortDefault);
339    }
340
341    require_action(device->async.port, exit, result=false);
342
343    result = true;
344
345exit:
346
347    return true;
348}
349
350//------------------------------------------------------------------------------
351// IOHIDUserDeviceScheduleWithRunLoop
352//------------------------------------------------------------------------------
353void IOHIDUserDeviceScheduleWithRunLoop(IOHIDUserDeviceRef device, CFRunLoopRef runLoop, CFStringRef runLoopMode)
354{
355    if ( !__IOHIDUserDeviceSetupAsyncSupport(device) )
356        return;
357
358    if ( !device->queue.source ) {
359        device->queue.source = CFMachPortCreateRunLoopSource(CFGetAllocator(device), device->queue.port, 0);
360        if ( !device->queue.source )
361            return;
362    }
363
364    if ( !device->async.source ) {
365        device->async.source = IONotificationPortGetRunLoopSource(device->async.port);
366        if ( !device->async.source )
367            return;
368    }
369
370    CFRunLoopAddSource(runLoop, device->async.source, runLoopMode);
371    CFRunLoopAddSource(runLoop, device->queue.source, runLoopMode);
372    IOConnectSetNotificationPort(device->connect, 0, CFMachPortGetPort(device->queue.port), (uintptr_t)NULL);
373
374    if ( device->options & kIOHIDUserDeviceCreateOptionStartWhenScheduled ) {
375        __IOHIDUserDeviceStartDevice(device, device->options);
376    }
377
378}
379
380//------------------------------------------------------------------------------
381// IOHIDUserDeviceUnscheduleFromRunLoop
382//------------------------------------------------------------------------------
383void IOHIDUserDeviceUnscheduleFromRunLoop(IOHIDUserDeviceRef device, CFRunLoopRef runLoop, CFStringRef runLoopMode)
384{
385    if ( !device->queue.port )
386        return;
387
388    IOConnectSetNotificationPort(device->connect, 0, MACH_PORT_NULL, (uintptr_t)NULL);
389    CFRunLoopRemoveSource(runLoop, device->queue.source, runLoopMode);
390    CFRunLoopRemoveSource(runLoop, device->async.source, runLoopMode);
391}
392
393//------------------------------------------------------------------------------
394// IOHIDUserDeviceScheduleWithDispatchQueue
395//------------------------------------------------------------------------------
396void IOHIDUserDeviceScheduleWithDispatchQueue(IOHIDUserDeviceRef device, dispatch_queue_t queue)
397{
398    if ( !__IOHIDUserDeviceSetupAsyncSupport(device) )
399        return;
400
401    if ( !device->queue.dispatchSource ) {
402        device->queue.dispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_MACH_RECV, CFMachPortGetPort(device->queue.port), 0, queue);
403
404        if ( !device->queue.dispatchSource )
405            return;
406
407        dispatch_source_set_event_handler(device->queue.dispatchSource, ^{
408            CFRetain(device);
409            mach_msg_size_t size = sizeof(mach_msg_header_t) + MAX_TRAILER_SIZE;
410            mach_msg_header_t *msg = (mach_msg_header_t *)CFAllocatorAllocate(CFGetAllocator(device), size, 0);
411            msg->msgh_size = size;
412            for (;;) {
413                msg->msgh_bits = 0;
414                msg->msgh_local_port = CFMachPortGetPort(device->queue.port);
415                msg->msgh_remote_port = MACH_PORT_NULL;
416                msg->msgh_id = 0;
417                kern_return_t ret = mach_msg(msg, MACH_RCV_MSG|MACH_RCV_LARGE|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, CFMachPortGetPort(device->queue.port), 0, MACH_PORT_NULL);
418                if (MACH_MSG_SUCCESS == ret) break;
419                if (MACH_RCV_TOO_LARGE != ret) goto inner_exit;
420                uint32_t newSize = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
421                msg = CFAllocatorReallocate(CFGetAllocator(device), msg, newSize, 0);
422                msg->msgh_size = newSize;
423            }
424
425            __IOHIDUserDeviceQueueCallback(device->queue.port, msg, msg->msgh_size, device);
426
427        inner_exit:
428            CFAllocatorDeallocate(kCFAllocatorSystemDefault, msg);
429            CFRelease(device);
430        });
431    }
432
433    IONotificationPortSetDispatchQueue(device->async.port, queue);
434
435    dispatch_resume(device->queue.dispatchSource);
436    IOConnectSetNotificationPort(device->connect, 0, CFMachPortGetPort(device->queue.port), (uintptr_t)NULL);
437
438    device->dispatchQueue = queue;
439
440    if ( device->options & kIOHIDUserDeviceCreateOptionStartWhenScheduled ) {
441        __IOHIDUserDeviceStartDevice(device, device->options);
442    }
443
444}
445
446//------------------------------------------------------------------------------
447// IOHIDUserDeviceUnscheduleFromDispatchQueue
448//------------------------------------------------------------------------------
449void IOHIDUserDeviceUnscheduleFromDispatchQueue(IOHIDUserDeviceRef device, dispatch_queue_t queue)
450{
451    if ( !device->queue.port || device->dispatchQueue != queue)
452        return;
453
454    IOConnectSetNotificationPort(device->connect, 0, MACH_PORT_NULL, (uintptr_t)NULL);
455
456    if ( device->queue.dispatchSource ) {
457        dispatch_release(device->queue.dispatchSource);
458        device->queue.dispatchSource = NULL;
459    }
460
461    if ( device->async.port ) {
462        IONotificationPortDestroy(device->async.port);
463        device->async.port = NULL;
464    }
465}
466
467//------------------------------------------------------------------------------
468// IOHIDUserDeviceRegisterGetReportCallback
469//------------------------------------------------------------------------------
470void IOHIDUserDeviceRegisterGetReportCallback(IOHIDUserDeviceRef device, IOHIDUserDeviceReportCallback callback, void * refcon)
471{
472    device->getReport.callback  = callback;
473    device->getReport.refcon    = refcon;
474}
475
476//------------------------------------------------------------------------------
477// IOHIDUserDeviceRegisterGetReportCallback
478//------------------------------------------------------------------------------
479void IOHIDUserDeviceRegisterGetReportWithReturnLengthCallback(IOHIDUserDeviceRef device, IOHIDUserDeviceReportWithReturnLengthCallback callback, void * refcon)
480{
481    device->getReportWithReturnLength.callback  = callback;
482    device->getReportWithReturnLength.refcon    = refcon;
483}
484
485
486//------------------------------------------------------------------------------
487// IOHIDUserDeviceRegisterSetReportCallback
488//------------------------------------------------------------------------------
489void IOHIDUserDeviceRegisterSetReportCallback(IOHIDUserDeviceRef device, IOHIDUserDeviceReportCallback callback, void * refcon)
490{
491    device->setReport.callback  = callback;
492    device->setReport.refcon    = refcon;
493}
494
495#ifndef min
496#define min(a, b) \
497    ((a < b) ? a:b)
498#endif
499//------------------------------------------------------------------------------
500// __IOHIDUserDeviceQueueCallback
501//------------------------------------------------------------------------------
502void __IOHIDUserDeviceQueueCallback(CFMachPortRef port __unused, void *msg __unused, CFIndex size __unused, void *info)
503{
504    IOHIDUserDeviceRef device = (IOHIDUserDeviceRef)info;
505
506    if ( !device->queue.data )
507        return;
508
509    // check entry size
510    IODataQueueEntry *  nextEntry;
511    uint32_t            dataSize;
512
513    // if queue empty, then stop
514    while ((nextEntry = IODataQueuePeek(device->queue.data))) {
515
516        IOHIDResourceDataQueueHeader *  header                                                  = (IOHIDResourceDataQueueHeader*)&(nextEntry->data);
517        uint64_t                        response[kIOHIDResourceUserClientResponseIndexCount]    = {kIOReturnUnsupported,header->token};
518        uint8_t *                       responseReport  = NULL;
519        CFIndex                         responseLength  = 0;
520
521        // set report
522        if ( header->direction == kIOHIDResourceReportDirectionOut ) {
523            CFIndex     reportLength    = min(header->length, (nextEntry->size - sizeof(IOHIDResourceDataQueueHeader)));
524            uint8_t *   report          = ((uint8_t*)header)+sizeof(IOHIDResourceDataQueueHeader);
525
526            if ( device->setReport.callback )
527                response[kIOHIDResourceUserClientResponseIndexResult] = (*device->setReport.callback)(device->setReport.refcon, header->type, header->reportID, report, reportLength);
528
529        }
530        else if ( header->direction == kIOHIDResourceReportDirectionIn ) {
531            // RY: malloc our own data that we'll send back to the kernel.
532            // I thought about mapping the mem dec from the caller in kernel,
533            // but given the typical usage, it is so not worth it
534            responseReport = (uint8_t *)malloc(header->length);
535            responseLength = header->length;
536
537            if ( device->getReport.callback )
538                response[kIOHIDResourceUserClientResponseIndexResult] = (*device->getReport.callback)(device->getReport.refcon, header->type, header->reportID, responseReport, responseLength);
539
540            if ( device->getReportWithReturnLength.callback )
541                response[kIOHIDResourceUserClientResponseIndexResult] = (*device->getReportWithReturnLength.callback)(device->getReportWithReturnLength.refcon, header->type, header->reportID, responseReport, &responseLength);
542        }
543
544        // post the response
545        IOConnectCallMethod(device->connect, kIOHIDResourceDeviceUserClientMethodPostReportResponse, response, sizeof(response)/sizeof(uint64_t), responseReport, responseLength, NULL, NULL, NULL, NULL);
546
547        if ( responseReport )
548            free(responseReport);
549
550        // dequeue the item
551        dataSize = 0;
552        IODataQueueDequeue(device->queue.data, NULL, &dataSize);
553    }
554}
555
556
557//------------------------------------------------------------------------------
558// __IOHIDUserDeviceHandleReportAsyncCallback
559//------------------------------------------------------------------------------
560void __IOHIDUserDeviceHandleReportAsyncCallback(void *refcon, IOReturn result)
561{
562    IOHIDDeviceHandleReportAsyncContext *pContext = (IOHIDDeviceHandleReportAsyncContext *)refcon;
563
564    if (pContext->callback)
565        pContext->callback(pContext->refcon, result);
566
567    free(pContext);
568}
569
570//------------------------------------------------------------------------------
571// IOHIDUserDeviceHandleReportAsync
572//------------------------------------------------------------------------------
573IOReturn IOHIDUserDeviceHandleReportAsyncWithTimeStamp(IOHIDUserDeviceRef device, uint64_t timestamp, uint8_t *report, CFIndex reportLength, IOHIDUserDeviceHandleReportAsyncCallback callback, void * refcon)
574{
575    IOHIDDeviceHandleReportAsyncContext *pContext = malloc(sizeof(IOHIDDeviceHandleReportAsyncContext));
576
577    if (!pContext)
578        return kIOReturnNoMemory;
579
580    pContext->callback = callback;
581    pContext->refcon = refcon;
582
583    mach_port_t wakePort = MACH_PORT_NULL;
584    uint64_t asyncRef[kOSAsyncRef64Count];
585
586    wakePort = IONotificationPortGetMachPort(device->async.port);
587
588    asyncRef[kIOAsyncCalloutFuncIndex] = (uint64_t)(uintptr_t)__IOHIDUserDeviceHandleReportAsyncCallback;
589    asyncRef[kIOAsyncCalloutRefconIndex] = (uint64_t)(uintptr_t)pContext;
590
591    return IOConnectCallAsyncMethod(device->connect, kIOHIDResourceDeviceUserClientMethodHandleReport, wakePort, asyncRef, kOSAsyncRef64Count, &timestamp, 1, report, reportLength, NULL, NULL, NULL, NULL);
592}
593
594//------------------------------------------------------------------------------
595// IOHIDUserDeviceHandleReportWithTimeStamp
596//------------------------------------------------------------------------------
597IOReturn IOHIDUserDeviceHandleReportWithTimeStamp(IOHIDUserDeviceRef device, uint64_t timestamp, uint8_t * report, CFIndex reportLength)
598{
599    return IOConnectCallMethod(device->connect, kIOHIDResourceDeviceUserClientMethodHandleReport, &timestamp, 1, report, reportLength, NULL, NULL, NULL, NULL);
600}
601
602//------------------------------------------------------------------------------
603// IOHIDUserDeviceHandleReport
604//------------------------------------------------------------------------------
605IOReturn IOHIDUserDeviceHandleReport(IOHIDUserDeviceRef device, uint8_t * report, CFIndex reportLength)
606{
607    return IOHIDUserDeviceHandleReportWithTimeStamp(device, mach_absolute_time(), report, reportLength);
608}
609
610//------------------------------------------------------------------------------
611// IOHIDUserDeviceHandleReportAsync
612//------------------------------------------------------------------------------
613IOReturn IOHIDUserDeviceHandleReportAsync(IOHIDUserDeviceRef device, uint8_t * report, CFIndex reportLength, IOHIDUserDeviceHandleReportAsyncCallback callback, void * refcon)
614{
615    return IOHIDUserDeviceHandleReportAsyncWithTimeStamp(device, mach_absolute_time(), report, reportLength, callback, refcon);
616}
617
618