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