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 "IOHIDLibPrivate.h"
28#include "IOHIDDevice.h"
29#include "IOHIDTransaction.h"
30
31static IOHIDTransactionRef  __IOHIDTransactionCreate(
32                                    CFAllocatorRef          allocator,
33                                    CFAllocatorContext *    context __unused);
34static void                 __IOHIDTransactionRelease( CFTypeRef object );
35static void                 __IOHIDTransactionCommitCallback(
36                                    void *                  context,
37                                    IOReturn                result,
38                                    void *                  sender);
39
40
41typedef struct __IOHIDTransaction
42{
43    CFRuntimeBase                   cfBase;   // base CFType information
44
45    IOHIDDeviceTransactionInterface**     transactionInterface;
46
47    CFTypeRef                       asyncEventSource;
48    CFRunLoopRef                    runLoop;
49    CFStringRef                     runLoopMode;
50
51    IOHIDDeviceRef                  device;
52    void *                          context;
53    IOHIDCallback                   callback;
54} __IOHIDTransaction, *__IOHIDTransactionRef;
55
56static const CFRuntimeClass __IOHIDTransactionClass = {
57    0,                          // version
58    "IOHIDTransaction",         // className
59    NULL,                       // init
60    NULL,                       // copy
61    __IOHIDTransactionRelease,  // finalize
62    NULL,                       // equal
63    NULL,                       // hash
64    NULL,                       // copyFormattingDesc
65    NULL,
66    NULL,
67    NULL
68};
69
70static pthread_once_t   __transactionTypeInit = PTHREAD_ONCE_INIT;
71static CFTypeID         __kIOHIDTransactionTypeID = _kCFRuntimeNotATypeID;
72
73//------------------------------------------------------------------------------
74// __IOHIDTransactionRegister
75//------------------------------------------------------------------------------
76void __IOHIDTransactionRegister(void)
77{
78    __kIOHIDTransactionTypeID =
79                _CFRuntimeRegisterClass(&__IOHIDTransactionClass);
80}
81
82//------------------------------------------------------------------------------
83// __IOHIDTransactionCreate
84//------------------------------------------------------------------------------
85IOHIDTransactionRef __IOHIDTransactionCreate(
86                                CFAllocatorRef              allocator,
87                                CFAllocatorContext *        context __unused)
88{
89    IOHIDTransactionRef transaction = NULL;
90    void *              offset      = NULL;
91    uint32_t            size;
92
93    /* allocate service */
94    size  = sizeof(__IOHIDTransaction) - sizeof(CFRuntimeBase);
95    transaction = (IOHIDTransactionRef)_CFRuntimeCreateInstance(
96                                                allocator,
97                                                IOHIDTransactionGetTypeID(),
98                                                size,
99                                                NULL);
100
101    if (!transaction)
102        return NULL;
103
104    offset = transaction;
105    bzero(offset + sizeof(CFRuntimeBase), size);
106
107    return transaction;
108}
109
110//------------------------------------------------------------------------------
111// __IOHIDTransactionRelease
112//------------------------------------------------------------------------------
113void __IOHIDTransactionRelease( CFTypeRef object )
114{
115    IOHIDTransactionRef transaction = (IOHIDTransactionRef)object;
116
117    if ( transaction->transactionInterface ) {
118        (*transaction->transactionInterface)->Release(
119                                            transaction->transactionInterface);
120        transaction->transactionInterface = NULL;
121    }
122
123    if ( transaction->device ) {
124        CFRelease(transaction->device);
125        transaction->device = NULL;
126    }
127}
128
129//------------------------------------------------------------------------------
130// __IOHIDTransactionCommitCallback
131//------------------------------------------------------------------------------
132void __IOHIDTransactionCommitCallback(
133                                    void *                  context,
134                                    IOReturn                result,
135                                    void *                  sender)
136{
137    IOHIDTransactionRef transaction = (IOHIDTransactionRef)context;
138
139    if ((transaction->transactionInterface == sender) && transaction->callback)
140        (*transaction->callback)(   transaction->context,
141                                    result,
142                                    transaction);
143}
144
145//------------------------------------------------------------------------------
146// IOHIDTransactionGetTypeID
147//------------------------------------------------------------------------------
148CFTypeID IOHIDTransactionGetTypeID(void)
149{
150    if ( _kCFRuntimeNotATypeID == __kIOHIDTransactionTypeID )
151        pthread_once(&__transactionTypeInit, __IOHIDTransactionRegister);
152
153    return __kIOHIDTransactionTypeID;
154}
155
156//------------------------------------------------------------------------------
157// IOHIDTransactionCreate
158//------------------------------------------------------------------------------
159IOHIDTransactionRef IOHIDTransactionCreate(
160                                CFAllocatorRef                  allocator,
161                                IOHIDDeviceRef                  device,
162                                IOHIDTransactionDirectionType   direction,
163                                IOOptionBits                    options)
164{
165    IOCFPlugInInterface **              deviceInterface         = NULL;
166    IOHIDDeviceTransactionInterface **  transactionInterface    = NULL;
167    IOHIDTransactionRef                 transaction             = NULL;
168    IOReturn                            ret;
169
170    if ( !device )
171        return NULL;
172
173    deviceInterface = _IOHIDDeviceGetIOCFPlugInInterface(device);
174
175    if ( !deviceInterface )
176        return NULL;
177
178    ret = (*deviceInterface)->QueryInterface(
179                        deviceInterface,
180                        CFUUIDGetUUIDBytes(kIOHIDDeviceTransactionInterfaceID),
181                        (LPVOID)&transactionInterface);
182
183    if ( ret != kIOReturnSuccess || !transactionInterface )
184        return NULL;
185
186    transaction = __IOHIDTransactionCreate(allocator, NULL);
187
188    if ( !transaction ) {
189        (*transactionInterface)->Release(transactionInterface);
190        return NULL;
191    }
192
193    transaction->transactionInterface   = transactionInterface;
194    transaction->device                 = (IOHIDDeviceRef)CFRetain(device);
195
196    (*transaction->transactionInterface)->setDirection(
197                            transaction->transactionInterface,
198                            direction,
199                            options);
200
201    return transaction;
202}
203
204//------------------------------------------------------------------------------
205// IOHIDTransactionGetDirection
206//------------------------------------------------------------------------------
207IOHIDDeviceRef IOHIDTransactionGetDevice(
208                                IOHIDTransactionRef             transaction)
209{
210    return transaction->device;
211}
212
213//------------------------------------------------------------------------------
214// IOHIDTransactionGetDirection
215//------------------------------------------------------------------------------
216IOHIDTransactionDirectionType IOHIDTransactionGetDirection(
217                                IOHIDTransactionRef             transaction)
218{
219    IOHIDTransactionDirectionType direction = 0;
220    (*transaction->transactionInterface)->getDirection(
221                                        transaction->transactionInterface,
222                                        &direction);
223
224    return direction;
225}
226
227//------------------------------------------------------------------------------
228// IOHIDTransactionSetDirection
229//------------------------------------------------------------------------------
230void IOHIDTransactionSetDirection(
231                                IOHIDTransactionRef             transaction,
232                                IOHIDTransactionDirectionType   direction)
233{
234    (*transaction->transactionInterface)->setDirection(
235                                        transaction->transactionInterface,
236                                        direction,
237                                        0);
238}
239
240//------------------------------------------------------------------------------
241// IOHIDTransactionAddElement
242//------------------------------------------------------------------------------
243void IOHIDTransactionAddElement(
244                                IOHIDTransactionRef             transaction,
245                                IOHIDElementRef                 element)
246{
247    (*transaction->transactionInterface)->addElement(
248                                        transaction->transactionInterface,
249                                        element,
250                                        0);
251}
252
253//------------------------------------------------------------------------------
254// IOHIDTransactionRemoveElement
255//------------------------------------------------------------------------------
256void IOHIDTransactionRemoveElement(
257                                IOHIDTransactionRef             transaction,
258                                IOHIDElementRef                 element)
259{
260    (*transaction->transactionInterface)->removeElement(
261                                        transaction->transactionInterface,
262                                        element,
263                                        0);
264}
265
266//------------------------------------------------------------------------------
267// IOHIDTransactionContainsElement
268//------------------------------------------------------------------------------
269Boolean IOHIDTransactionContainsElement(
270                                IOHIDTransactionRef             transaction,
271                                IOHIDElementRef                 element)
272{
273    Boolean hasElement = FALSE;
274
275    (*transaction->transactionInterface)->containsElement(
276                                            transaction->transactionInterface,
277                                            element,
278                                            &hasElement,
279                                            0);
280
281    return hasElement;
282}
283
284//------------------------------------------------------------------------------
285// IOHIDTransactionScheduleWithRunLoop
286//------------------------------------------------------------------------------
287void IOHIDTransactionScheduleWithRunLoop(
288                                IOHIDTransactionRef             transaction,
289                                CFRunLoopRef                    runLoop,
290                                CFStringRef                     runLoopMode)
291{
292    if ( !transaction->asyncEventSource) {
293        IOReturn ret;
294
295        ret = (*transaction->transactionInterface)->getAsyncEventSource(
296                                            transaction->transactionInterface,
297                                            &transaction->asyncEventSource);
298
299        if (ret != kIOReturnSuccess || !transaction->asyncEventSource)
300            return;
301    }
302
303    transaction->runLoop     = runLoop;
304    transaction->runLoopMode = runLoopMode;
305
306    if (CFGetTypeID(transaction->asyncEventSource) == CFRunLoopSourceGetTypeID())
307        CFRunLoopAddSource( transaction->runLoop,
308                            (CFRunLoopSourceRef)transaction->asyncEventSource,
309                            transaction->runLoopMode);
310    else if (CFGetTypeID(transaction->asyncEventSource) == CFRunLoopTimerGetTypeID())
311        CFRunLoopAddTimer(  transaction->runLoop,
312                            (CFRunLoopTimerRef)transaction->asyncEventSource,
313                            transaction->runLoopMode);
314
315}
316
317//------------------------------------------------------------------------------
318// IOHIDTransactionUnscheduleFromRunLoop
319//------------------------------------------------------------------------------
320void IOHIDTransactionUnscheduleFromRunLoop(
321                                IOHIDTransactionRef                   transaction,
322                                CFRunLoopRef                    runLoop,
323                                CFStringRef                     runLoopMode)
324{
325    if ( !transaction->asyncEventSource )
326        return;
327
328    if (CFGetTypeID(transaction->asyncEventSource) == CFRunLoopSourceGetTypeID())
329        CFRunLoopRemoveSource(  runLoop,
330                                (CFRunLoopSourceRef)transaction->asyncEventSource,
331                                runLoopMode);
332    else if (CFGetTypeID(transaction->asyncEventSource) == CFRunLoopTimerGetTypeID())
333        CFRunLoopRemoveTimer(   runLoop,
334                                (CFRunLoopTimerRef)transaction->asyncEventSource,
335                                runLoopMode);
336
337    transaction->runLoop     = NULL;
338    transaction->runLoopMode = NULL;
339}
340
341//------------------------------------------------------------------------------
342// IOHIDTransactionSetValue
343//------------------------------------------------------------------------------
344void IOHIDTransactionSetValue(
345                                IOHIDTransactionRef             transaction,
346                                IOHIDElementRef                 element,
347                                IOHIDValueRef                   value,
348                                IOOptionBits                    options)
349{
350    (*transaction->transactionInterface)->setValue(
351                                            transaction->transactionInterface,
352                                            element,
353                                            value,
354                                            options);
355}
356
357//------------------------------------------------------------------------------
358// IOHIDTransactionGetValue
359//------------------------------------------------------------------------------
360IOHIDValueRef IOHIDTransactionGetValue(
361                                IOHIDTransactionRef             transaction,
362                                IOHIDElementRef                 element,
363                                IOOptionBits                    options)
364{
365    IOHIDValueRef value = NULL;
366    IOReturn ret;
367
368    ret = (*transaction->transactionInterface)->getValue(
369                                            transaction->transactionInterface,
370                                            element,
371                                            &value,
372                                            options);
373
374    return (ret == kIOReturnSuccess) ? value : NULL;
375}
376
377//------------------------------------------------------------------------------
378// IOHIDTransactionCommit
379//------------------------------------------------------------------------------
380IOReturn IOHIDTransactionCommit(
381                                IOHIDTransactionRef             transaction)
382{
383    return IOHIDTransactionCommitWithCallback(transaction, 0, NULL, NULL);
384}
385
386//------------------------------------------------------------------------------
387// IOHIDTransactionCommitWithCallback
388//------------------------------------------------------------------------------
389IOReturn IOHIDTransactionCommitWithCallback(
390                                IOHIDTransactionRef             transaction,
391                                CFTimeInterval                  timeout,
392                                IOHIDCallback                   callback,
393                                void *                          context)
394{
395    uint32_t    timeoutMS = timeout / 1000;
396
397    transaction->callback   = callback;
398    transaction->context    = context;
399
400    return (*transaction->transactionInterface)->commit(
401                                            transaction->transactionInterface,
402                                            timeoutMS,
403                                            __IOHIDTransactionCommitCallback,
404                                            transaction,
405                                            0);
406}
407
408//------------------------------------------------------------------------------
409// IOHIDTransactionUnscheduleFromRunLoop
410//------------------------------------------------------------------------------
411void IOHIDTransactionClear(
412                                IOHIDTransactionRef             transaction)
413{
414    (*transaction->transactionInterface)->clear(
415                                            transaction->transactionInterface,
416                                            0);
417}
418