1/*
2 * Copyright (c) 1999-2008 Apple Computer, Inc.  All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <pthread.h>
25#include <CoreFoundation/CFRuntime.h>
26#include <IOKit/hid/IOHIDDevicePlugIn.h>
27#include <asl.h>
28#include "IOHIDLibPrivate.h"
29#include "IOHIDDevice.h"
30#include "IOHIDQueue.h"
31
32static IOHIDQueueRef    __IOHIDQueueCreate(
33                                    CFAllocatorRef          allocator,
34                                    CFAllocatorContext *    context __unused);
35static void             __IOHIDQueueRelease( CFTypeRef object );
36static void             __IOHIDQueueValueAvailableCallback(
37                                void *                          context,
38                                IOReturn                        result,
39                                void *                          sender);
40
41
42typedef struct __IOHIDQueue
43{
44    CFRuntimeBase                   cfBase;   // base CFType information
45
46    IOHIDDeviceQueueInterface**     queueInterface;
47
48    CFTypeRef                       asyncEventSource;
49    CFRunLoopRef                    asyncRunLoop;
50    CFStringRef                     asyncRunLoopMode;
51
52    IOHIDDeviceRef                  device;
53    CFMutableDictionaryRef          callbackDictionary;
54
55    CFMutableSetRef                 elements;
56} __IOHIDQueue, *__IOHIDQueueRef;
57
58static const CFRuntimeClass __IOHIDQueueClass = {
59    0,                      // version
60    "IOHIDQueue",           // className
61    NULL,                   // init
62    NULL,                   // copy
63    __IOHIDQueueRelease,    // finalize
64    NULL,                   // equal
65    NULL,                   // hash
66    NULL,                   // copyFormattingDesc
67    NULL,
68    NULL,
69    NULL
70};
71
72static pthread_once_t __queueTypeInit = PTHREAD_ONCE_INIT;
73static CFTypeID __kIOHIDQueueTypeID = _kCFRuntimeNotATypeID;
74
75//------------------------------------------------------------------------------
76// __IOHIDQueueRegister
77//------------------------------------------------------------------------------
78void __IOHIDQueueRegister(void)
79{
80    __kIOHIDQueueTypeID = _CFRuntimeRegisterClass(&__IOHIDQueueClass);
81}
82
83//------------------------------------------------------------------------------
84// __IOHIDQueueCreate
85//------------------------------------------------------------------------------
86IOHIDQueueRef __IOHIDQueueCreate(
87                                CFAllocatorRef              allocator,
88                                CFAllocatorContext *        context __unused)
89{
90    IOHIDQueueRef   queue   = NULL;
91    void *          offset  = NULL;
92    uint32_t        size;
93
94    /* allocate service */
95    size  = sizeof(__IOHIDQueue) - sizeof(CFRuntimeBase);
96    queue = (IOHIDQueueRef)_CFRuntimeCreateInstance(allocator, IOHIDQueueGetTypeID(), size, NULL);
97
98    if (!queue)
99        return NULL;
100
101    offset = queue;
102    bzero(offset + sizeof(CFRuntimeBase), size);
103
104    return queue;
105}
106
107//------------------------------------------------------------------------------
108// __IOHIDQueueRelease
109//------------------------------------------------------------------------------
110void __IOHIDQueueRelease( CFTypeRef object )
111{
112    IOHIDQueueRef queue = (IOHIDQueueRef)object;
113
114    if ( queue->elements ) {
115        CFRelease(queue->elements);
116        queue->elements = NULL;
117    }
118
119    if ( queue->queueInterface ) {
120        (*queue->queueInterface)->Release(queue->queueInterface);
121        queue->queueInterface = NULL;
122    }
123
124    if ( queue->device ) {
125        queue->device = NULL;
126    }
127
128    if ( queue->callbackDictionary ) {
129        CFRelease(queue->callbackDictionary);
130        queue->callbackDictionary = NULL;
131    }
132
133}
134
135//------------------------------------------------------------------------------
136// __IOHIDQueueValueAvailableCallback
137//------------------------------------------------------------------------------
138void __IOHIDQueueValueAvailableCallback(
139                                void *                          context,
140                                IOReturn                        result,
141                                void *                          sender __unused)
142{
143    IOHIDQueueRef queue = (IOHIDQueueRef)context;
144
145    if ( !queue || !queue->callbackDictionary)
146        return;
147
148    IOHIDCallbackApplierContext applierContext = {
149        result, queue
150    };
151
152    CFRetain(queue);
153    CFDictionaryApplyFunction(queue->callbackDictionary, _IOHIDCallbackApplier, (void*)&applierContext);
154    CFRelease(queue);
155}
156
157//------------------------------------------------------------------------------
158// IOHIDQueueGetTypeID
159//------------------------------------------------------------------------------
160CFTypeID IOHIDQueueGetTypeID(void)
161{
162    if ( _kCFRuntimeNotATypeID == __kIOHIDQueueTypeID )
163        pthread_once(&__queueTypeInit, __IOHIDQueueRegister);
164
165    return __kIOHIDQueueTypeID;
166}
167
168//------------------------------------------------------------------------------
169// IOHIDQueueCreate
170//------------------------------------------------------------------------------
171IOHIDQueueRef IOHIDQueueCreate(
172                                CFAllocatorRef                  allocator,
173                                IOHIDDeviceRef                  device,
174                                CFIndex                         depth,
175                                IOOptionBits                    options)
176{
177    IOCFPlugInInterface **          deviceInterface = NULL;
178    IOHIDDeviceQueueInterface **    queueInterface  = NULL;
179    IOHIDQueueRef                   queue           = NULL;
180    IOReturn                        ret;
181
182    if ( !device )
183        return NULL;
184
185    deviceInterface = _IOHIDDeviceGetIOCFPlugInInterface(device);
186
187    if ( !deviceInterface )
188        return NULL;
189
190    ret = (*deviceInterface)->QueryInterface(
191                            deviceInterface,
192                            CFUUIDGetUUIDBytes(kIOHIDDeviceQueueInterfaceID),
193                            (LPVOID)&queueInterface);
194
195    if ( ret != kIOReturnSuccess || !queueInterface )
196        return NULL;
197
198    queue = __IOHIDQueueCreate(allocator, NULL);
199
200    if ( !queue ) {
201        (*queueInterface)->Release(queueInterface);
202        return NULL;
203    }
204
205    queue->queueInterface   = queueInterface;
206    /* 9254987 - device is retained by our caller */
207    queue->device           = device;
208
209    (*queue->queueInterface)->setDepth(queue->queueInterface, depth, options);
210
211    return queue;
212}
213
214//------------------------------------------------------------------------------
215// IOHIDQueueGetDevice
216//------------------------------------------------------------------------------
217IOHIDDeviceRef IOHIDQueueGetDevice(
218                                IOHIDQueueRef                   queue)
219{
220    /* caller should retain */
221    return queue->device;
222}
223
224//------------------------------------------------------------------------------
225// IOHIDQueueGetDepth
226//------------------------------------------------------------------------------
227CFIndex IOHIDQueueGetDepth(
228                                IOHIDQueueRef                   queue)
229{
230    uint32_t depth = 0;
231    (*queue->queueInterface)->getDepth(queue->queueInterface, &depth);
232
233    return depth;
234}
235
236//------------------------------------------------------------------------------
237// IOHIDQueueSetDepth
238//------------------------------------------------------------------------------
239void IOHIDQueueSetDepth(
240                                IOHIDQueueRef                   queue,
241                                CFIndex                         depth)
242{
243    (*queue->queueInterface)->setDepth(queue->queueInterface, depth, 0);
244}
245
246//------------------------------------------------------------------------------
247// IOHIDQueueAddElement
248//------------------------------------------------------------------------------
249void IOHIDQueueAddElement(
250                                IOHIDQueueRef                   queue,
251                                IOHIDElementRef                 element)
252{
253    (*queue->queueInterface)->addElement(queue->queueInterface, element, 0);
254
255    if ( !queue->elements )
256        queue->elements = CFSetCreateMutable(CFGetAllocator(queue), 0, &kCFTypeSetCallBacks);
257
258    if ( queue->elements )
259        CFSetAddValue(queue->elements, element);
260}
261
262//------------------------------------------------------------------------------
263// IOHIDQueueRemoveElement
264//------------------------------------------------------------------------------
265void IOHIDQueueRemoveElement(
266                                IOHIDQueueRef                   queue,
267                                IOHIDElementRef                 element)
268{
269    (*queue->queueInterface)->removeElement(queue->queueInterface, element, 0);
270
271    if ( queue->elements )
272        CFSetRemoveValue(queue->elements, element);
273}
274
275//------------------------------------------------------------------------------
276// IOHIDQueueContainsElement
277//------------------------------------------------------------------------------
278Boolean IOHIDQueueContainsElement(
279                                IOHIDQueueRef                   queue,
280                                IOHIDElementRef                 element)
281{
282    Boolean hasElement = FALSE;
283
284    (*queue->queueInterface)->containsElement(
285                                            queue->queueInterface,
286                                            element,
287                                            &hasElement,
288                                            0);
289
290    return hasElement;
291}
292
293//------------------------------------------------------------------------------
294// IOHIDQueueStart
295//------------------------------------------------------------------------------
296void IOHIDQueueStart(           IOHIDQueueRef                   queue)
297{
298    (*queue->queueInterface)->start(queue->queueInterface, 0);
299}
300
301//------------------------------------------------------------------------------
302// IOHIDQueueStop
303//------------------------------------------------------------------------------
304void IOHIDQueueStop(            IOHIDQueueRef                   queue)
305{
306    (*queue->queueInterface)->stop(queue->queueInterface, 0);
307}
308
309//------------------------------------------------------------------------------
310// IOHIDQueueScheduleWithRunLoop
311//------------------------------------------------------------------------------
312void IOHIDQueueScheduleWithRunLoop(
313                                IOHIDQueueRef                   queue,
314                                CFRunLoopRef                    runLoop,
315                                CFStringRef                     runLoopMode)
316{
317    if ( !queue->asyncEventSource) {
318        IOReturn ret;
319
320        ret = (*queue->queueInterface)->getAsyncEventSource(
321                                                    queue->queueInterface,
322                                                    &queue->asyncEventSource);
323
324        if (ret != kIOReturnSuccess || !queue->asyncEventSource)
325            return;
326    }
327
328    queue->asyncRunLoop     = runLoop;
329    queue->asyncRunLoopMode = runLoopMode;
330
331    if (CFGetTypeID(queue->asyncEventSource) == CFRunLoopSourceGetTypeID())
332        CFRunLoopAddSource( queue->asyncRunLoop,
333                            (CFRunLoopSourceRef)queue->asyncEventSource,
334                            queue->asyncRunLoopMode);
335    else if (CFGetTypeID(queue->asyncEventSource) == CFRunLoopTimerGetTypeID())
336        CFRunLoopAddTimer(  queue->asyncRunLoop,
337                            (CFRunLoopTimerRef)queue->asyncEventSource,
338                            queue->asyncRunLoopMode);
339
340}
341
342//------------------------------------------------------------------------------
343// IOHIDQueueUnscheduleFromRunLoop
344//------------------------------------------------------------------------------
345void IOHIDQueueUnscheduleFromRunLoop(
346                                IOHIDQueueRef                   queue,
347                                CFRunLoopRef                    runLoop,
348                                CFStringRef                     runLoopMode)
349{
350    if ( !queue->asyncEventSource )
351        return;
352
353    if (CFGetTypeID(queue->asyncEventSource) == CFRunLoopSourceGetTypeID())
354        CFRunLoopRemoveSource(  runLoop,
355                                (CFRunLoopSourceRef)queue->asyncEventSource,
356                                runLoopMode);
357    else if (CFGetTypeID(queue->asyncEventSource) == CFRunLoopTimerGetTypeID())
358        CFRunLoopRemoveTimer(   runLoop,
359                                (CFRunLoopTimerRef)queue->asyncEventSource,
360                                runLoopMode);
361
362    queue->asyncRunLoop     = NULL;
363    queue->asyncRunLoopMode = NULL;
364}
365
366//------------------------------------------------------------------------------
367// IOHIDQueueRegisterValueAvailableCallback
368//------------------------------------------------------------------------------
369void IOHIDQueueRegisterValueAvailableCallback(
370                                              IOHIDQueueRef                   queue,
371                                              IOHIDCallback                   callback,
372                                              void *                          context)
373{
374    if (!callback) {
375        _IOHIDLog(ASL_LEVEL_ERR, "%s called with a NULL callback\n", __func__);
376        return;
377    }
378    if (!queue->callbackDictionary) {
379        queue->callbackDictionary = CFDictionaryCreateMutable(NULL, 0, NULL, NULL);
380    }
381    if (!queue->callbackDictionary) {
382        _IOHIDLog(ASL_LEVEL_ERR, "%s unable to create dictionary\n", __func__);
383        return;
384    }
385    CFDictionarySetValue(queue->callbackDictionary, (void*)callback, context);
386
387    (*queue->queueInterface)->setValueAvailableCallback(
388                                                        queue->queueInterface,
389                                                        __IOHIDQueueValueAvailableCallback,
390                                                        queue);
391}
392
393//------------------------------------------------------------------------------
394// IOHIDQueueCopyNextValue
395//------------------------------------------------------------------------------
396IOHIDValueRef IOHIDQueueCopyNextValue(
397                                IOHIDQueueRef                   queue)
398{
399    return IOHIDQueueCopyNextValueWithTimeout(queue, 0);
400}
401
402//------------------------------------------------------------------------------
403// IOHIDQueueCopyNextValueWithTimeout
404//------------------------------------------------------------------------------
405IOHIDValueRef IOHIDQueueCopyNextValueWithTimeout(
406                                IOHIDQueueRef                   queue,
407                                CFTimeInterval                  timeout)
408{
409    IOHIDValueRef   value       = NULL;
410    uint32_t        timeoutMS   = timeout * 1000;
411
412    (*queue->queueInterface)->copyNextValue(queue->queueInterface,
413                                            &value,
414                                            timeoutMS,
415                                            0);
416
417    return value;
418}
419
420//------------------------------------------------------------------------------
421// _IOHIDQueueCopyElements
422//------------------------------------------------------------------------------
423CFArrayRef _IOHIDQueueCopyElements(IOHIDQueueRef queue)
424{
425    if ( !queue->elements )
426        return NULL;
427
428    CFIndex count = CFSetGetCount( queue->elements );
429
430    if ( !count )
431        return NULL;
432
433    IOHIDElementRef * elements  = malloc(sizeof(IOHIDElementRef) * count);
434    CFArrayRef        ret       = NULL;
435
436    bzero(elements, sizeof(IOHIDElementRef) * count);
437
438    CFSetGetValues(queue->elements, (const void **)elements);
439
440    ret = CFArrayCreate(CFGetAllocator(queue), (const void **)elements, count, &kCFTypeArrayCallBacks);
441
442    free(elements);
443
444    return ret;
445}
446
447