/* * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include "IOHIDEventServiceUserClient.h" #include "IOHIDEventServiceQueue.h" #include "IOHIDEventData.h" #include "IOHIDEvent.h" #include "IOHIDPrivateKeys.h" #define kQueueSizeMin 0 #define kQueueSizeFake 128 #define kQueueSizeMax 4096 //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // __IOHIDEventServiceQueueFake //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class __IOHIDEventServiceQueueFake { public: IOHIDEventServiceQueue * queue; __IOHIDEventServiceQueueFake() { queue = IOHIDEventServiceQueue::withCapacity(kQueueSizeFake); }; ~__IOHIDEventServiceQueueFake() { queue->release(); }; }; static __IOHIDEventServiceQueueFake __fakeQueue; //=========================================================================== // IOHIDEventServiceUserClient class #define super IOUserClient OSDefineMetaClassAndStructors( IOHIDEventServiceUserClient, IOUserClient ) //============================================================================== // IOHIDEventServiceUserClient::sMethods //============================================================================== const IOExternalMethodDispatch IOHIDEventServiceUserClient::sMethods[kIOHIDEventServiceUserClientNumCommands] = { { // kIOHIDEventServiceUserClientOpen (IOExternalMethodAction) &IOHIDEventServiceUserClient::_open, 1, 0, 0, 0 }, { // kIOHIDEventServiceUserClientClose (IOExternalMethodAction) &IOHIDEventServiceUserClient::_close, 1, 0, 0, 0 }, { // kIOHIDEventServiceUserClientCopyEvent (IOExternalMethodAction) &IOHIDEventServiceUserClient::_copyEvent, 2, -1, 0, -1 }, { // kIOHIDEventServiceUserClientSetElementValue (IOExternalMethodAction) &IOHIDEventServiceUserClient::_setElementValue, 3, 0, 0, 0 }, }; //============================================================================== // IOHIDEventServiceUserClient::getService //============================================================================== IOService * IOHIDEventServiceUserClient::getService( void ) { return _owner; } //============================================================================== // IOHIDEventServiceUserClient::clientClose //============================================================================== IOReturn IOHIDEventServiceUserClient::clientClose( void ) { if (_client) { task_deallocate(_client); _client = 0; } if (_owner) { _owner->close(this, _options); } terminate(); return kIOReturnSuccess; } //============================================================================== // IOHIDEventServiceUserClient::registerNotificationPort //============================================================================== IOReturn IOHIDEventServiceUserClient::registerNotificationPort( mach_port_t port, UInt32 type, UInt32 refCon ) { _queue->setNotificationPort(port); return kIOReturnSuccess; } //============================================================================== // IOHIDEventServiceUserClient::clientMemoryForType //============================================================================== IOReturn IOHIDEventServiceUserClient::clientMemoryForType( UInt32 type, IOOptionBits * options, IOMemoryDescriptor ** memory ) { IOReturn ret = kIOReturnNoMemory; if ( _queue ) { IOMemoryDescriptor * memoryToShare = _queue->getMemoryDescriptor(); // if we got some memory if (memoryToShare) { // Memory will be released by user client // when last map is destroyed. memoryToShare->retain(); ret = kIOReturnSuccess; } // set the result *options = 0; *memory = memoryToShare; } return ret; } //============================================================================== // IOHIDEventServiceUserClient::externalMethod //============================================================================== typedef struct HIDCommandGateArgs { uint32_t selector; IOExternalMethodArguments * arguments; IOExternalMethodDispatch * dispatch; OSObject * target; void * reference; }HIDCommandGateArgs; IOReturn IOHIDEventServiceUserClient::externalMethod( uint32_t selector, IOExternalMethodArguments * arguments, IOExternalMethodDispatch * dispatch, OSObject * target, void * reference) { if (selector < (uint32_t) kIOHIDEventServiceUserClientNumCommands) { dispatch = (IOExternalMethodDispatch *) &sMethods[selector]; if (!target) target = this; } return super::externalMethod(selector, arguments, dispatch, target, reference); } //============================================================================== // IOHIDEventServiceUserClient::initWithTask //============================================================================== bool IOHIDEventServiceUserClient::initWithTask(task_t owningTask, void * security_id, UInt32 type) { if (!super::init()) return false; _client = owningTask; task_reference (_client); return true; } //============================================================================== // IOHIDEventServiceUserClient::start //============================================================================== bool IOHIDEventServiceUserClient::start( IOService * provider ) { OSObject * object; uint32_t queueSize = kQueueSizeMax; if ( !super::start(provider) ) return false; _owner = OSDynamicCast(IOHIDEventService, provider); if ( !_owner ) return false; object = provider->copyProperty(kIOHIDEventServiceQueueSize); if ( OSDynamicCast(OSNumber, object) ) { queueSize = ((OSNumber*)object)->unsigned32BitValue(); queueSize = min(kQueueSizeMax, queueSize); } OSSafeReleaseNULL(object); if ( queueSize ) { _queue = IOHIDEventServiceQueue::withCapacity(queueSize); } else { _queue = __fakeQueue.queue; if ( _queue ) _queue->retain(); } if ( !_queue ) return false; return true; } void IOHIDEventServiceUserClient::stop( IOService * provider ) { _owner = NULL; super::stop(provider); } //============================================================================== // IOHIDEventServiceUserClient::_open //============================================================================== IOReturn IOHIDEventServiceUserClient::_open( IOHIDEventServiceUserClient * target, void * reference, IOExternalMethodArguments * arguments) { return target->open((IOOptionBits)arguments->scalarInput[0]); } //============================================================================== // IOHIDEventServiceUserClient::open //============================================================================== IOReturn IOHIDEventServiceUserClient::open(IOOptionBits options) { if (!_owner) { _queue->setState(false); return kIOReturnOffline; } // get ready just in case events start coming our way _queue->setState(true); _options = options; if (!_owner->open( this, options, NULL, OSMemberFunctionCast(IOHIDEventService::Action, this, &IOHIDEventServiceUserClient::eventServiceCallback)) ) { _queue->setState(false); return kIOReturnExclusiveAccess; } return kIOReturnSuccess; } //============================================================================== // IOHIDEventServiceUserClient::_close //============================================================================== IOReturn IOHIDEventServiceUserClient::_close( IOHIDEventServiceUserClient * target, void * reference, IOExternalMethodArguments * arguments) { return target->close(); } //============================================================================== // IOHIDEventServiceUserClient::close //============================================================================== IOReturn IOHIDEventServiceUserClient::close() { _queue->setState(false); if (_owner) _owner->close(this, _options); return kIOReturnSuccess; } //============================================================================== // IOHIDEventServiceUserClient::_copyEvent //============================================================================== IOReturn IOHIDEventServiceUserClient::_copyEvent( IOHIDEventServiceUserClient * target, void * reference, IOExternalMethodArguments * arguments) { IOHIDEvent * inEvent = NULL; IOHIDEvent * outEvent = NULL; IOReturn ret = kIOReturnError; IOByteCount length = 0; if ( arguments->structureInput && arguments->structureInputSize) inEvent = IOHIDEvent::withBytes(arguments->structureInput, arguments->structureInputSize); do { outEvent = target->copyEvent(arguments->scalarInput[0], inEvent, arguments->scalarInput[1]); if ( !outEvent ) break; length = outEvent->getLength(); if ( length > arguments->structureOutputSize ) { ret = kIOReturnBadArgument; break; } outEvent->readBytes(arguments->structureOutput, length); arguments->structureOutputSize = length; ret = kIOReturnSuccess; } while ( 0 ); if ( inEvent ) inEvent->release(); if ( outEvent ) outEvent->release(); return ret; } //============================================================================== // IOHIDEventServiceUserClient::copyEvent //============================================================================== IOHIDEvent * IOHIDEventServiceUserClient::copyEvent(IOHIDEventType type, IOHIDEvent * matching, IOOptionBits options) { return _owner ? _owner->copyEvent(type, matching, options) : NULL; } //============================================================================== // IOHIDEventServiceUserClient::_setElementValue //============================================================================== IOReturn IOHIDEventServiceUserClient::_setElementValue( IOHIDEventServiceUserClient * target, void * reference, IOExternalMethodArguments * arguments) { target->setElementValue(arguments->scalarInput[0], arguments->scalarInput[1], arguments->scalarInput[2]); return kIOReturnSuccess; } //============================================================================== // IOHIDEventServiceUserClient::setElementValue //============================================================================== void IOHIDEventServiceUserClient::setElementValue(UInt32 usagePage, UInt32 usage, UInt32 value) { if (_owner) _owner->setElementValue(usagePage, usage, value); } //============================================================================== // IOHIDEventServiceUserClient::didTerminate //============================================================================== bool IOHIDEventServiceUserClient::didTerminate(IOService *provider, IOOptionBits options, bool *defer) { if (_owner) _owner->close(this, _options); return super::didTerminate(provider, options, defer); } //============================================================================== // IOHIDEventServiceUserClient::free //============================================================================== void IOHIDEventServiceUserClient::free() { if (_queue) { _queue->release(); _queue = NULL; } if (_owner) { _owner = NULL; } super::free(); } //============================================================================== // IOHIDEventServiceUserClient::setProperties //============================================================================== IOReturn IOHIDEventServiceUserClient::setProperties( OSObject * properties ) { return _owner ? _owner->setProperties(properties) : kIOReturnOffline; } //============================================================================== // IOHIDEventServiceUserClient::eventServiceCallback //============================================================================== void IOHIDEventServiceUserClient::eventServiceCallback( IOHIDEventService * sender, void * context, IOHIDEvent * event, IOOptionBits options) { if (!_queue || !_queue->getState() || _queue == __fakeQueue.queue ) return; //enqueue the event _queue->enqueueEvent(event); }