1/*
2 * @APPLE_LICENSE_HEADER_START@
3 *
4 * Copyright (c) 1999-2003 Apple Computer, Inc.  All Rights Reserved.
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23#include "IOHIDEventServiceUserClient.h"
24#include "IOHIDEventServiceQueue.h"
25#include "IOHIDEventData.h"
26#include "IOHIDEvent.h"
27#include "IOHIDPrivateKeys.h"
28
29
30#define kQueueSizeMin   0
31#define kQueueSizeFake  128
32#define kQueueSizeMax   4096
33
34
35//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
36// __IOHIDEventServiceQueueFake
37//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
38class __IOHIDEventServiceQueueFake {
39public:
40    IOHIDEventServiceQueue * queue;
41
42    __IOHIDEventServiceQueueFake() {
43        queue = IOHIDEventServiceQueue::withCapacity(kQueueSizeFake);
44    };
45    ~__IOHIDEventServiceQueueFake() {
46        queue->release();
47    };
48};
49
50static __IOHIDEventServiceQueueFake __fakeQueue;
51
52
53//===========================================================================
54// IOHIDEventServiceUserClient class
55
56#define super IOUserClient
57
58OSDefineMetaClassAndStructors( IOHIDEventServiceUserClient, IOUserClient )
59
60//==============================================================================
61// IOHIDEventServiceUserClient::sMethods
62//==============================================================================
63const IOExternalMethodDispatch IOHIDEventServiceUserClient::sMethods[kIOHIDEventServiceUserClientNumCommands] = {
64    { //    kIOHIDEventServiceUserClientOpen
65	(IOExternalMethodAction) &IOHIDEventServiceUserClient::_open,
66	1, 0,
67    0, 0
68    },
69    { //    kIOHIDEventServiceUserClientClose
70	(IOExternalMethodAction) &IOHIDEventServiceUserClient::_close,
71	1, 0,
72    0, 0
73    },
74    { //    kIOHIDEventServiceUserClientCopyEvent
75	(IOExternalMethodAction) &IOHIDEventServiceUserClient::_copyEvent,
76	2, -1,
77    0, -1
78    },
79    { //    kIOHIDEventServiceUserClientSetElementValue
80	(IOExternalMethodAction) &IOHIDEventServiceUserClient::_setElementValue,
81	3, 0,
82    0, 0
83    },
84};
85
86
87//==============================================================================
88// IOHIDEventServiceUserClient::getService
89//==============================================================================
90IOService * IOHIDEventServiceUserClient::getService( void )
91{
92    return _owner;
93}
94
95//==============================================================================
96// IOHIDEventServiceUserClient::clientClose
97//==============================================================================
98IOReturn IOHIDEventServiceUserClient::clientClose( void )
99{
100   if (_client) {
101        task_deallocate(_client);
102        _client = 0;
103    }
104
105   if (_owner) {
106        _owner->close(this, _options);
107    }
108
109    terminate();
110
111    return kIOReturnSuccess;
112}
113
114//==============================================================================
115// IOHIDEventServiceUserClient::registerNotificationPort
116//==============================================================================
117IOReturn IOHIDEventServiceUserClient::registerNotificationPort(
118                            mach_port_t                 port,
119                            UInt32                      type,
120                            UInt32                      refCon )
121{
122    _queue->setNotificationPort(port);
123
124    return kIOReturnSuccess;
125}
126
127//==============================================================================
128// IOHIDEventServiceUserClient::clientMemoryForType
129//==============================================================================
130IOReturn IOHIDEventServiceUserClient::clientMemoryForType(
131                            UInt32                      type,
132                            IOOptionBits *              options,
133                            IOMemoryDescriptor **       memory )
134{
135    IOReturn ret = kIOReturnNoMemory;
136
137    if ( _queue ) {
138        IOMemoryDescriptor * memoryToShare = _queue->getMemoryDescriptor();
139
140        // if we got some memory
141        if (memoryToShare)
142        {
143            // Memory will be released by user client
144            // when last map is destroyed.
145
146            memoryToShare->retain();
147
148            ret = kIOReturnSuccess;
149        }
150
151        // set the result
152        *options = 0;
153        *memory  = memoryToShare;
154    }
155
156    return ret;
157}
158
159//==============================================================================
160// IOHIDEventServiceUserClient::externalMethod
161//==============================================================================
162typedef struct HIDCommandGateArgs {
163    uint32_t                    selector;
164    IOExternalMethodArguments * arguments;
165    IOExternalMethodDispatch *  dispatch;
166    OSObject *                  target;
167    void *                      reference;
168}HIDCommandGateArgs;
169
170IOReturn IOHIDEventServiceUserClient::externalMethod(
171                            uint32_t                    selector,
172                            IOExternalMethodArguments * arguments,
173                            IOExternalMethodDispatch *  dispatch,
174                            OSObject *                  target,
175                            void *                      reference)
176{
177    if (selector < (uint32_t) kIOHIDEventServiceUserClientNumCommands)
178    {
179        dispatch = (IOExternalMethodDispatch *) &sMethods[selector];
180
181        if (!target)
182            target = this;
183    }
184
185	return super::externalMethod(selector, arguments, dispatch, target, reference);
186}
187
188//==============================================================================
189// IOHIDEventServiceUserClient::initWithTask
190//==============================================================================
191bool IOHIDEventServiceUserClient::initWithTask(task_t owningTask, void * security_id, UInt32 type)
192{
193    if (!super::init())
194        return false;
195
196    _client = owningTask;
197
198    task_reference (_client);
199
200    return true;
201}
202
203//==============================================================================
204// IOHIDEventServiceUserClient::start
205//==============================================================================
206bool IOHIDEventServiceUserClient::start( IOService * provider )
207{
208    OSObject *  object;
209    uint32_t    queueSize = kQueueSizeMax;
210
211    if ( !super::start(provider) )
212        return false;
213
214    _owner = OSDynamicCast(IOHIDEventService, provider);
215    if ( !_owner )
216        return false;
217
218    object = provider->copyProperty(kIOHIDEventServiceQueueSize);
219    if ( OSDynamicCast(OSNumber, object) ) {
220        queueSize = ((OSNumber*)object)->unsigned32BitValue();
221        queueSize = min(kQueueSizeMax, queueSize);
222    }
223    OSSafeReleaseNULL(object);
224
225    if ( queueSize ) {
226        _queue = IOHIDEventServiceQueue::withCapacity(queueSize);
227    } else {
228        _queue = __fakeQueue.queue;
229        if ( _queue )
230            _queue->retain();
231    }
232
233    if ( !_queue )
234        return false;
235
236    return true;
237}
238
239void IOHIDEventServiceUserClient::stop( IOService * provider )
240{
241    _owner = NULL;
242    super::stop(provider);
243}
244
245//==============================================================================
246// IOHIDEventServiceUserClient::_open
247//==============================================================================
248IOReturn IOHIDEventServiceUserClient::_open(
249                                IOHIDEventServiceUserClient *   target,
250                                void *                          reference,
251                                IOExternalMethodArguments *     arguments)
252{
253    return target->open((IOOptionBits)arguments->scalarInput[0]);
254}
255
256//==============================================================================
257// IOHIDEventServiceUserClient::open
258//==============================================================================
259IOReturn IOHIDEventServiceUserClient::open(IOOptionBits options)
260{
261    if (!_owner) {
262        _queue->setState(false);
263        return kIOReturnOffline;
264    }
265
266    // get ready just in case events start coming our way
267    _queue->setState(true);
268    _options = options;
269
270    if (!_owner->open(  this,
271                        options,
272                        NULL,
273                        OSMemberFunctionCast(IOHIDEventService::Action,
274                        this, &IOHIDEventServiceUserClient::eventServiceCallback)) ) {
275        _queue->setState(false);
276        return kIOReturnExclusiveAccess;
277    }
278
279    return kIOReturnSuccess;
280}
281
282//==============================================================================
283// IOHIDEventServiceUserClient::_close
284//==============================================================================
285IOReturn IOHIDEventServiceUserClient::_close(
286                                IOHIDEventServiceUserClient *   target,
287                                void *                          reference,
288                                IOExternalMethodArguments *     arguments)
289{
290    return target->close();
291}
292
293//==============================================================================
294// IOHIDEventServiceUserClient::close
295//==============================================================================
296IOReturn IOHIDEventServiceUserClient::close()
297{
298    _queue->setState(false);
299    if (_owner)
300    _owner->close(this, _options);
301
302    return kIOReturnSuccess;
303}
304
305//==============================================================================
306// IOHIDEventServiceUserClient::_copyEvent
307//==============================================================================
308IOReturn IOHIDEventServiceUserClient::_copyEvent(
309                                IOHIDEventServiceUserClient *   target,
310                                void *                          reference,
311                                IOExternalMethodArguments *     arguments)
312{
313    IOHIDEvent *    inEvent     = NULL;
314    IOHIDEvent *    outEvent    = NULL;
315    IOReturn        ret         = kIOReturnError;
316    IOByteCount     length      = 0;
317
318    if ( arguments->structureInput && arguments->structureInputSize)
319        inEvent = IOHIDEvent::withBytes(arguments->structureInput, arguments->structureInputSize);
320
321    do {
322        outEvent = target->copyEvent(arguments->scalarInput[0], inEvent, arguments->scalarInput[1]);
323
324        if ( !outEvent )
325            break;
326
327        length = outEvent->getLength();
328
329        if ( length > arguments->structureOutputSize ) {
330            ret = kIOReturnBadArgument;
331            break;
332        }
333
334        outEvent->readBytes(arguments->structureOutput, length);
335        arguments->structureOutputSize = length;
336
337        ret = kIOReturnSuccess;
338
339    } while ( 0 );
340
341    if ( inEvent )
342        inEvent->release();
343
344    if ( outEvent )
345        outEvent->release();
346
347    return ret;
348}
349
350//==============================================================================
351// IOHIDEventServiceUserClient::copyEvent
352//==============================================================================
353IOHIDEvent * IOHIDEventServiceUserClient::copyEvent(IOHIDEventType type, IOHIDEvent * matching, IOOptionBits options)
354{
355    return _owner ? _owner->copyEvent(type, matching, options) : NULL;
356}
357
358//==============================================================================
359// IOHIDEventServiceUserClient::_setElementValue
360//==============================================================================
361IOReturn IOHIDEventServiceUserClient::_setElementValue(
362                                IOHIDEventServiceUserClient *   target,
363                                void *                          reference,
364                                IOExternalMethodArguments *     arguments)
365{
366    target->setElementValue(arguments->scalarInput[0], arguments->scalarInput[1], arguments->scalarInput[2]);
367
368    return kIOReturnSuccess;
369}
370
371//==============================================================================
372// IOHIDEventServiceUserClient::setElementValue
373//==============================================================================
374void IOHIDEventServiceUserClient::setElementValue(UInt32 usagePage, UInt32 usage, UInt32 value)
375{
376    if (_owner)
377        _owner->setElementValue(usagePage, usage, value);
378}
379
380//==============================================================================
381// IOHIDEventServiceUserClient::didTerminate
382//==============================================================================
383bool IOHIDEventServiceUserClient::didTerminate(IOService *provider, IOOptionBits options, bool *defer)
384{
385    if (_owner)
386        _owner->close(this, _options);
387
388    return super::didTerminate(provider, options, defer);
389}
390
391//==============================================================================
392// IOHIDEventServiceUserClient::free
393//==============================================================================
394void IOHIDEventServiceUserClient::free()
395{
396    if (_queue) {
397        _queue->release();
398        _queue = NULL;
399    }
400
401    if (_owner) {
402        _owner = NULL;
403    }
404
405    super::free();
406}
407
408//==============================================================================
409// IOHIDEventServiceUserClient::setProperties
410//==============================================================================
411IOReturn IOHIDEventServiceUserClient::setProperties( OSObject * properties )
412{
413    return _owner ? _owner->setProperties(properties) : kIOReturnOffline;
414}
415
416//==============================================================================
417// IOHIDEventServiceUserClient::eventServiceCallback
418//==============================================================================
419void IOHIDEventServiceUserClient::eventServiceCallback(
420                                IOHIDEventService *             sender,
421                                void *                          context,
422                                IOHIDEvent *                    event,
423                                IOOptionBits                    options)
424{
425    if (!_queue || !_queue->getState() || _queue == __fakeQueue.queue )
426        return;
427
428    //enqueue the event
429    _queue->enqueueEvent(event);
430}
431