1/*
2 * @APPLE_LICENSE_HEADER_START@
3 *
4 * Copyright (c) 1999-2009 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/*
24 * Copyright (c) 1998 Apple Computer, Inc.  All rights reserved.
25 *
26 * HISTORY
27 *
28 */
29
30
31#include <IOKit/IOLib.h>
32#include <IOKit/hid/IOHIDEventTypes.h>
33#include <libkern/c++/OSContainers.h>
34#include <sys/proc.h>
35#include <AssertMacros.h>
36
37#include "IOHIDUserClient.h"
38#include "IOHIDParameter.h"
39#include "IOHIDFamilyPrivate.h"
40#include "IOHIDPrivate.h"
41#include "IOHIDSystem.h"
42#include "IOHIDEventSystemQueue.h"
43
44
45/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
46
47#undef super
48#define super IOUserClient
49
50OSDefineMetaClassAndStructors(IOHIDUserClient, IOUserClient)
51
52OSDefineMetaClassAndStructors(IOHIDParamUserClient, IOUserClient)
53
54OSDefineMetaClassAndStructors(IOHIDStackShotUserClient, IOUserClient)
55
56OSDefineMetaClassAndStructorsWithInit(IOHIDEventSystemUserClient, IOUserClient, IOHIDEventSystemUserClient::initialize())
57
58/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
59
60bool IOHIDUserClient::start( IOService * _owner )
61{
62    if( !super::start( _owner ))
63        return( false);
64
65    owner = (IOHIDSystem *) _owner;
66
67    return( true );
68}
69
70IOReturn IOHIDUserClient::clientClose( void )
71{
72    owner->evClose();
73#ifdef DEBUG
74    kprintf("%s: client token invalidated\n", getName());
75#endif
76    owner->serverConnect = 0;
77    detach( owner);
78
79    return( kIOReturnSuccess);
80}
81
82IOService * IOHIDUserClient::getService( void )
83{
84    return( owner );
85}
86
87IOReturn IOHIDUserClient::registerNotificationPort(
88		mach_port_t 	port,
89		UInt32		type,
90		UInt32		refCon __unused )
91{
92    if( type != kIOHIDEventNotification)
93	return( kIOReturnUnsupported);
94
95    owner->setEventPort(port);
96    return( kIOReturnSuccess);
97}
98
99IOReturn IOHIDUserClient::connectClient( IOUserClient * client )
100{
101    IOGBounds *         bounds;
102    IOService *         provider;
103    IOGraphicsDevice *	graphicsDevice;
104
105    provider = client->getProvider();
106
107    // avoiding OSDynamicCast & dependency on graphics family
108    if( !provider || !provider->metaCast("IOGraphicsDevice"))
109    	return( kIOReturnBadArgument );
110
111    graphicsDevice = (IOGraphicsDevice *) provider;
112    graphicsDevice->getBoundingRect(&bounds);
113
114    owner->registerScreen(graphicsDevice, bounds, bounds+1);
115
116    return( kIOReturnSuccess);
117}
118
119IOReturn IOHIDUserClient::clientMemoryForType( UInt32 type,
120        UInt32 * flags, IOMemoryDescriptor ** memory )
121{
122    if( type == kIOHIDGlobalMemory) {
123        *flags = 0;
124
125        if (owner->globalMemory)
126            owner->globalMemory->retain();
127        *memory = owner->globalMemory;
128    } else {
129        return kIOReturnBadArgument;
130    }
131
132    return kIOReturnSuccess;
133}
134
135IOExternalMethod * IOHIDUserClient::getTargetAndMethodForIndex(
136                        IOService ** targetP, UInt32 index )
137{
138    static const IOExternalMethod methodTemplate[] = {
139/* 0 */  { NULL, (IOMethod) &IOHIDSystem::createShmem,
140            kIOUCScalarIScalarO, 1, 0 },
141/* 1 */  { NULL, (IOMethod) &IOHIDSystem::setEventsEnable,
142            kIOUCScalarIScalarO, 1, 0 },
143/* 2 */  { NULL, (IOMethod) &IOHIDSystem::setCursorEnable,
144            kIOUCScalarIScalarO, 1, 0 },
145/* 3 */  { NULL, (IOMethod) &IOHIDSystem::extPostEvent,
146            kIOUCStructIStructO, kIOUCVariableStructureSize, 0 },
147/* 4 */  { NULL, (IOMethod) &IOHIDSystem::extSetMouseLocation,
148            kIOUCStructIStructO, kIOUCVariableStructureSize, 0 },
149/* 5 */  { NULL, (IOMethod) &IOHIDSystem::extGetButtonEventNum,
150            kIOUCScalarIScalarO, 1, 1 },
151/* 6 */  { NULL, (IOMethod) &IOHIDSystem::extSetBounds,
152            kIOUCStructIStructO, sizeof( IOGBounds), 0 },
153/* 7 */  { NULL, (IOMethod) &IOHIDSystem::extRegisterVirtualDisplay,
154            kIOUCScalarIScalarO, 0, 1 },
155/* 8 */  { NULL, (IOMethod) &IOHIDSystem::extUnregisterVirtualDisplay,
156            kIOUCScalarIScalarO, 1, 0 },
157/* 9 */  { NULL, (IOMethod) &IOHIDSystem::extSetVirtualDisplayBounds,
158            kIOUCScalarIScalarO, 5, 0 },
159/* 10 */ { NULL, (IOMethod) &IOHIDSystem::extGetUserHidActivityState,
160            kIOUCScalarIScalarO, 0, 1 },
161};
162
163    if( index >= (sizeof(methodTemplate) / sizeof(methodTemplate[0])))
164        return( NULL );
165
166    *targetP = owner;
167
168    return( (IOExternalMethod *)(methodTemplate + index) );
169}
170
171IOReturn IOHIDUserClient::setProperties( OSObject * properties )
172{
173    OSDictionary * dict = OSDynamicCast(OSDictionary, properties);
174    if (dict && dict->getObject(kIOHIDUseKeyswitchKey) &&
175        ( clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator) != kIOReturnSuccess))
176    {
177        dict->removeObject(kIOHIDUseKeyswitchKey);
178    }
179
180    return( owner->setProperties( properties ) );
181}
182
183IOReturn IOHIDUserClient::extGetUserHidActivityState(void* value,void*,void*,void*,void*,void*)
184{
185    IOReturn result = owner->extSetVirtualDisplayBounds(value, 0,0,0,0,0);
186
187    return result;
188}
189
190/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
191bool IOHIDParamUserClient::start( IOService * _owner )
192{
193    if( !super::start( _owner ))
194        return( false);
195
196    owner = (IOHIDSystem *) _owner;
197
198    return( true );
199}
200
201IOService * IOHIDParamUserClient::getService( void )
202{
203    return( owner );
204}
205
206IOExternalMethod * IOHIDParamUserClient::getTargetAndMethodForIndex(
207                        IOService ** targetP, UInt32 index )
208{
209    // get the same library function to work for param & server connects
210    static const IOExternalMethod methodTemplate[] = {
211        /* 0 */  { NULL, NULL, kIOUCScalarIScalarO, 1, 0 },
212        /* 1 */  { NULL, NULL, kIOUCScalarIScalarO, 1, 0 },
213        /* 2 */  { NULL, NULL, kIOUCScalarIScalarO, 1, 0 },
214        /* 3 */  { NULL, (IOMethod) &IOHIDParamUserClient::extPostEvent, kIOUCStructIStructO, 0xffffffff, 0 },
215        /* 4 */  { NULL, (IOMethod) &IOHIDSystem::extSetMouseLocation, kIOUCStructIStructO, 0xffffffff, 0 },
216        /* 5 */  { NULL, (IOMethod) &IOHIDSystem::extGetStateForSelector, kIOUCScalarIScalarO, 1, 1 },
217        /* 6 */  { NULL, (IOMethod) &IOHIDSystem::extSetStateForSelector, kIOUCScalarIScalarO, 2, 0 },
218        /* 7 */  { NULL, (IOMethod) &IOHIDSystem::extRegisterVirtualDisplay, kIOUCScalarIScalarO, 0, 1 },
219        /* 8 */  { NULL, (IOMethod) &IOHIDSystem::extUnregisterVirtualDisplay, kIOUCScalarIScalarO, 1, 0 },
220        /* 9 */  { NULL, (IOMethod) &IOHIDSystem::extSetVirtualDisplayBounds, kIOUCScalarIScalarO, 5, 0 },
221        /* 10 */ { NULL, (IOMethod) &IOHIDParamUserClient::extGetUserHidActivityState, kIOUCScalarIScalarO, 0, 1 },
222    };
223    IOExternalMethod *result = NULL;
224
225    if ((index < 3) || (index >= (sizeof(methodTemplate) / sizeof(methodTemplate[0])))) {
226        *targetP = NULL;
227        result = NULL;
228    }
229    else {
230        result = (IOExternalMethod *) (methodTemplate + index);
231        if ((index == 10) || (index == 3)) {
232            *targetP = this;
233        }
234        else {
235            *targetP = owner;
236        }
237    }
238
239    return result;
240}
241
242IOReturn IOHIDParamUserClient::extPostEvent(void*p1,void*p2,void*,void*,void*,void*)
243{
244    IOReturn result = clientHasPrivilege(current_task(), kIOClientPrivilegeLocalUser);
245    if ( result == kIOReturnSuccess ) {
246        result = owner->extPostEvent(p1, p2, NULL, NULL, NULL, NULL);
247    }
248    return result;
249}
250
251IOReturn IOHIDParamUserClient::setProperties( OSObject * properties )
252{
253    OSDictionary * dict = OSDynamicCast(OSDictionary, properties);
254    if (dict && dict->getObject(kIOHIDUseKeyswitchKey) &&
255        ( clientHasPrivilege(current_task(), kIOClientPrivilegeAdministrator) != kIOReturnSuccess))
256    {
257        dict->removeObject(kIOHIDUseKeyswitchKey);
258    }
259
260    return( owner->setProperties( properties ) );
261}
262
263IOReturn IOHIDParamUserClient::extGetUserHidActivityState(void* value,void*,void*,void*,void*,void*)
264{
265    IOReturn result = owner->extGetUserHidActivityState(value, 0,0,0,0,0);
266
267    return result;
268}
269
270/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
271bool IOHIDStackShotUserClient::
272initWithTask(task_t owningTask, void * /* security_id */, UInt32 /* type */)
273{
274    if (!super::init())
275        return false;
276    IOReturn priv = IOUserClient::clientHasPrivilege(owningTask, kIOClientPrivilegeAdministrator);
277    if (priv != kIOReturnSuccess) {
278        IOLog("%s call failed %08x\n", __PRETTY_FUNCTION__, priv);
279        return false;
280    }
281
282    client = owningTask;
283    task_reference (client);
284
285    return true;
286}
287
288bool IOHIDStackShotUserClient::start( IOService * _owner )
289{
290    if( !super::start( _owner ))
291	return( false);
292
293    owner = (IOHIDSystem *) _owner;
294
295    return( true );
296}
297
298IOReturn IOHIDStackShotUserClient::clientClose( void )
299{
300   if (client) {
301        task_deallocate(client);
302        client = 0;
303    }
304
305    detach( owner);
306
307    return( kIOReturnSuccess);
308}
309
310IOService * IOHIDStackShotUserClient::getService( void )
311{
312    return( owner );
313}
314
315
316IOReturn IOHIDStackShotUserClient::registerNotificationPort(
317		mach_port_t 	port,
318		UInt32		type,
319		UInt32		refCon __unused )
320{
321    if( type != kIOHIDStackShotNotification)
322	return( kIOReturnUnsupported);
323
324    owner->setStackShotPort(port);
325    return( kIOReturnSuccess);
326}
327
328/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
329
330enum { kIOHIDEventSystemKernelQueueID = 100, kIOHIDEventSystemUserQueueID = 200 };
331
332static OSArray * gAllUserQueues;
333static IOLock  * gAllUserQueuesLock;
334
335void
336IOHIDEventSystemUserClient::initialize(void)
337{
338	gAllUserQueuesLock = IOLockAlloc();
339	gAllUserQueues     = OSArray::withCapacity(4);
340}
341
342UInt32
343IOHIDEventSystemUserClient::createIDForDataQueue(IODataQueue * eventQueue)
344{
345	UInt32 queueIdx;
346
347	if (!eventQueue)
348		return (0);
349
350	IOLockLock(gAllUserQueuesLock);
351	for (queueIdx = 0;
352		  OSDynamicCast(IODataQueue, gAllUserQueues->getObject(queueIdx));
353		  queueIdx++) {}
354	gAllUserQueues->setObject(queueIdx, eventQueue);
355	IOLockUnlock(gAllUserQueuesLock);
356
357	return (queueIdx + kIOHIDEventSystemUserQueueID);
358}
359
360void
361IOHIDEventSystemUserClient::removeIDForDataQueue(IODataQueue * eventQueue)
362{
363	UInt32     queueIdx;
364	OSObject * obj;
365
366	if (!eventQueue)
367		return;
368
369	IOLockLock(gAllUserQueuesLock);
370	for (queueIdx = 0;
371		  (obj = gAllUserQueues->getObject(queueIdx));
372		  queueIdx++) {
373		if (obj == eventQueue)
374			gAllUserQueues->replaceObject(queueIdx, kOSBooleanFalse);
375	}
376	IOLockUnlock(gAllUserQueuesLock);
377}
378
379IODataQueue *
380IOHIDEventSystemUserClient::copyDataQueueWithID(UInt32 queueID)
381{
382	IODataQueue * eventQueue;
383
384	IOLockLock(gAllUserQueuesLock);
385	eventQueue = OSDynamicCast(IODataQueue, gAllUserQueues->getObject(queueID - kIOHIDEventSystemUserQueueID));
386	if (eventQueue)
387		eventQueue->retain();
388	IOLockUnlock(gAllUserQueuesLock);
389
390	return (eventQueue);
391}
392
393bool IOHIDEventSystemUserClient::
394initWithTask(task_t owningTask, void * /* security_id */, UInt32 /* type */)
395{
396    if ( !super::init() ) {
397        return false;
398    }
399
400    IOReturn priv = IOUserClient::clientHasPrivilege(owningTask, kIOClientPrivilegeAdministrator);
401    if (priv != kIOReturnSuccess) {
402        IOLog("%s: Client task not privileged to open IOHIDSystem for mapping memory (%08x)\n", __PRETTY_FUNCTION__, priv);
403        return false;
404    }
405
406
407    client = owningTask;
408    task_reference (client);
409
410    return true;
411}
412
413bool IOHIDEventSystemUserClient::start( IOService * _owner )
414{
415    if( !super::start( _owner ))
416	return( false);
417
418    owner = (IOHIDSystem *) _owner;
419
420    return( true );
421}
422
423IOReturn IOHIDEventSystemUserClient::clientClose( void )
424{
425   if (client) {
426        task_deallocate(client);
427        client = 0;
428    }
429
430    detach( owner);
431
432    return( kIOReturnSuccess);
433}
434
435IOService * IOHIDEventSystemUserClient::getService( void )
436{
437    return( owner );
438}
439
440IOReturn IOHIDEventSystemUserClient::clientMemoryForType( UInt32 type,
441        UInt32 * flags, IOMemoryDescriptor ** memory )
442{
443    IODataQueue *   eventQueue = NULL;
444    IOReturn        ret = kIOReturnNoMemory;
445
446	if (type == kIOHIDEventSystemKernelQueueID)
447		eventQueue = kernelQueue;
448	else
449		eventQueue  = copyDataQueueWithID(type);
450
451    if ( eventQueue ) {
452        IOMemoryDescriptor * desc = NULL;
453        *flags = 0;
454
455        desc = eventQueue->getMemoryDescriptor();
456
457        if ( desc ) {
458            desc->retain();
459            ret = kIOReturnSuccess;
460        }
461
462        *memory = desc;
463		if (type != kIOHIDEventSystemKernelQueueID)
464			eventQueue->release();
465
466    } else {
467        ret = kIOReturnBadArgument;
468    }
469
470    return ret;
471}
472
473IOExternalMethod * IOHIDEventSystemUserClient::getTargetAndMethodForIndex(
474                        IOService ** targetP, UInt32 index )
475{
476    static const IOExternalMethod methodTemplate[] = {
477/* 0 */  { NULL, (IOMethod) &IOHIDEventSystemUserClient::createEventQueue,
478            kIOUCScalarIScalarO, 2, 1 },
479/* 1 */  { NULL, (IOMethod) &IOHIDEventSystemUserClient::destroyEventQueue,
480            kIOUCScalarIScalarO, 2, 0 },
481/* 2 */  { NULL, (IOMethod) &IOHIDEventSystemUserClient::tickle,
482            kIOUCScalarIScalarO, 1, 0 }
483    };
484
485    if( index > (sizeof(methodTemplate) / sizeof(methodTemplate[0])))
486        return( NULL );
487
488    *targetP = this;
489    return( (IOExternalMethod *)(methodTemplate + index) );
490}
491
492IOReturn IOHIDEventSystemUserClient::createEventQueue(void*p1,void*p2,void*p3,void*,void*,void*)
493{
494    UInt32          type        = (uintptr_t)p1;
495    IOByteCount     size        = (uintptr_t)p2;
496    UInt32 *        pToken      = (UInt32 *)p3;
497	UInt32			token       = 0;
498    IODataQueue *   eventQueue  = NULL;
499
500    if( !size )
501        return kIOReturnBadArgument;
502
503    switch ( type ) {
504        case kIOHIDEventQueueTypeKernel:
505            if ( !kernelQueue ) {
506                kernelQueue = IOHIDEventServiceQueue::withCapacity(size);
507                if ( kernelQueue ) {
508                    kernelQueue->setState(true);
509                    owner->registerEventQueue(kernelQueue);
510                }
511            }
512            eventQueue = kernelQueue;
513			token = kIOHIDEventSystemKernelQueueID;
514			if ( pToken ) {
515				*pToken = kIOHIDEventSystemKernelQueueID;
516			}
517            break;
518        case kIOHIDEventQueueTypeUser:
519            if (!userQueues)
520                userQueues = OSSet::withCapacity(4);
521
522            eventQueue = IOHIDEventSystemQueue::withCapacity(size);
523			token = createIDForDataQueue(eventQueue);
524			if (eventQueue && userQueues) {
525				userQueues->setObject(eventQueue);
526				eventQueue->release();
527			}
528            break;
529    }
530
531    if( !eventQueue )
532        return kIOReturnNoMemory;
533
534    if ( pToken ) {
535		*pToken = token;
536	}
537
538    return kIOReturnSuccess;
539}
540
541IOReturn IOHIDEventSystemUserClient::destroyEventQueue(void*p1,void*p2,void*,void*,void*,void*)
542{
543    UInt32          type       = (uintptr_t) p1;
544    UInt32          queueID    = (uintptr_t) p2;
545    IODataQueue *   eventQueue = NULL;
546
547	if (queueID == kIOHIDEventSystemKernelQueueID) {
548		eventQueue = kernelQueue;
549		type = kIOHIDEventQueueTypeKernel;
550	} else {
551		eventQueue = copyDataQueueWithID(queueID);
552		type = kIOHIDEventQueueTypeUser;
553	}
554
555    if ( !eventQueue )
556        return kIOReturnBadArgument;
557
558    switch ( type ) {
559        case kIOHIDEventQueueTypeKernel:
560			kernelQueue->setState(false);
561			owner->unregisterEventQueue(kernelQueue);
562			kernelQueue->release();
563			kernelQueue = NULL;
564			break;
565        case kIOHIDEventQueueTypeUser:
566            if (userQueues)
567                userQueues->removeObject(eventQueue);
568			removeIDForDataQueue(eventQueue);
569			eventQueue->release();
570            break;
571    }
572
573    return kIOReturnSuccess;
574}
575
576IOReturn IOHIDEventSystemUserClient::tickle(void*p1,void*,void*,void*,void*,void*)
577{
578    IOHIDEventType eventType = (uintptr_t) p1;
579
580    /* Tickles coming from userspace must follow the same policy as IOHIDSystem.cpp:
581     *   If the display is on, send tickles as usual
582     *   If the display is off, only tickle on key presses and button clicks.
583     */
584    intptr_t otherType = NX_NULLEVENT;
585    if (eventType == kIOHIDEventTypeButton)
586        otherType = NX_LMOUSEDOWN;
587    else if (eventType == kIOHIDEventTypeKeyboard)
588        otherType = NX_KEYDOWN;
589
590    if (otherType)
591    {
592        IOHIDSystemActivityTickle(otherType, this);
593    }
594
595    return kIOReturnSuccess;
596}
597
598void IOHIDEventSystemUserClient::free()
599{
600    if ( kernelQueue ) {
601        kernelQueue->setState(false);
602        if ( owner )
603            owner->unregisterEventQueue(kernelQueue);
604
605        kernelQueue->release();
606    }
607
608    if ( userQueues ) {
609		OSObject * obj;
610		while ((obj = userQueues->getAnyObject()))
611		{
612			removeIDForDataQueue(OSDynamicCast(IODataQueue, obj));
613			userQueues->removeObject(obj);
614		}
615        userQueues->release();
616    }
617
618    super::free();
619}
620
621IOReturn IOHIDEventSystemUserClient::registerNotificationPort(
622		mach_port_t 	port,
623		UInt32		type,
624		UInt32		refCon __unused )
625{
626    IODataQueue * eventQueue = NULL;
627
628	if (type == kIOHIDEventSystemKernelQueueID)
629		eventQueue = kernelQueue;
630	else
631		eventQueue = copyDataQueueWithID(type);
632
633    if ( !eventQueue )
634        return kIOReturnBadArgument;
635
636    eventQueue->setNotificationPort(port);
637
638	if (type != kIOHIDEventSystemKernelQueueID)
639		eventQueue->release();
640
641    return (kIOReturnSuccess);
642}
643
644