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