1/*
2 * @APPLE_LICENSE_HEADER_START@
3 *
4 * Copyright (c) 2012 Apple, Inc.  All Rights Reserved.
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <AssertMacros.h>
25#include <IOKit/IOLib.h>
26
27#ifdef enqueue
28    #undef enqueue
29#endif
30
31#include "IOHIDResourceUserClient.h"
32
33#define kHIDClientTimeoutUS     1000000ULL
34
35#define kHIDQueueSize           16384
36
37#define super IOUserClient
38
39
40OSDefineMetaClassAndStructors( IOHIDResourceDeviceUserClient, IOUserClient )
41
42
43//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
44// IOHIDResourceDeviceUserClient::_methods
45//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
46const IOExternalMethodDispatch IOHIDResourceDeviceUserClient::_methods[kIOHIDResourceDeviceUserClientMethodCount] = {
47    {   // kIOHIDResourceDeviceUserClientMethodCreate
48        (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_createDevice,
49        1, -1, /* 1 struct input : the report descriptor */
50        0, 0
51    },
52    {   // kIOHIDResourceDeviceUserClientMethodTerminate
53        (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_terminateDevice,
54        0, 0,
55        0, 0
56    },
57    {   // kIOHIDResourceDeviceUserClientMethodHandleReport
58        (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_handleReport,
59        1, -1, /* 1 struct input : the buffer */
60        0, 0
61    },
62    {   // kIOHIDResourceDeviceUserClientMethodPostReportResult
63        (IOExternalMethodAction) &IOHIDResourceDeviceUserClient::_postReportResult,
64        kIOHIDResourceUserClientResponseIndexCount, -1, /* 1 scalar input: the result, 1 struct input : the buffer */
65        0, 0
66    }
67};
68
69
70
71//----------------------------------------------------------------------------------------------------
72// IOHIDResourceDeviceUserClient::initWithTask
73//----------------------------------------------------------------------------------------------------
74bool IOHIDResourceDeviceUserClient::initWithTask(task_t owningTask, void * security_id, UInt32 type)
75{
76    bool result = false;
77
78#if !TARGET_OS_EMBEDDED
79    require_noerr_action(clientHasPrivilege(owningTask, kIOClientPrivilegeAdministrator), exit, result=false);
80#endif
81
82    result = super::initWithTask(owningTask, security_id, type);
83    require_action(result, exit, IOLog("%s failed\n", __FUNCTION__));
84
85    _pending            = OSSet::withCapacity(4);
86    _maxClientTimeoutUS = kHIDClientTimeoutUS;
87
88exit:
89    return result;
90}
91
92//----------------------------------------------------------------------------------------------------
93// IOHIDResourceDeviceUserClient::start
94//----------------------------------------------------------------------------------------------------
95bool IOHIDResourceDeviceUserClient::start(IOService * provider)
96{
97    IOWorkLoop *    workLoop;
98    bool            result;
99
100    _owner = OSDynamicCast(IOHIDResource, provider);
101    require_action(_owner, exit, result=false);
102    _owner->retain();
103
104    require_action(super::start(provider), exit, result=false);
105
106    workLoop = getWorkLoop();
107    require_action(workLoop, exit, result=false);
108
109    _createDeviceTimer = IOTimerEventSource::timerEventSource(this, OSMemberFunctionCast(IOTimerEventSource::Action, this, &IOHIDResourceDeviceUserClient::createAndStartDeviceAsyncCallback));
110    require_action(_createDeviceTimer, exit, result=false);
111    require_noerr_action(workLoop->addEventSource(_createDeviceTimer), exit, result=false);
112
113    _commandGate = IOCommandGate::commandGate(this);
114    require_action(_commandGate, exit, result=false);
115    require_noerr_action(workLoop->addEventSource(_commandGate), exit, result=false);
116
117    result = true;
118
119exit:
120    if ( result==false ) {
121        IOLog("%s failed\n", __FUNCTION__);
122        stop(provider);
123    }
124
125    return result;
126}
127
128//----------------------------------------------------------------------------------------------------
129// IOHIDResourceDeviceUserClient::stop
130//----------------------------------------------------------------------------------------------------
131void IOHIDResourceDeviceUserClient::stop(IOService * provider)
132{
133    IOWorkLoop * workLoop = getWorkLoop();
134
135    require(workLoop, exit);
136
137    if ( _createDeviceTimer ) {
138        _createDeviceTimer->cancelTimeout();
139        workLoop->removeEventSource(_createDeviceTimer);
140    }
141
142    if ( _commandGate ) {
143        cleanupPendingReports();
144
145        workLoop->removeEventSource(_commandGate);
146    }
147
148exit:
149    super::stop(provider);
150}
151
152//----------------------------------------------------------------------------------------------------
153// IOHIDResourceDeviceUserClient::free
154//----------------------------------------------------------------------------------------------------
155void IOHIDResourceDeviceUserClient::free()
156{
157    if ( _properties )
158        _properties->release();
159
160    if ( _commandGate ) {
161        _commandGate->release();
162    }
163
164    if ( _createDeviceTimer )
165        _createDeviceTimer->release();
166
167    if ( _device )
168        _device->release();
169
170    if ( _queue )
171        _queue->release();
172
173    if ( _owner )
174        _owner->release();
175
176    return super::free();
177}
178
179//----------------------------------------------------------------------------------------------------
180// IOHIDResourceDeviceUserClient::registerNotificationPort
181//----------------------------------------------------------------------------------------------------
182IOReturn IOHIDResourceDeviceUserClient::registerNotificationPort(mach_port_t port, UInt32 type __unused, io_user_reference_t refCon __unused)
183{
184    IOReturn result;
185
186    require_action(!isInactive(), exit, result=kIOReturnOffline);
187
188    result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::registerNotificationPortGated), port);
189
190exit:
191    return result;
192}
193
194//----------------------------------------------------------------------------------------------------
195// IOHIDResourceDeviceUserClient::registerNotificationPortGated
196//----------------------------------------------------------------------------------------------------
197IOReturn IOHIDResourceDeviceUserClient::registerNotificationPortGated(mach_port_t port)
198{
199    IOReturn result;
200
201    require_action(!isInactive(), exit, result=kIOReturnOffline);
202
203    _port = port;
204    _queue->setNotificationPort(port);
205
206    result = kIOReturnSuccess;
207exit:
208    return result;
209}
210
211//----------------------------------------------------------------------------------------------------
212// IOHIDResourceDeviceUserClient::clientMemoryForType
213//----------------------------------------------------------------------------------------------------
214IOReturn IOHIDResourceDeviceUserClient::clientMemoryForType(UInt32 type __unused, IOOptionBits * options, IOMemoryDescriptor ** memory )
215{
216    IOReturn result;
217
218    require_action(!isInactive(), exit, result=kIOReturnOffline);
219
220    result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::clientMemoryForTypeGated), options, memory);
221
222exit:
223    return result;
224}
225
226//----------------------------------------------------------------------------------------------------
227// IOHIDResourceDeviceUserClient::clientMemoryForTypeGated
228//----------------------------------------------------------------------------------------------------
229IOReturn IOHIDResourceDeviceUserClient::clientMemoryForTypeGated(IOOptionBits * options, IOMemoryDescriptor ** memory )
230{
231    IOReturn ret;
232    IOMemoryDescriptor * memoryToShare = NULL;
233
234    require_action(!isInactive(), exit, ret=kIOReturnOffline);
235
236    if ( !_queue ) {
237        _queue = IOHIDResourceQueue::withCapacity(kHIDQueueSize);
238    }
239
240    require_action(_queue, exit, ret = kIOReturnNoMemory);
241
242    memoryToShare = _queue->getMemoryDescriptor();
243    require_action(memoryToShare, exit, ret = kIOReturnNoMemory);
244
245    memoryToShare->retain();
246
247    ret = kIOReturnSuccess;
248
249exit:
250    // set the result
251    *options = 0;
252    *memory  = memoryToShare;
253
254    return ret;
255}
256//----------------------------------------------------------------------------------------------------
257// IOHIDResourceDeviceUserClient::externalMethod
258//----------------------------------------------------------------------------------------------------
259IOReturn IOHIDResourceDeviceUserClient::externalMethod(
260                                            uint32_t                    selector,
261                                            IOExternalMethodArguments * arguments,
262                                            IOExternalMethodDispatch *  dispatch,
263                                            OSObject *                  target,
264                                            void *                      reference)
265{
266    ExternalMethodGatedArguments gatedArguments = {selector, arguments, dispatch, target, reference};
267    IOReturn result;
268
269    require_action(!isInactive(), exit, result=kIOReturnOffline);
270
271    result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::externalMethodGated), &gatedArguments);
272
273exit:
274    return result;
275}
276
277//----------------------------------------------------------------------------------------------------
278// IOHIDResourceDeviceUserClient::externalMethodGated
279//----------------------------------------------------------------------------------------------------
280IOReturn IOHIDResourceDeviceUserClient::externalMethodGated(ExternalMethodGatedArguments *arguments)
281{
282    IOReturn result;
283
284    require_action(!isInactive(), exit, result=kIOReturnOffline);
285
286    require_action(arguments->selector < (uint32_t) kIOHIDResourceDeviceUserClientMethodCount, exit, result=kIOReturnBadArgument);
287
288    arguments->dispatch = (IOExternalMethodDispatch *) &_methods[arguments->selector];
289    if (!arguments->target)
290        arguments->target = this;
291
292    result = super::externalMethod(arguments->selector, arguments->arguments, arguments->dispatch, arguments->target, arguments->reference);
293
294exit:
295    return result;
296}
297
298//----------------------------------------------------------------------------------------------------
299// IOHIDResourceDeviceUserClient::createMemoryDescriptorFromInputArguments
300//----------------------------------------------------------------------------------------------------
301IOMemoryDescriptor * IOHIDResourceDeviceUserClient::createMemoryDescriptorFromInputArguments(
302                                            IOExternalMethodArguments * arguments)
303{
304    IOMemoryDescriptor * report = NULL;
305
306    if ( arguments->structureInputDescriptor ) {
307        report = arguments->structureInputDescriptor;
308        report->retain();
309    } else {
310        report = IOMemoryDescriptor::withAddress((void *)arguments->structureInput, arguments->structureInputSize, kIODirectionOut);
311    }
312
313    return report;
314}
315
316
317//----------------------------------------------------------------------------------------------------
318// IOHIDResourceDeviceUserClient::getService
319//----------------------------------------------------------------------------------------------------
320IOService * IOHIDResourceDeviceUserClient::getService(void)
321{
322    return _owner ? _owner : NULL;
323}
324
325//----------------------------------------------------------------------------------------------------
326// IOHIDResourceDeviceUserClient::clientClose
327//----------------------------------------------------------------------------------------------------
328IOReturn IOHIDResourceDeviceUserClient::clientClose(void)
329{
330    terminate();
331    return kIOReturnSuccess;
332}
333
334//----------------------------------------------------------------------------------------------------
335// IOHIDResourceDeviceUserClient::createAndStartDevice
336//----------------------------------------------------------------------------------------------------
337IOReturn IOHIDResourceDeviceUserClient::createAndStartDevice()
338{
339    IOReturn    result;
340    OSNumber *  number = NULL;
341
342    number = OSDynamicCast(OSNumber, _properties->getObject(kIOHIDRequestTimeoutKey));
343    if ( number )
344        _maxClientTimeoutUS = number->unsigned32BitValue();
345
346    // If after all the unwrapping we have a dictionary, let's create the device
347    _device = IOHIDUserDevice::withProperties(_properties);
348    require_action(_device, exit, result=kIOReturnNoResources);
349
350    require_action(_device->attach(this), exit, result=kIOReturnInternalError);
351
352    require_action(_device->start(this), exit, _device->detach(this); result=kIOReturnInternalError);
353
354    result = kIOReturnSuccess;
355
356exit:
357    if ( result!=kIOReturnSuccess ) {
358        IOLog("%s: result=0x%08x\n", __FUNCTION__, result);
359        OSSafeReleaseNULL(_device);
360    }
361
362    return result;
363}
364
365//----------------------------------------------------------------------------------------------------
366// IOHIDResourceDeviceUserClient::createAndStartDeviceAsyncCallback
367//----------------------------------------------------------------------------------------------------
368void IOHIDResourceDeviceUserClient::createAndStartDeviceAsyncCallback()
369{
370    createAndStartDevice();
371}
372
373//----------------------------------------------------------------------------------------------------
374// IOHIDResourceDeviceUserClient::createAndStartDeviceAsync
375//----------------------------------------------------------------------------------------------------
376IOReturn IOHIDResourceDeviceUserClient::createAndStartDeviceAsync()
377{
378    _createDeviceTimer->setTimeoutMS(0);
379    return kIOReturnSuccess;
380}
381
382//----------------------------------------------------------------------------------------------------
383// IOHIDResourceDeviceUserClient::createDevice
384//----------------------------------------------------------------------------------------------------
385IOReturn IOHIDResourceDeviceUserClient::createDevice(IOExternalMethodArguments * arguments)
386{
387    IOMemoryDescriptor *    propertiesDesc      = NULL;
388    void *                  propertiesData      = NULL;
389    IOByteCount             propertiesLength    = 0;
390    OSObject *              object              = NULL;
391    IOReturn                result;
392
393    // Report descriptor is static and thus can only be set on creation
394    require_action(_device==NULL, exit, result=kIOReturnInternalError);
395
396    // Let's deal with our device properties from data
397    propertiesDesc = createMemoryDescriptorFromInputArguments(arguments);
398    require_action(propertiesDesc, exit, result=kIOReturnNoMemory);
399
400    propertiesLength = propertiesDesc->getLength();
401    require_action(propertiesLength, exit, result=kIOReturnNoResources);
402
403    propertiesData = IOMalloc(propertiesLength);
404    require_action(propertiesData, exit, result=kIOReturnNoMemory);
405
406    propertiesDesc->readBytes(0, propertiesData, propertiesLength);
407
408    require_action(strnlen((const char *) propertiesData, propertiesLength) < propertiesLength, exit, result=kIOReturnInternalError);
409
410    object = OSUnserializeXML((const char *)propertiesData, propertiesLength);
411    require_action(object, exit, result=kIOReturnInternalError);
412
413    _properties = OSDynamicCast(OSDictionary, object);
414    require_action(_properties, exit, result=kIOReturnNoMemory);
415
416    _properties->retain();
417
418    if ( arguments->scalarInput[0] )
419        result = createAndStartDeviceAsync();
420    else
421        result = createAndStartDevice();
422
423    require_noerr(result, exit);
424
425exit:
426
427    if ( object )
428        object->release();
429
430    if ( propertiesData && propertiesLength )
431        IOFree(propertiesData, propertiesLength);
432
433    if ( propertiesDesc )
434        propertiesDesc->release();
435
436    return result;
437}
438
439//----------------------------------------------------------------------------------------------------
440// IOHIDResourceDeviceUserClient::_createDevice
441//----------------------------------------------------------------------------------------------------
442IOReturn IOHIDResourceDeviceUserClient::_createDevice(
443                                        IOHIDResourceDeviceUserClient * target,
444                                        void *                          reference __unused,
445                                        IOExternalMethodArguments *     arguments)
446{
447    return target->createDevice(arguments);
448}
449
450struct IOHIDResourceDeviceUserClientAsyncParamBlock {
451    OSAsyncReference64                  fAsyncRef;
452    uint32_t                            fAsyncCount;
453};
454
455void IOHIDResourceDeviceUserClient::ReportComplete(void *param, IOReturn res, UInt32 remaining __unused)
456{
457    IOHIDResourceDeviceUserClientAsyncParamBlock *pb = (IOHIDResourceDeviceUserClientAsyncParamBlock *)param;
458
459    io_user_reference_t args[1];
460    args[0] = 0;
461
462    sendAsyncResult64(pb->fAsyncRef, res, args, 0);
463    IOFree(pb, sizeof(*pb));
464
465    release();
466}
467
468//----------------------------------------------------------------------------------------------------
469// IOHIDResourceDeviceUserClient::handleReport
470//----------------------------------------------------------------------------------------------------
471IOReturn IOHIDResourceDeviceUserClient::handleReport(IOExternalMethodArguments * arguments)
472{
473    AbsoluteTime timestamp;
474
475    if (_device == NULL) {
476        IOLog("%s failed : device is NULL\n", __FUNCTION__);
477        return kIOReturnNotOpen;
478    }
479
480    IOReturn                ret;
481    IOMemoryDescriptor *    report;
482
483    report = createMemoryDescriptorFromInputArguments(arguments);
484    if ( !report ) {
485        IOLog("%s failed : could not create descriptor\n", __FUNCTION__);
486        return kIOReturnNoMemory;
487    }
488
489    if ( arguments->scalarInput[0] )
490        AbsoluteTime_to_scalar(&timestamp) = arguments->scalarInput[0];
491    else
492        clock_get_uptime( &timestamp );
493
494    if ( !arguments->asyncWakePort ) {
495        ret = _device->handleReportWithTime(timestamp, report);
496        report->release();
497    } else {
498        IOHIDCompletion tap;
499
500        IOHIDResourceDeviceUserClientAsyncParamBlock *pb =
501        (IOHIDResourceDeviceUserClientAsyncParamBlock *)IOMalloc(sizeof(IOHIDResourceDeviceUserClientAsyncParamBlock));
502
503        if (!pb) {
504            report->release();
505            return kIOReturnNoMemory;   // need to release report
506        }
507
508        retain();
509
510        bcopy(arguments->asyncReference, pb->fAsyncRef, sizeof(OSAsyncReference64));
511        pb->fAsyncCount = arguments->asyncReferenceCount;
512
513        tap.target = this;
514        tap.action = OSMemberFunctionCast(IOHIDCompletionAction, this, &IOHIDResourceDeviceUserClient::ReportComplete);
515        tap.parameter = pb;
516
517        ret = _device->handleReportWithTimeAsync(timestamp, report, kIOHIDReportTypeInput, 0, 0, &tap);
518
519        report->release();
520
521        if (ret != kIOReturnSuccess) {
522            IOFree(pb, sizeof(*pb));
523            release();
524        }
525    }
526
527    return ret;
528}
529
530//----------------------------------------------------------------------------------------------------
531// IOHIDResourceDeviceUserClient::_handleReport
532//----------------------------------------------------------------------------------------------------
533IOReturn IOHIDResourceDeviceUserClient::_handleReport(IOHIDResourceDeviceUserClient    *target,
534                                             void                        *reference __unused,
535                                             IOExternalMethodArguments    *arguments)
536{
537    return target->handleReport(arguments);
538}
539
540typedef struct {
541    IOReturn                ret;
542    IOMemoryDescriptor *    descriptor;
543} __ReportResult;
544
545//----------------------------------------------------------------------------------------------------
546// IOHIDResourceDeviceUserClient::getReport
547//----------------------------------------------------------------------------------------------------
548IOReturn IOHIDResourceDeviceUserClient::getReport(IOMemoryDescriptor *report, IOHIDReportType reportType, IOOptionBits options)
549{
550    ReportGatedArguments    arguments   = {report, reportType, options};
551    IOReturn                result;
552
553    require_action(!isInactive(), exit, result=kIOReturnOffline);
554
555    result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::getReportGated), &arguments);
556exit:
557    return result;
558}
559
560//----------------------------------------------------------------------------------------------------
561// IOHIDResourceDeviceUserClient::getReport
562//----------------------------------------------------------------------------------------------------
563IOReturn IOHIDResourceDeviceUserClient::getReportGated(ReportGatedArguments * arguments)
564{
565    IOHIDResourceDataQueueHeader    header;
566    __ReportResult                  result;
567    AbsoluteTime                    ts;
568    IOReturn                        ret;
569    OSData *                        retData = NULL;
570
571    require_action(!isInactive(), exit, ret=kIOReturnOffline);
572
573    result.descriptor = arguments->report;
574
575    retData = OSData::withBytesNoCopy(&result, sizeof(__ReportResult));
576    require_action(retData, exit, ret=kIOReturnNoMemory);
577
578    header.direction   = kIOHIDResourceReportDirectionIn;
579    header.type        = arguments->reportType;
580    header.reportID    = arguments->options&0xff;
581    header.length      = (uint32_t)arguments->report->getLength();
582    header.token       = (intptr_t)retData;
583
584    _pending->setObject(retData);
585
586    require_action(_queue && _queue->enqueueReport(&header), exit, ret=kIOReturnNoMemory);
587
588    // if we successfully enqueue, let's sleep till we get a result from postReportResult
589    clock_interval_to_deadline(kMicrosecondScale, _maxClientTimeoutUS, &ts);
590
591    switch ( _commandGate->commandSleep(retData, ts, THREAD_ABORTSAFE) ) {
592        case THREAD_AWAKENED:
593            ret = result.ret;
594            break;
595        case THREAD_TIMED_OUT:
596            ret = kIOReturnTimeout;
597            break;
598        default:
599            ret = kIOReturnError;
600            break;
601    }
602
603exit:
604    if ( retData ) {
605        _pending->removeObject(retData);
606        _commandGate->commandWakeup(&_pending);
607        retData->release();
608    }
609
610    return ret;
611}
612
613//----------------------------------------------------------------------------------------------------
614// IOHIDResourceDeviceUserClient::setReport
615//----------------------------------------------------------------------------------------------------
616IOReturn IOHIDResourceDeviceUserClient::setReport(IOMemoryDescriptor *report, IOHIDReportType reportType, IOOptionBits options)
617{
618    ReportGatedArguments    arguments={report, reportType, options};
619    IOReturn                result;
620
621    require_action(!isInactive(), exit, result=kIOReturnOffline);
622
623    result = _commandGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDResourceDeviceUserClient::setReportGated), &arguments);
624exit:
625    return result;
626}
627
628//----------------------------------------------------------------------------------------------------
629// IOHIDResourceDeviceUserClient::setReportGated
630//----------------------------------------------------------------------------------------------------
631IOReturn IOHIDResourceDeviceUserClient::setReportGated(ReportGatedArguments * arguments)
632{
633    IOHIDResourceDataQueueHeader    header;
634    __ReportResult                  result;
635    AbsoluteTime                    ts;
636    IOReturn                        ret;
637    OSData *                        retData = NULL;
638
639    require_action(!isInactive(), exit, ret=kIOReturnOffline);
640
641    bzero(&result, sizeof(result));
642
643    retData = OSData::withBytesNoCopy(&result, sizeof(result));
644    require_action(retData, exit, ret=kIOReturnNoMemory);
645
646    header.direction   = kIOHIDResourceReportDirectionOut;
647    header.type        = arguments->reportType;
648    header.reportID    = arguments->options&0xff;
649    header.length      = (uint32_t)arguments->report->getLength();
650    header.token       = (intptr_t)retData;
651
652    _pending->setObject(retData);
653
654    require_action(_queue && _queue->enqueueReport(&header, arguments->report), exit, ret=kIOReturnNoMemory);
655
656    // if we successfully enqueue, let's sleep till we get a result from postReportResult
657    clock_interval_to_deadline(kMicrosecondScale, _maxClientTimeoutUS, (uint64_t *)&ts);
658
659    switch ( _commandGate->commandSleep(retData, ts, THREAD_ABORTSAFE) ) {
660        case THREAD_AWAKENED:
661            ret = result.ret;
662            break;
663        case THREAD_TIMED_OUT:
664            ret = kIOReturnTimeout;
665            break;
666        default:
667            ret = kIOReturnError;
668            break;
669    }
670
671exit:
672    if ( retData ) {
673        _pending->removeObject(retData);
674        _commandGate->commandWakeup(&_pending);
675        retData->release();
676    }
677
678    return ret;
679}
680
681//----------------------------------------------------------------------------------------------------
682// IOHIDResourceDeviceUserClient::postReportResult
683//----------------------------------------------------------------------------------------------------
684IOReturn IOHIDResourceDeviceUserClient::postReportResult(IOExternalMethodArguments * arguments)
685{
686    OSObject * tokenObj = (OSObject*)arguments->scalarInput[kIOHIDResourceUserClientResponseIndexToken];
687
688    if ( tokenObj && _pending->containsObject(tokenObj) ) {
689        OSData * data = OSDynamicCast(OSData, tokenObj);
690        if ( data ) {
691            __ReportResult * pResult = (__ReportResult*)data->getBytesNoCopy();
692
693            // RY: HIGHLY UNLIKELY > 4K
694            if ( pResult->descriptor && arguments->structureInput ) {
695                pResult->descriptor->writeBytes(0, arguments->structureInput, arguments->structureInputSize);
696
697                // 12978252:  If we get an IOBMD passed in, set the length to be the # of bytes that were transferred
698                IOBufferMemoryDescriptor * buffer = OSDynamicCast(IOBufferMemoryDescriptor, pResult->descriptor);
699                if (buffer)
700                    buffer->setLength((vm_size_t)arguments->structureInputSize);
701
702            }
703
704            pResult->ret = (IOReturn)arguments->scalarInput[kIOHIDResourceUserClientResponseIndexResult];
705
706            _commandGate->commandWakeup(data);
707        }
708
709    }
710
711    return kIOReturnSuccess;
712}
713
714//----------------------------------------------------------------------------------------------------
715// IOHIDResourceDeviceUserClient::_postReportResult
716//----------------------------------------------------------------------------------------------------
717IOReturn IOHIDResourceDeviceUserClient::_postReportResult(IOHIDResourceDeviceUserClient    *target,
718                                             void                        *reference __unused,
719                                             IOExternalMethodArguments    *arguments)
720{
721    return target->postReportResult(arguments);
722}
723
724//----------------------------------------------------------------------------------------------------
725// IOHIDResourceDeviceUserClient::cleanupPendingReports
726//----------------------------------------------------------------------------------------------------
727void IOHIDResourceDeviceUserClient::cleanupPendingReports()
728{
729    OSCollectionIterator *  iterator;
730    OSObject *              object;
731
732    iterator = OSCollectionIterator::withCollection(_pending);
733    if ( !iterator )
734        return;
735
736    while ( (object = iterator->getNextObject()) ) {
737        __ReportResult * pResult = (__ReportResult*)((OSData*)object)->getBytesNoCopy();
738
739        pResult->ret = kIOReturnAborted;
740
741        _commandGate->commandWakeup(object);
742    }
743
744    iterator->release();
745
746    while ( _pending->getCount() ) {
747        _commandGate->commandSleep(&_pending);
748    }
749}
750
751//----------------------------------------------------------------------------------------------------
752// IOHIDResourceDeviceUserClient::terminateDevice
753//----------------------------------------------------------------------------------------------------
754IOReturn IOHIDResourceDeviceUserClient::terminateDevice()
755{
756    if (_device) {
757        _device->terminate();
758    }
759    OSSafeRelease(_device);
760
761    return kIOReturnSuccess;
762}
763
764//----------------------------------------------------------------------------------------------------
765// IOHIDResourceDeviceUserClient::_terminateDevice
766//----------------------------------------------------------------------------------------------------
767IOReturn IOHIDResourceDeviceUserClient::_terminateDevice(
768                                        IOHIDResourceDeviceUserClient    *target,
769                                        void                            *reference __unused,
770                                        IOExternalMethodArguments       *arguments __unused)
771{
772    return target->terminateDevice();
773}
774
775
776//====================================================================================================
777// IOHIDResourceQueue
778//====================================================================================================
779#include <IOKit/IODataQueueShared.h>
780OSDefineMetaClassAndStructors( IOHIDResourceQueue, IOSharedDataQueue )
781
782IOHIDResourceQueue *IOHIDResourceQueue::withCapacity(UInt32 capacity)
783{
784    IOHIDResourceQueue *dataQueue = new IOHIDResourceQueue;
785
786    if (dataQueue) {
787        if (!dataQueue->initWithCapacity(capacity)) {
788            dataQueue->release();
789            dataQueue = 0;
790        }
791    }
792
793    return dataQueue;
794}
795
796void IOHIDResourceQueue::free()
797{
798    if ( _descriptor )
799    {
800        _descriptor->release();
801        _descriptor = 0;
802    }
803
804    IOSharedDataQueue::free();
805}
806
807#define ALIGNED_DATA_SIZE(data_size,align_size) ((((data_size - 1) / align_size) + 1) * align_size)
808
809Boolean IOHIDResourceQueue::enqueueReport(IOHIDResourceDataQueueHeader * header, IOMemoryDescriptor * report)
810{
811    UInt32              headerSize  = sizeof(IOHIDResourceDataQueueHeader);
812    UInt32              reportSize  = report ? (UInt32)report->getLength() : 0;
813    UInt32              dataSize    = ALIGNED_DATA_SIZE(headerSize + reportSize, sizeof(uint32_t));
814    const UInt32        head        = dataQueue->head;  // volatile
815    const UInt32        tail        = dataQueue->tail;
816    const UInt32        entrySize   = dataSize + DATA_QUEUE_ENTRY_HEADER_SIZE;
817    IODataQueueEntry *  entry;
818
819    if ( tail >= head )
820    {
821        // Is there enough room at the end for the entry?
822        if ( (tail + entrySize) <= getQueueSize() )
823        {
824            entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
825
826            entry->size = dataSize;
827
828            bcopy(header, &entry->data, headerSize);
829
830            if ( report )
831                report->readBytes(0, ((UInt8*)&entry->data) + headerSize, reportSize);
832
833            // The tail can be out of bound when the size of the new entry
834            // exactly matches the available space at the end of the queue.
835            // The tail can range from 0 to getQueueSize() inclusive.
836
837            dataQueue->tail += entrySize;
838        }
839        else if ( head > entrySize )     // Is there enough room at the beginning?
840        {
841            // Wrap around to the beginning, but do not allow the tail to catch
842            // up to the head.
843
844            dataQueue->queue->size = dataSize;
845
846            // We need to make sure that there is enough room to set the size before
847            // doing this. The user client checks for this and will look for the size
848            // at the beginning if there isn't room for it at the end.
849
850            if ( ( getQueueSize() - tail ) >= DATA_QUEUE_ENTRY_HEADER_SIZE )
851            {
852                ((IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail))->size = dataSize;
853            }
854
855            bcopy(header, &dataQueue->queue->data, sizeof(IOHIDResourceDataQueueHeader));
856            if ( report )
857                report->readBytes(0, ((UInt8*)&dataQueue->queue->data) + headerSize, reportSize);
858            dataQueue->tail = entrySize;
859        }
860        else
861        {
862            return false;    // queue is full
863        }
864    }
865    else
866    {
867        // Do not allow the tail to catch up to the head when the queue is full.
868        // That's why the comparison uses a '>' rather than '>='.
869
870        if ( (head - tail) > entrySize )
871        {
872            entry = (IODataQueueEntry *)((UInt8 *)dataQueue->queue + tail);
873
874            entry->size = dataSize;
875
876            bcopy(header, &entry->data, sizeof(IOHIDResourceDataQueueHeader));
877            if ( report )
878                report->readBytes(0, ((UInt8*)&entry->data) + headerSize, reportSize);
879            dataQueue->tail += entrySize;
880        }
881        else
882        {
883            return false;    // queue is full
884        }
885    }
886
887    // Send notification (via mach message) that data is available if either the
888    // queue was empty prior to enqueue() or queue was emptied during enqueue()
889    if ( ( head == tail ) || ( dataQueue->head == tail ) )
890        sendDataAvailableNotification();
891
892    return true;
893}
894
895
896void IOHIDResourceQueue::setNotificationPort(mach_port_t port)
897{
898    IOSharedDataQueue::setNotificationPort(port);
899
900    if (dataQueue->head != dataQueue->tail)
901        sendDataAvailableNotification();
902}
903
904IOMemoryDescriptor * IOHIDResourceQueue::getMemoryDescriptor()
905{
906    if (!_descriptor)
907        _descriptor = IOSharedDataQueue::getMemoryDescriptor();
908
909    return _descriptor;
910}
911