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 <TargetConditionals.h>
26
27#include <sys/systm.h>
28#include <sys/proc.h>
29#include <kern/task.h>
30#include <mach/port.h>
31#include <mach/message.h>
32#include <mach/mach_port.h>
33#include <IOKit/IOCommandGate.h>
34#include <IOKit/IOMemoryDescriptor.h>
35#include <IOKit/IOBufferMemoryDescriptor.h>
36#include <IOKit/IOService.h>
37#include <IOKit/IOSyncer.h>
38#include <IOKit/IOWorkLoop.h>
39#include <IOKit/IOKitKeysPrivate.h>
40#include <IOKit/IOUserClient.h>
41#include "IOHIDLibUserClient.h"
42#include "IOHIDDevice.h"
43#include "IOHIDEventQueue.h"
44
45__BEGIN_DECLS
46#include <ipc/ipc_port.h>
47__END_DECLS
48
49#if TARGET_OS_EMBEDDED
50#include <AppleMobileFileIntegrity/AppleMobileFileIntegrity.h>
51#define kIOHIDManagerUserAccessKeyboardEntitlement "com.apple.hid.manager.user-access-keyboard"
52#endif
53
54#define super IOUserClient
55
56struct AsyncParam {
57    OSAsyncReference64        fAsyncRef;
58    UInt32                    fMax;
59    IOMemoryDescriptor        *fMem;
60    IOHIDReportType            reportType;
61    uint32_t                fAsyncCount;
62};
63
64struct AsyncGateParam {
65    OSAsyncReference        asyncRef;
66    IOHIDReportType            reportType;
67    UInt32                    reportID;
68    void *                    reportBuffer;
69    UInt32                    reportBufferSize;
70    UInt32                    completionTimeOutMS;
71};
72
73
74OSDefineMetaClassAndStructors(IOHIDLibUserClient, IOUserClient);
75
76
77const IOExternalMethodDispatch IOHIDLibUserClient::
78sMethods[kIOHIDLibUserClientNumCommands] = {
79    { //    kIOHIDLibUserClientDeviceIsValid
80    (IOExternalMethodAction) &IOHIDLibUserClient::_deviceIsValid,
81    0, 0,
82    2, 0
83    },
84    { //    kIOHIDLibUserClientOpen
85    (IOExternalMethodAction) &IOHIDLibUserClient::_open,
86    1, 0,
87    0, 0
88    },
89    { //    kIOHIDLibUserClientClose
90    (IOExternalMethodAction) &IOHIDLibUserClient::_close,
91    0, 0,
92    0, 0
93    },
94    { //    kIOHIDLibUserClientCreateQueue
95    (IOExternalMethodAction) &IOHIDLibUserClient::_createQueue,
96    2, 0,
97    1, 0
98    },
99    { //    kIOHIDLibUserClientDisposeQueue
100    (IOExternalMethodAction) &IOHIDLibUserClient::_disposeQueue,
101    1, 0,
102    0, 0
103    },
104    { //    kIOHIDLibUserClientAddElementToQueue
105    (IOExternalMethodAction) &IOHIDLibUserClient::_addElementToQueue,
106    3, 0,
107    1, 0
108    },
109    { //    kIOHIDLibUserClientRemoveElementFromQueue
110    (IOExternalMethodAction) &IOHIDLibUserClient::_removeElementFromQueue,
111    2, 0,
112    1, 0
113    },
114    { //    kIOHIDLibUserClientQueueHasElement
115    (IOExternalMethodAction) &IOHIDLibUserClient::_queueHasElement,
116    2, 0,
117    1, 0
118    },
119    { //    kIOHIDLibUserClientStartQueue
120    (IOExternalMethodAction) &IOHIDLibUserClient::_startQueue,
121    1, 0,
122    0, 0
123    },
124    { //    kIOHIDLibUserClientStopQueue
125    (IOExternalMethodAction) &IOHIDLibUserClient::_stopQueue,
126    1, 0,
127    0, 0
128    },
129    { //    kIOHIDLibUserClientUpdateElementValues
130    (IOExternalMethodAction) &IOHIDLibUserClient::_updateElementValues,
131    kIOUCVariableStructureSize, 0,
132    0, 0
133    },
134    { //    kIOHIDLibUserClientPostElementValues
135    (IOExternalMethodAction) &IOHIDLibUserClient::_postElementValues,
136    kIOUCVariableStructureSize, 0,
137    0, 0
138    },
139    { //    kIOHIDLibUserClientGetReport
140    (IOExternalMethodAction) &IOHIDLibUserClient::_getReport,
141    3, 0,
142    0, kIOUCVariableStructureSize
143    },
144    { //    kIOHIDLibUserClientSetReport
145    (IOExternalMethodAction) &IOHIDLibUserClient::_setReport,
146    3, kIOUCVariableStructureSize,
147    0, 0
148    },
149    { //    kIOHIDLibUserClientGetElementCount
150    (IOExternalMethodAction) &IOHIDLibUserClient::_getElementCount,
151    0, 0,
152    2, 0
153    },
154    { //    kIOHIDLibUserClientGetElements
155    (IOExternalMethodAction) &IOHIDLibUserClient::_getElements,
156    1, 0,
157    0, kIOUCVariableStructureSize
158    },
159    // ASYNC METHODS
160    { //    kIOHIDLibUserClientSetQueueAsyncPort
161    (IOExternalMethodAction) &IOHIDLibUserClient::_setQueueAsyncPort,
162    1, 0,
163    0, 0
164    }
165};
166
167#define kIOHIDLibClientExtendedData     "ExtendedData"
168
169static void deflate_vec(uint32_t *dp, uint32_t d, const uint64_t *sp, uint32_t s)
170{
171    if (d > s)
172    d = s;
173
174    for (uint32_t i = 0; i < d; i++)
175    dp[i] = (uint32_t) sp[i];
176}
177
178
179bool IOHIDLibUserClient::initWithTask(task_t owningTask, void * /* security_id */, UInt32 /* type */)
180{
181    if (!super::init())
182        return false;
183
184    if (IOUserClient::clientHasPrivilege(owningTask, kIOClientPrivilegeAdministrator) != kIOReturnSuccess) {
185        // Preparing for extended data. Set a temporary key.
186        setProperty(kIOHIDLibClientExtendedData, true);
187    }
188
189    fClient = owningTask;
190    task_reference (fClient);
191
192    proc_t p = (proc_t)get_bsdtask_info(fClient);
193    fPid = proc_pid(p);
194
195    fQueueMap = OSArray::withCapacity(4);
196    if (!fQueueMap)
197        return false;
198
199    return true;
200}
201
202IOReturn IOHIDLibUserClient::clientClose(void)
203{
204    if ( !isInactive() && fGate ) {
205        fGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDLibUserClient::cleanupGated));
206        terminate();
207    }
208
209    return kIOReturnSuccess;
210}
211
212void IOHIDLibUserClient::cleanupGated(void)
213{
214    if (fClient) {
215        task_deallocate(fClient);
216        fClient = 0;
217    }
218
219   if (fNub) {
220
221        // First clear any remaining queues
222        setStateForQueues(kHIDQueueStateClear);
223
224        // Have been started so we better detach
225
226        // make sure device is closed (especially on crash)
227        // note radar #2729708 for a more comprehensive fix
228        // probably should also subclass clientDied for crash specific code
229        fNub->close(this, fCachedOptionBits);
230    }
231
232    if ( fResourceNotification ) {
233        fResourceNotification->remove();
234        fResourceNotification = 0;
235    }
236
237    if (fResourceES) {
238        if ( fWL )
239            fWL->removeEventSource(fResourceES);
240        fResourceES->release();
241        fResourceES = 0;
242    }
243}
244
245bool IOHIDLibUserClient::start(IOService *provider)
246{
247    OSDictionary *matching = NULL;
248    if (!super::start(provider))
249        return false;
250
251    fNub = OSDynamicCast(IOHIDDevice, provider);
252    if (!fNub)
253        return false;
254    fNub->retain();
255
256    fWL = getWorkLoop();
257    if (!fWL)
258        return false;
259
260    fWL->retain();
261
262    OSNumber *primaryUsage = (OSNumber*)fNub->copyProperty(kIOHIDPrimaryUsageKey);
263    OSNumber *primaryUsagePage = (OSNumber*)fNub->copyProperty(kIOHIDPrimaryUsagePageKey);
264
265    if ((OSDynamicCast(OSNumber, primaryUsagePage) && (primaryUsagePage->unsigned32BitValue() == kHIDPage_GenericDesktop)) &&
266        (OSDynamicCast(OSNumber, primaryUsage) && ((primaryUsage->unsigned32BitValue() == kHIDUsage_GD_Keyboard) || (primaryUsage->unsigned32BitValue() == kHIDUsage_GD_Keypad))))
267    {
268        fNubIsKeyboard = true;
269    }
270
271    OSSafeReleaseNULL(primaryUsage);
272    OSSafeReleaseNULL(primaryUsagePage);
273
274    IOCommandGate * cmdGate = IOCommandGate::commandGate(this);
275    if (!cmdGate)
276        goto ABORT_START;
277
278    fWL->addEventSource(cmdGate);
279
280    fGate = cmdGate;
281
282    fResourceES = IOInterruptEventSource::interruptEventSource
283        (this, OSMemberFunctionCast(IOInterruptEventSource::Action, this, &IOHIDLibUserClient::resourceNotificationGated));
284
285    if ( !fResourceES )
286        goto ABORT_START;
287
288    fWL->addEventSource(fResourceES);
289
290    // Get notified everytime Root properties change
291    matching = serviceMatching("IOResources");
292    fResourceNotification = addMatchingNotification(
293        gIOPublishNotification,
294        matching,
295        OSMemberFunctionCast(IOServiceMatchingNotificationHandler, this, &IOHIDLibUserClient::resourceNotification),
296        this);
297    matching->release();
298    matching = NULL;
299
300    if ( !fResourceNotification )
301        goto ABORT_START;
302
303    return true;
304
305ABORT_START:
306    if (fResourceES) {
307        fWL->removeEventSource(fResourceES);
308        fResourceES->release();
309        fResourceES = 0;
310    }
311    if (fGate) {
312        fWL->removeEventSource(fGate);
313        fGate->release();
314        fGate = 0;
315    }
316    fWL->release();
317    fWL = 0;
318
319    return false;
320}
321
322void IOHIDLibUserClient::stop(IOService *provider)
323{
324    super::stop(provider);
325}
326
327bool IOHIDLibUserClient::resourceNotification(void * refcon __unused, IOService *service __unused, IONotifier *notifier __unused)
328{
329    #pragma ignore(notifier)
330
331    if (!isInactive() && fResourceES)
332        fResourceES->interruptOccurred(0, 0, 0);
333
334    return true;
335}
336
337void IOHIDLibUserClient::resourceNotificationGated()
338{
339    IOReturn ret = kIOReturnSuccess;
340
341    do {
342        // We should force success on seize
343        if ( kIOHIDOptionsTypeSeizeDevice & fCachedOptionBits )
344            break;
345
346#if !TARGET_OS_EMBEDDED
347        OSData * data;
348        IOService * service = getResourceService();
349        if ( !service ) {
350            ret = kIOReturnError;
351            break;
352        }
353
354        data = (OSData*)service->copyProperty(kIOConsoleUsersSeedKey);
355
356        if ( !OSDynamicCast(OSData, data) || !data->getLength() || !data->getBytesNoCopy() ) {
357            ret = kIOReturnError;
358            OSSafeReleaseNULL(data);
359            break;
360        }
361
362        UInt64 currentSeed = 0;
363
364        switch ( data->getLength() ) {
365            case sizeof(UInt8):
366                currentSeed = *(UInt8*)(data->getBytesNoCopy());
367                break;
368            case sizeof(UInt16):
369                currentSeed = *(UInt16*)(data->getBytesNoCopy());
370                break;
371            case sizeof(UInt32):
372                currentSeed = *(UInt32*)(data->getBytesNoCopy());
373                break;
374            case sizeof(UInt64):
375            default:
376                currentSeed = *(UInt64*)(data->getBytesNoCopy());
377                break;
378        }
379        OSSafeReleaseNULL(data);
380
381        // We should return rather than break so that previous setting is retained
382        if ( currentSeed == fCachedConsoleUsersSeed )
383            return;
384
385        fCachedConsoleUsersSeed = currentSeed;
386#endif
387        ret = clientHasPrivilege(fClient, kIOClientPrivilegeAdministrator);
388        if (ret == kIOReturnSuccess)
389            break;
390
391#if TARGET_OS_EMBEDDED
392        if ( fNubIsKeyboard ) {
393            bool result = false;
394            IOReturn    kr;
395            proc_t      process;
396
397            process = (proc_t)get_bsdtask_info(fClient);
398            if ( process ) {
399
400                kr = AppleMobileFileIntegrity::AMFIEntitlementGetBool(process, kIOHIDManagerUserAccessKeyboardEntitlement, &result);
401
402                if ( kr || !result ) {
403                    char name[255];
404
405                    bzero(name, sizeof(name));
406                    proc_name(fPid, name, sizeof(name));
407
408                    IOLog("IOHIDLibUserClient: %s is not entitled\n", name);
409                }
410
411                if (kr == kIOReturnSuccess && result) {
412                    ret = kIOReturnSuccess;
413                }
414            }
415        } else {
416            ret = kIOReturnSuccess;
417        }
418#else
419        if ( fNubIsKeyboard ) {
420            IOUCProcessToken token;
421            token.token = fClient;
422            token.pid = fPid;
423            ret = clientHasPrivilege(&token, kIOClientPrivilegeSecureConsoleProcess);
424        } else {
425            ret = clientHasPrivilege(fClient, kIOClientPrivilegeConsoleUser);
426        }
427#endif /* TARGET_OS_EMBEDDED */
428
429    } while (false);
430
431    setValid(kIOReturnSuccess == ret);
432}
433
434typedef struct HIDCommandGateArgs {
435    uint32_t                    selector;
436    IOExternalMethodArguments * arguments;
437    IOExternalMethodDispatch *    dispatch;
438    OSObject *                    target;
439    void *                        reference;
440}HIDCommandGateArgs;
441
442IOReturn IOHIDLibUserClient::externalMethod(
443                                uint32_t                    selector,
444                                IOExternalMethodArguments * arguments,
445                                IOExternalMethodDispatch *    dispatch,
446                                OSObject *                    target,
447                                void *                        reference)
448{
449    IOReturn status = kIOReturnOffline;
450
451    if (fGate) {
452        HIDCommandGateArgs args;
453
454        args.selector    = selector;
455        args.arguments    = arguments;
456        args.dispatch    = dispatch;
457        args.target        = target;
458        args.reference    = reference;
459
460        if (!isInactive())
461            status = fGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, target, &IOHIDLibUserClient::externalMethodGated), (void *)&args);
462    }
463
464    return status;
465}
466
467IOReturn IOHIDLibUserClient::externalMethodGated(void * args)
468{
469    HIDCommandGateArgs *        cArgs        = (HIDCommandGateArgs *)args;
470    uint32_t                    selector    = cArgs->selector;
471    IOExternalMethodArguments * arguments    = cArgs->arguments;
472    IOExternalMethodDispatch *    dispatch    = cArgs->dispatch;
473    OSObject *                    target        = cArgs->target;
474    void *                        reference    = cArgs->reference;
475
476    if (isInactive())
477        return kIOReturnOffline;
478
479    if (selector < (uint32_t) kIOHIDLibUserClientNumCommands)
480    {
481        dispatch = (IOExternalMethodDispatch *) &sMethods[selector];
482
483        if (!target)
484            target = this;
485    }
486
487    return super::externalMethod(selector, arguments, dispatch, target, reference);
488}
489
490IOReturn IOHIDLibUserClient::_setQueueAsyncPort(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
491{
492    return target->setQueueAsyncPort(target->getQueueForToken(arguments->scalarInput[0]), arguments->asyncWakePort);
493}
494
495IOReturn IOHIDLibUserClient::setQueueAsyncPort(IOHIDEventQueue * queue, mach_port_t port)
496{
497    if ( !queue )
498        return kIOReturnBadArgument;
499
500    queue->setNotificationPort(port);
501
502    return kIOReturnSuccess;
503}
504
505IOReturn IOHIDLibUserClient::_open(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
506{
507    return target->open((IOOptionBits)arguments->scalarInput[0]);
508}
509
510IOReturn IOHIDLibUserClient::open(IOOptionBits options)
511{
512    IOReturn ret = kIOReturnNotPrivileged;
513
514    do {
515        ret = clientHasPrivilege(fClient, kIOClientPrivilegeAdministrator);
516        if ( ret == kIOReturnSuccess )
517            break;
518
519        // RY: If this is a keyboard and the client is attempting to seize,
520        // the client needs to be admin
521        if ( !fNubIsKeyboard || ((options & kIOHIDOptionsTypeSeizeDevice) == 0) ) {
522#if TARGET_OS_EMBEDDED
523            ret = kIOReturnSuccess;
524#else
525            ret = clientHasPrivilege(fClient, kIOClientPrivilegeLocalUser);
526#endif
527        }
528    } while (false);
529
530    if (ret != kIOReturnSuccess)
531        return ret;
532
533    if (!fNub)
534        return kIOReturnOffline;
535    if (!fNub->open(this, options))
536        return kIOReturnExclusiveAccess;
537
538    fCachedOptionBits = options;
539
540    fCachedConsoleUsersSeed = 0;
541    resourceNotificationGated();
542
543    return kIOReturnSuccess;
544}
545
546
547IOReturn IOHIDLibUserClient::_close(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments __unused)
548{
549    return target->close();
550}
551
552IOReturn IOHIDLibUserClient::close()
553{
554    if (fNub)
555    fNub->close(this, fCachedOptionBits);
556
557    setValid(false);
558
559    fCachedOptionBits = 0;
560
561    // @@@ gvdl: release fWakePort leak them for the time being
562
563    return kIOReturnSuccess;
564}
565
566bool
567IOHIDLibUserClient::didTerminate( IOService * provider, IOOptionBits options, bool * defer )
568{
569    if (fGate) {
570        fGate->runAction(OSMemberFunctionCast(IOCommandGate::Action, this, &IOHIDLibUserClient::cleanupGated));
571    }
572
573    return super::didTerminate(provider, options, defer);
574}
575
576void IOHIDLibUserClient::free()
577{
578    OSSafeReleaseNULL(fQueueMap);
579    OSSafeReleaseNULL(fNub);
580
581    if (fResourceES) {
582        if (fWL)
583            fWL->removeEventSource(fResourceES);
584        fResourceES->release();
585        fResourceES = 0;
586    }
587
588    if (fGate) {
589        if (fWL)
590            fWL->removeEventSource(fGate);
591
592        fGate->release();
593        fGate = 0;
594    }
595
596    OSSafeReleaseNULL(fWL);
597
598    if ( fValidMessage ) {
599        IOFree(fValidMessage, sizeof (struct _notifyMsg));
600        fValidMessage = NULL;
601    }
602
603    if (fWakePort != MACH_PORT_NULL) {
604        ipc_port_release_send(fWakePort);
605        fWakePort = MACH_PORT_NULL;
606    }
607
608    if (fValidPort != MACH_PORT_NULL) {
609        ipc_port_release_send(fValidPort);
610        fValidPort = MACH_PORT_NULL;
611    }
612    super::free();
613}
614
615IOReturn IOHIDLibUserClient::message(UInt32 type, IOService * provider, void * argument )
616{
617    if ( !isInactive() && fGate ) {
618        fGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
619                                              this,
620                                              &IOHIDLibUserClient::messageGated),
621                         (void*)(intptr_t)type,
622                         provider,
623                         argument);
624    }
625    return super::message(type, provider, argument);
626}
627
628IOReturn IOHIDLibUserClient::messageGated(UInt32 type, IOService * provider __unused, void * argument )
629{
630    IOOptionBits options = (uintptr_t)argument;
631    switch ( type ) {
632        case kIOMessageServiceIsRequestingClose:
633            if ((options & kIOHIDOptionsTypeSeizeDevice) && (options != fCachedOptionBits))
634                 setValid(false);
635            break;
636
637        case kIOMessageServiceWasClosed:
638            if ((options & kIOHIDOptionsTypeSeizeDevice) && (options != fCachedOptionBits)) {
639                // instead of calling set valid, let's make sure we still have
640                // permission through the resource notification
641                fCachedConsoleUsersSeed = 0;
642                resourceNotificationGated();
643            }
644            break;
645    };
646
647    return kIOReturnSuccess;
648}
649
650IOReturn IOHIDLibUserClient::registerNotificationPort(mach_port_t port, UInt32 type, UInt32 refCon)
651{
652    if (fGate) {
653        return fGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
654                                                     this,
655                                                     &IOHIDLibUserClient::registerNotificationPortGated),
656                                (void *)port,
657                                (void*)(intptr_t)type,
658                                (void*)(intptr_t)refCon);
659    }
660    else {
661        return kIOReturnOffline;
662    }
663}
664
665IOReturn IOHIDLibUserClient::registerNotificationPortGated(mach_port_t port, UInt32 type, UInt32 refCon __unused)
666{
667    IOReturn kr = kIOReturnSuccess;
668
669    switch ( type ) {
670        case kIOHIDLibUserClientAsyncPortType:
671            if (fWakePort != MACH_PORT_NULL) {
672                ipc_port_release_send(fWakePort);
673                fWakePort = MACH_PORT_NULL;
674            }
675            fWakePort = port;
676            break;
677        case kIOHIDLibUserClientDeviceValidPortType:
678            if (fValidPort != MACH_PORT_NULL) {
679                ipc_port_release_send(fValidPort);
680                fValidPort = MACH_PORT_NULL;
681            }
682            fValidPort = port;
683
684            static struct _notifyMsg init_msg = { {
685                MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0),
686                sizeof (struct _notifyMsg),
687                MACH_PORT_NULL,
688                MACH_PORT_NULL,
689                0,
690                0
691            } };
692
693            if ( fValidMessage ) {
694                IOFree(fValidMessage, sizeof (struct _notifyMsg));
695                fValidMessage = NULL;
696            }
697
698            if ( !fValidPort )
699                break;
700
701            if ( !(fValidMessage = IOMalloc( sizeof(struct _notifyMsg))) ) {
702                kr = kIOReturnNoMemory;
703                break;
704            }
705
706            // Initialize the events available message.
707            *((struct _notifyMsg *)fValidMessage) = init_msg;
708
709            ((struct _notifyMsg *)fValidMessage)->h.msgh_remote_port = fValidPort;
710
711            dispatchMessage(fValidMessage);
712
713            break;
714        default:
715            kr = kIOReturnUnsupported;
716            break;
717    };
718
719    return kr;
720}
721
722void IOHIDLibUserClient::setValid(bool state)
723{
724    if (fValid == state)
725        return;
726
727    if ( !state ) {
728        // unmap this memory
729        if (fNub && !isInactive()) {
730            IOMemoryDescriptor * mem;
731            IOMemoryMap * map;
732
733            mem = fNub->getMemoryWithCurrentElementValues();
734
735            if ( mem ) {
736                map = removeMappingForDescriptor(mem);
737
738                if ( map )
739                    map->release();
740            }
741        }
742        fGeneration++;
743    }
744
745    // set the queue states
746    setStateForQueues(state ? kHIDQueueStateEnable : kHIDQueueStateDisable);
747
748    // dispatch message
749    dispatchMessage(fValidMessage);
750
751    fValid = state;
752}
753
754IOReturn IOHIDLibUserClient::dispatchMessage(void * messageIn)
755{
756    IOReturn ret = kIOReturnError;
757    mach_msg_header_t * msgh = (mach_msg_header_t *)messageIn;
758    if( msgh) {
759        ret = mach_msg_send_from_kernel( msgh, msgh->msgh_size);
760        switch ( ret ) {
761            case MACH_SEND_TIMED_OUT:/* Already has a message posted */
762            case MACH_MSG_SUCCESS:    /* Message is posted */
763                break;
764        };
765    }
766    return ret;
767}
768
769void IOHIDLibUserClient::setStateForQueues(UInt32 state, IOOptionBits options __unused)
770{
771    for (u_int token = getNextTokenForToken(0); token != 0; token = getNextTokenForToken(token))
772    {
773        // this cannot return a NULL queue because of the above code
774        IOHIDEventQueue *queue = getQueueForToken(token);
775        switch (state) {
776            case kHIDQueueStateEnable:
777                queue->enable();
778                break;
779            case kHIDQueueStateDisable:
780                queue->disable();
781                break;
782            case kHIDQueueStateClear:
783                fNub->stopEventDelivery(queue);
784                break;
785        }
786    }
787}
788
789IOReturn IOHIDLibUserClient::clientMemoryForType (
790                                    UInt32                    type,
791                                    IOOptionBits *            options,
792                                    IOMemoryDescriptor **    memory )
793{
794    if (fGate) {
795        return fGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
796                                                     this,
797                                                     &IOHIDLibUserClient::clientMemoryForTypeGated),
798                                (void*)(intptr_t)type,
799                                (void *)options,
800                                (void *)memory);
801    }
802    else {
803        return kIOReturnOffline;
804    }
805}
806
807IOReturn IOHIDLibUserClient::clientMemoryForTypeGated(
808                                    UInt32                    token,
809                                    IOOptionBits *            options,
810                                    IOMemoryDescriptor **    memory )
811{
812    IOReturn                ret                = kIOReturnNoMemory;
813    IOMemoryDescriptor        *memoryToShare    = NULL;
814    IOHIDEventQueue            *queue            = NULL;
815
816    // if the type is element values, then get that
817    if (token == kIOHIDLibUserClientElementValuesType)
818    {
819        // if we can get an element values ptr
820        if (fValid && fNub && !isInactive())
821            memoryToShare = fNub->getMemoryWithCurrentElementValues();
822    }
823    // otherwise, the type is token
824    else if (NULL != (queue = getQueueForToken(token)))
825    {
826        memoryToShare = queue->getMemoryDescriptor();
827    }
828    // if we got some memory
829    if (memoryToShare)
830    {
831        // Memory will be released by user client
832        // when last map is destroyed.
833
834        memoryToShare->retain();
835
836        ret = kIOReturnSuccess;
837    }
838
839    // set the result
840    *options    = 0;
841    *memory        = memoryToShare;
842
843    return ret;
844}
845
846
847IOReturn IOHIDLibUserClient::_getElementCount(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
848{
849    return target->getElementCount(&(arguments->scalarOutput[0]), &(arguments->scalarOutput[1]));
850}
851
852IOReturn IOHIDLibUserClient::getElementCount(uint64_t * pOutElementCount, uint64_t * pOutReportElementCount)
853{
854    uint32_t outElementCount, outReportElementCount;
855
856    if (!pOutElementCount || !pOutReportElementCount)
857        return kIOReturnBadArgument;
858
859    getElements(kHIDElementType, (void *)NULL, &outElementCount);
860    getElements(kHIDReportHandlerType, (void*)NULL, &outReportElementCount);
861
862    *pOutElementCount        = outElementCount / sizeof(IOHIDElementStruct);
863    *pOutReportElementCount    = outReportElementCount / sizeof(IOHIDElementStruct);
864
865    return kIOReturnSuccess;
866}
867
868IOReturn IOHIDLibUserClient::_getElements(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
869{
870    if ( arguments->structureOutputDescriptor )
871        return target->getElements((uint32_t)arguments->scalarInput[0], arguments->structureOutputDescriptor, &(arguments->structureOutputDescriptorSize));
872    else
873        return target->getElements((uint32_t)arguments->scalarInput[0], arguments->structureOutput, &(arguments->structureOutputSize));
874}
875
876IOReturn IOHIDLibUserClient::getElements (uint32_t elementType, void *elementBuffer, uint32_t *elementBufferSize)
877{
878    OSArray *                array;
879    uint32_t                i, bi, count;
880    IOHIDElementPrivate *    element;
881    IOHIDElementStruct *    elementStruct;
882
883    if (elementBuffer && elementBufferSize && !*elementBufferSize)
884        return kIOReturnBadArgument;
885
886    if (!fNub || isInactive())
887        return kIOReturnNotAttached;
888
889    if ( elementType == kHIDElementType )
890        array = fNub->_reserved->hierarchElements;
891    else
892        array = fNub->_reserved->inputInterruptElementArray;
893
894    if ( !array )
895        return kIOReturnError;
896
897    if ( elementBuffer )
898        bzero(elementBuffer, *elementBufferSize);
899
900    count = array->getCount();
901    bi = 0;
902
903    for ( i=0; i<count; i++ )
904    {
905        element = OSDynamicCast(IOHIDElementPrivate, array->getObject(i));
906
907        if (!element) continue;
908
909        // Passing elementBuffer=0 means we are just attempting to get the count;
910        elementStruct = elementBuffer ? &(((IOHIDElementStruct *)elementBuffer)[bi]) : 0;
911
912        if ( element->fillElementStruct(elementStruct) )
913            bi++;
914    }
915
916    if (elementBufferSize)
917        *elementBufferSize = bi * sizeof(IOHIDElementStruct);
918
919    return kIOReturnSuccess;
920}
921
922IOReturn IOHIDLibUserClient::getElements(uint32_t elementType, IOMemoryDescriptor * mem, uint32_t *elementBufferSize)
923{
924    IOReturn ret = kIOReturnNoMemory;
925
926    if (!fNub || isInactive())
927        return kIOReturnNotAttached;
928
929    ret = mem->prepare();
930
931    if(ret == kIOReturnSuccess)
932    {
933        void *        elementData;
934        uint32_t    elementLength;
935
936        elementLength = mem->getLength();
937        if ( elementLength )
938        {
939            elementData = IOMalloc( elementLength );
940
941            if ( elementData )
942            {
943                bzero(elementData, elementLength);
944
945                ret = getElements(elementType, elementData, &elementLength);
946
947                if ( elementBufferSize )
948                    *elementBufferSize = elementLength;
949
950                mem->writeBytes( 0, elementData, elementLength );
951
952                IOFree( elementData, elementLength );
953            }
954            else
955                ret = kIOReturnNoMemory;
956        }
957        else
958            ret = kIOReturnBadArgument;
959
960        mem->complete();
961    }
962
963    return ret;
964}
965
966IOReturn IOHIDLibUserClient::_deviceIsValid(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
967{
968    IOReturn    kr;
969    bool        status;
970    uint64_t    generation;
971
972    kr = target->deviceIsValid(&status, &generation);
973
974    arguments->scalarOutput[0] = status;
975    arguments->scalarOutput[1] = generation;
976
977    return kr;
978}
979
980IOReturn IOHIDLibUserClient::deviceIsValid(bool *status, uint64_t *generation)
981{
982    if ( status )
983        *status = fValid;
984
985    if ( generation )
986        *generation = fGeneration;
987
988    return kIOReturnSuccess;
989}
990
991IOReturn IOHIDLibUserClient::_createQueue(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
992{
993    return target->createQueue((uint32_t)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1], &(arguments->scalarOutput[0]));
994}
995
996IOReturn IOHIDLibUserClient::createQueue(uint32_t flags, uint32_t depth, uint64_t * outQueue)
997{
998    // create the queue (fudge it a bit bigger than requested)
999    IOHIDEventQueue * eventQueue = IOHIDEventQueue::withEntries (depth+1, DEFAULT_HID_ENTRY_SIZE);
1000
1001    if ( !eventQueue )
1002        return kIOReturnNoMemory;
1003
1004    eventQueue->setOptions(flags);
1005
1006    if ( !fValid )
1007        eventQueue->disable();
1008
1009    // add the queue to the map and set out queue
1010    *outQueue = (uint64_t)createTokenForQueue(eventQueue);
1011
1012    eventQueue->release();
1013
1014    return kIOReturnSuccess;
1015}
1016
1017
1018IOReturn IOHIDLibUserClient::_disposeQueue(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1019{
1020    return target->disposeQueue(target->getQueueForToken(arguments->scalarInput[0]));
1021}
1022
1023IOReturn IOHIDLibUserClient::disposeQueue(IOHIDEventQueue * queue)
1024{
1025    IOReturn ret = kIOReturnSuccess;
1026
1027    // remove this queue from all elements that use it
1028    if (fNub && !isInactive())
1029        ret = fNub->stopEventDelivery (queue);
1030
1031    // remove the queue from the map
1032    removeQueueFromMap(queue);
1033
1034    // This should really return an actual result
1035    return kIOReturnSuccess;
1036}
1037
1038    // Add an element to a queue
1039IOReturn IOHIDLibUserClient::_addElementToQueue(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1040{
1041    return target->addElementToQueue(target->getQueueForToken(arguments->scalarInput[0]), (IOHIDElementCookie)arguments->scalarInput[1], (uint32_t)arguments->scalarInput[2], &(arguments->scalarOutput[0]));
1042}
1043
1044IOReturn IOHIDLibUserClient::addElementToQueue(IOHIDEventQueue * queue, IOHIDElementCookie elementCookie, uint32_t flags __unused, uint64_t *pSizeChange)
1045{
1046    IOReturn    ret        = kIOReturnSuccess;
1047    UInt32        size    = 0;
1048
1049    size = (queue) ? queue->getEntrySize() : 0;
1050
1051    // add the queue to the element's queues
1052    if (fNub && !isInactive())
1053        ret = fNub->startEventDelivery (queue, elementCookie);
1054
1055    *pSizeChange = (queue && (size != queue->getEntrySize()));
1056
1057    return ret;
1058}
1059    // remove an element from a queue
1060IOReturn IOHIDLibUserClient::_removeElementFromQueue (IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1061{
1062    return target->removeElementFromQueue(target->getQueueForToken(arguments->scalarInput[0]), (IOHIDElementCookie)arguments->scalarInput[1], &(arguments->scalarOutput[0]));
1063}
1064
1065IOReturn IOHIDLibUserClient::removeElementFromQueue (IOHIDEventQueue * queue, IOHIDElementCookie elementCookie, uint64_t *pSizeChange)
1066{
1067    IOReturn    ret        = kIOReturnSuccess;
1068    UInt32        size    = 0;
1069
1070    size = (queue) ? queue->getEntrySize() : 0;
1071
1072    // remove the queue from the element's queues
1073    if (fNub && !isInactive())
1074        ret = fNub->stopEventDelivery (queue, elementCookie);
1075
1076    *pSizeChange = (queue && (size != queue->getEntrySize()));
1077
1078    return ret;
1079}
1080    // Check to see if a queue has an element
1081IOReturn IOHIDLibUserClient::_queueHasElement (IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1082{
1083    return target->queueHasElement(target->getQueueForToken(arguments->scalarInput[0]), (IOHIDElementCookie)arguments->scalarInput[1], &(arguments->scalarOutput[0]));
1084}
1085
1086IOReturn IOHIDLibUserClient::queueHasElement (IOHIDEventQueue * queue, IOHIDElementCookie elementCookie, uint64_t * pHasElement)
1087{
1088    IOReturn ret = kIOReturnSuccess;
1089
1090    // check to see if that element is feeding that queue
1091    bool hasElement = false;
1092
1093    if (fNub && !isInactive())
1094        ret = fNub->checkEventDelivery (queue, elementCookie, &hasElement);
1095
1096    // set return
1097    *pHasElement = hasElement;
1098
1099    return ret;
1100}
1101    // start a queue
1102IOReturn IOHIDLibUserClient::_startQueue (IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1103{
1104    return target->startQueue(target->getQueueForToken(arguments->scalarInput[0]));
1105}
1106
1107IOReturn IOHIDLibUserClient::startQueue (IOHIDEventQueue * queue)
1108{
1109    // start the queue
1110    if (queue) {
1111    queue->start();
1112    return kIOReturnSuccess;
1113}
1114    return kIOReturnBadArgument;
1115}
1116
1117    // stop a queue
1118IOReturn IOHIDLibUserClient::_stopQueue (IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1119{
1120    return target->stopQueue(target->getQueueForToken(arguments->scalarInput[0]));
1121}
1122
1123IOReturn IOHIDLibUserClient::stopQueue (IOHIDEventQueue * queue)
1124{
1125    // stop the queue
1126    if (queue) {
1127        queue->stop();
1128        return kIOReturnSuccess;
1129    }
1130    return kIOReturnBadArgument;
1131}
1132
1133    // update the feature element value
1134IOReturn IOHIDLibUserClient::_updateElementValues (IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1135{
1136    return target->updateElementValues(arguments->scalarInput, arguments->scalarInputCount);
1137}
1138
1139IOReturn IOHIDLibUserClient::updateElementValues (const uint64_t * lCookies, uint32_t cookieCount)
1140{
1141    IOReturn    ret = kIOReturnError;
1142
1143    if (fNub && !isInactive()) {
1144        uint32_t    cookies[cookieCount];
1145
1146        deflate_vec(cookies, cookieCount, lCookies, cookieCount);
1147
1148        ret = fNub->updateElementValues((IOHIDElementCookie *)cookies, cookieCount);
1149    }
1150
1151    return ret;
1152}
1153
1154    // Set the element values
1155IOReturn IOHIDLibUserClient::_postElementValues (IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1156{
1157    return target->postElementValues(arguments->scalarInput, arguments->scalarInputCount);
1158}
1159
1160IOReturn IOHIDLibUserClient::postElementValues (const uint64_t * lCookies, uint32_t cookieCount)
1161{
1162    IOReturn    ret = kIOReturnError;
1163
1164    if (fNub && !isInactive()) {
1165        uint32_t    cookies[cookieCount];
1166
1167        deflate_vec(cookies, cookieCount, lCookies, cookieCount);
1168
1169        ret = fNub->postElementValues((IOHIDElementCookie *)cookies, cookieCount);
1170    }
1171
1172    return ret;
1173}
1174
1175IOReturn IOHIDLibUserClient::_getReport(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1176{
1177    if ( arguments->asyncWakePort ) {
1178        IOReturn        ret;
1179        IOHIDCompletion tap;
1180        AsyncParam *    pb = (AsyncParam *)IOMalloc(sizeof(AsyncParam));
1181
1182        if(!pb)
1183            return kIOReturnNoMemory;
1184        target->retain();
1185
1186        bcopy(arguments->asyncReference, pb->fAsyncRef, sizeof(OSAsyncReference64));
1187        pb->fAsyncCount = arguments->asyncReferenceCount;
1188        tap.target = target;
1189        tap.action = OSMemberFunctionCast(IOHIDCompletionAction, target, &IOHIDLibUserClient::ReqComplete);
1190        tap.parameter = pb;
1191
1192        if ( arguments->structureOutputDescriptor )
1193            ret = target->getReport(arguments->structureOutputDescriptor, &(arguments->structureOutputDescriptorSize), (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1], (uint32_t)arguments->scalarInput[2], &tap);
1194        else
1195            ret = target->getReport(arguments->structureOutput, &(arguments->structureOutputSize), (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1], (uint32_t)arguments->scalarInput[2], &tap);
1196
1197        if ( ret ) {
1198            if ( pb )
1199                IOFree(pb, sizeof(*pb));
1200            target->release();
1201        }
1202    }
1203    if ( arguments->structureOutputDescriptor )
1204        return target->getReport(arguments->structureOutputDescriptor, &(arguments->structureOutputDescriptorSize), (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1]);
1205    else
1206        return target->getReport(arguments->structureOutput, &(arguments->structureOutputSize), (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1]);
1207}
1208
1209IOReturn IOHIDLibUserClient::getReport(void *reportBuffer, uint32_t *pOutsize, IOHIDReportType reportType, uint32_t reportID, uint32_t timeout, IOHIDCompletion * completion)
1210{
1211    IOReturn                ret = kIOReturnBadArgument;
1212    IOMemoryDescriptor *    mem;
1213
1214    // VTN3: Is there a real maximum report size? It looks like the current limit is around
1215    // 1024 bytes, but that will (or has) changed. 65536 is above every upper limit
1216    // I have seen by a few factors.
1217    if (*pOutsize > 0x10000) {
1218        IOLog("IOHIDLibUserClient::getReport called with an irrationally large output size: %lu\n", (long unsigned) *pOutsize);
1219    }
1220    else {
1221        mem = IOBufferMemoryDescriptor::withCapacity(*pOutsize, kIODirectionInOut);
1222        if(mem) {
1223            mem->prepare(kIODirectionInOut);
1224            ret = getReport(mem, pOutsize, reportType, reportID, timeout, completion);
1225            mem->readBytes(0, reportBuffer, *pOutsize);
1226            mem->complete();
1227            mem->release();
1228        }
1229        else {
1230            ret =  kIOReturnNoMemory;
1231        }
1232    }
1233    return ret;
1234}
1235
1236IOReturn IOHIDLibUserClient::getReport(IOMemoryDescriptor * mem, uint32_t * pOutsize, IOHIDReportType reportType, uint32_t reportID, uint32_t timeout, IOHIDCompletion * completion)
1237{
1238    IOReturn ret = kIOReturnBadArgument;
1239
1240    // VTN3: Is there a real maximum report size? It looks like the current limit is around
1241    // 1024 bytes, but that will (or has) changed. 65536 is above every upper limit
1242    // I have seen by a few factors.
1243    if (*pOutsize > 0x10000) {
1244        IOLog("IOHIDLibUserClient::getReport called with an irrationally large output size: %lu\n", (long unsigned) *pOutsize);
1245    }
1246    else if (fNub && !isInactive()) {
1247        ret = mem->prepare();
1248        if(ret == kIOReturnSuccess) {
1249            if (completion) {
1250                AsyncParam * pb = (AsyncParam *)completion->parameter;
1251                pb->fMax        = *pOutsize;
1252                pb->fMem        = mem;
1253                pb->reportType  = reportType;
1254
1255                mem->retain();
1256
1257                ret = fNub->getReport(mem, reportType, reportID, timeout, completion);
1258            }
1259            else {
1260                ret = fNub->getReport(mem, reportType, reportID);
1261
1262                // make sure the element values are updated.
1263                if (ret == kIOReturnSuccess)
1264                    fNub->handleReport(mem, reportType, kIOHIDReportOptionNotInterrupt);
1265
1266                *pOutsize = mem->getLength();
1267                mem->complete();
1268            }
1269        }
1270    }
1271    else {
1272        ret = kIOReturnNotAttached;
1273    }
1274
1275    return ret;
1276
1277}
1278
1279IOReturn IOHIDLibUserClient::_setReport(IOHIDLibUserClient * target, void * reference __unused, IOExternalMethodArguments * arguments)
1280{
1281    IOReturn ret = kIOReturnError;
1282
1283    if ( arguments->asyncWakePort ) {
1284        IOHIDCompletion tap;
1285        AsyncParam *    pb = (AsyncParam *)IOMalloc(sizeof(AsyncParam));
1286
1287        if(!pb)
1288            return kIOReturnNoMemory;
1289
1290        target->retain();
1291
1292        bcopy(arguments->asyncReference, pb->fAsyncRef, sizeof(OSAsyncReference64));
1293        pb->fAsyncCount = arguments->asyncReferenceCount;
1294        tap.target = target;
1295        tap.action = OSMemberFunctionCast(IOHIDCompletionAction, target, &IOHIDLibUserClient::ReqComplete);
1296        tap.parameter = pb;
1297
1298        if ( arguments->structureInputDescriptor )
1299            ret = target->setReport( arguments->structureInputDescriptor, (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1],(uint32_t)arguments->scalarInput[2], &tap);
1300        else
1301            ret = target->setReport(arguments->structureInput, arguments->structureInputSize, (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1], (uint32_t)arguments->scalarInput[2], &tap);
1302
1303        if ( ret ) {
1304            if ( pb )
1305                IOFree(pb, sizeof(*pb));
1306
1307            target->release();
1308        }
1309    }
1310    else
1311        if ( arguments->structureInputDescriptor )
1312            ret = target->setReport( arguments->structureInputDescriptor, (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1]);
1313        else
1314            ret = target->setReport(arguments->structureInput, arguments->structureInputSize, (IOHIDReportType)arguments->scalarInput[0], (uint32_t)arguments->scalarInput[1]);
1315
1316    return ret;
1317}
1318
1319IOReturn IOHIDLibUserClient::setReport(const void *reportBuffer, uint32_t reportBufferSize, IOHIDReportType reportType, uint32_t reportID, uint32_t timeout, IOHIDCompletion * completion)
1320{
1321    IOReturn                ret;
1322    IOMemoryDescriptor *    mem;
1323
1324    mem = IOMemoryDescriptor::withAddress((void *)reportBuffer, reportBufferSize, kIODirectionOut);
1325    if(mem) {
1326        ret = setReport(mem, reportType, reportID, timeout, completion);
1327        mem->release();
1328    }
1329    else
1330        ret = kIOReturnNoMemory;
1331
1332    return ret;
1333}
1334
1335IOReturn IOHIDLibUserClient::setReport(IOMemoryDescriptor * mem, IOHIDReportType reportType, uint32_t reportID, uint32_t timeout, IOHIDCompletion * completion)
1336{
1337    IOReturn            ret;
1338
1339    if (fNub && !isInactive()) {
1340        ret = mem->prepare();
1341        if(ret == kIOReturnSuccess) {
1342            OSArray *extended = (OSArray*)copyProperty(kIOHIDLibClientExtendedData);
1343            if (OSDynamicCast(OSArray, extended) && extended->getCount()) {
1344                OSCollectionIterator *itr = OSCollectionIterator::withCollection(extended);
1345                if (itr) {
1346                    bool done = false;
1347                    while (!done) {
1348                        OSObject *obj;
1349                        while (!done && (NULL != (obj = itr->getNextObject()))) {
1350                            OSNumber *num = OSDynamicCast(OSNumber, obj);
1351                            if (num) {
1352                                uint8_t excludedReportID =  (num->unsigned64BitValue() >> 16) & 0xff;
1353                                uint8_t excludedReportType =(num->unsigned64BitValue() >> 24) & 0xff;
1354
1355                                if ((excludedReportID == reportID) && (excludedReportType == (reportType + 1))) {
1356                                     // Block
1357                                    IOLog("IOHIDLibUserClient::setReport %02x/%02x blocked due to lack of privileges\n", reportID, reportType);
1358                                    done = true;
1359                                    ret = kIOReturnNotPrivileged;
1360                                }
1361                            }
1362                        }
1363                        if (itr->isValid()) {
1364                            // Do not block
1365                            done = true;
1366                        }
1367                        else {
1368                            // Someone changed the array. Check again.
1369                            itr->reset();
1370                        }
1371                    }
1372                    itr->release();
1373                }
1374            }
1375            OSSafeReleaseNULL(extended);
1376            if (ret == kIOReturnSuccess) {
1377                if ( completion ) {
1378                    AsyncParam * pb = (AsyncParam *)completion->parameter;
1379                    pb->fMax        = mem->getLength();
1380                    pb->fMem        = mem;
1381                    pb->reportType    = reportType;
1382
1383                    mem->retain();
1384
1385                    ret = fNub->setReport(mem, reportType, reportID, timeout, completion);
1386                }
1387                else {
1388                    ret = fNub->setReport(mem, reportType, reportID);
1389
1390                    // make sure the element values are updated.
1391                    if (ret == kIOReturnSuccess)
1392                        fNub->handleReport(mem, reportType, kIOHIDReportOptionNotInterrupt);
1393
1394                    mem->complete();
1395                }
1396            }
1397        }
1398    }
1399    else
1400        ret = kIOReturnNotAttached;
1401
1402    return ret;
1403}
1404
1405void IOHIDLibUserClient::ReqComplete(void *param, IOReturn res, UInt32 remaining)
1406{
1407    fGate->runAction(OSMemberFunctionCast(IOCommandGate::Action,
1408                                          this,
1409                                          &IOHIDLibUserClient::ReqCompleteGated),
1410                     param,
1411                     (void*)(intptr_t)res,
1412                     (void*)(intptr_t)remaining);
1413}
1414
1415IOReturn IOHIDLibUserClient::ReqCompleteGated(void *param, IOReturn res, UInt32 remaining)
1416{
1417    io_user_reference_t args[1];
1418    AsyncParam * pb = (AsyncParam *)param;
1419
1420    if(res == kIOReturnSuccess) {
1421        args[0] = (io_user_reference_t)(pb->fMax - remaining);
1422
1423        // make sure the element values are updated.
1424        if (fNub && !isInactive())
1425            fNub->handleReport(pb->fMem, pb->reportType, kIOHIDReportOptionNotInterrupt);
1426    } else {
1427        args[0] = 0;
1428    }
1429
1430    if (pb->fMem) {
1431        pb->fMem->complete();
1432        pb->fMem->release();
1433    }
1434
1435    sendAsyncResult64(pb->fAsyncRef, res, args, 1);
1436
1437    IOFree(pb, sizeof(*pb));
1438
1439    release();
1440
1441    return kIOReturnSuccess;
1442}
1443
1444
1445// This section is to track all user queues and hand out unique tokens for
1446// particular queues. vtn3
1447// rdar://5957582 start
1448
1449enum { kIOHIDLibUserClientQueueTokenOffset = 200 };
1450
1451
1452u_int IOHIDLibUserClient::createTokenForQueue(IOHIDEventQueue *queue)
1453{
1454    u_int index = 0;
1455    u_int result = 0;
1456
1457    while (OSDynamicCast(IOHIDEventQueue, fQueueMap->getObject(index)))
1458        index++;
1459
1460    if (index < (UINT_MAX - kIOHIDLibUserClientQueueTokenOffset)) {
1461    	fQueueMap->setObject(index, queue);
1462    	result = index + kIOHIDLibUserClientQueueTokenOffset;
1463    }
1464    else {
1465		IOLog("IOHIDLibUserClient::createTokenForQueue generated out-of-range index: %d\n", index);
1466    }
1467
1468    return (result);
1469}
1470
1471
1472void IOHIDLibUserClient::removeQueueFromMap(IOHIDEventQueue *queue)
1473{
1474    OSObject *obj = NULL;
1475
1476    for (u_int index = 0; NULL != (obj = fQueueMap->getObject(index)); index++)
1477        if (obj == queue) {
1478            fQueueMap->replaceObject(index, kOSBooleanFalse);
1479        }
1480}
1481
1482
1483IOHIDEventQueue* IOHIDLibUserClient::getQueueForToken(u_int token)
1484{
1485	IOHIDEventQueue	*result = NULL;
1486
1487	if (token >= kIOHIDLibUserClientQueueTokenOffset) {
1488		result = OSDynamicCast(IOHIDEventQueue, fQueueMap->getObject(token - kIOHIDLibUserClientQueueTokenOffset));
1489	}
1490	else {
1491		IOLog("IOHIDLibUserClient::getQueueForToken received out-of-range token: %d\n", token);
1492	}
1493
1494	return (result);
1495}
1496
1497
1498u_int IOHIDLibUserClient::getNextTokenForToken(u_int token)
1499{
1500    u_int next_token = (token < kIOHIDLibUserClientQueueTokenOffset) ?
1501                                kIOHIDLibUserClientQueueTokenOffset - 1 : token;
1502
1503    IOHIDEventQueue *queue = NULL;
1504
1505    do {
1506        queue = getQueueForToken(++next_token);
1507    }
1508    while ((next_token < fQueueMap->getCount() + kIOHIDLibUserClientQueueTokenOffset) && (queue == NULL));
1509
1510    if (next_token >= fQueueMap->getCount() + kIOHIDLibUserClientQueueTokenOffset)
1511        next_token = 0;
1512
1513    return next_token;
1514}
1515
1516// rdar://5957582 end
1517
1518bool
1519IOHIDLibUserClient::attach(IOService * provider)
1520{
1521    if (!super::attach(provider)) {
1522        return false;
1523    }
1524    if (provider && getProperty(kIOHIDLibClientExtendedData)) {
1525        // Check for extended data
1526        OSArray *extended = (OSArray*)provider->copyProperty(kIOHIDLibClientExtendedData, gIOServicePlane);
1527        if (OSDynamicCast(OSArray, extended) && extended->getCount()) {
1528            // Extended data found. Replace the temporary key.
1529            setProperty(kIOHIDLibClientExtendedData, extended);
1530        }
1531        else {
1532            // No extended data found. Remove the temporary key.
1533            removeProperty(kIOHIDLibClientExtendedData);
1534        }
1535        OSSafeReleaseNULL(extended);
1536    }
1537    return true;
1538}
1539