IOHIDUserClientIniter.cpp   [plain text]


/*
 * Copyright (c) 1998-2006 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * 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 <libkern/OSAtomic.h>
#include <libkern/c++/OSDictionary.h>
#include <IOKit/IOLib.h>

#include "IOHIDUserClientIniter.h"

#define super IOService

OSDefineMetaClassAndStructors(IOHIDUserClientIniter, IOService);

/*
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 0);
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 1);
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 2);
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 3);
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 4);
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 5);
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 6);
OSMetaClassDefineReservedUnused(IOHIDUserClientIniter, 7);
*/

bool IOHIDUserClientIniter::start(IOService* provider)
{
    OSObject *      providerMergeProperties = NULL;
    bool            result = false;

    providerMergeProperties = copyProperty("IOProviderMergeProperties");
    result = mergeDictionaryIntoProvider(provider, OSDynamicCast(OSDictionary, providerMergeProperties));
    OSSafeReleaseNULL(providerMergeProperties);
    return result;
}

bool IOHIDUserClientIniter::mergeDictionaryIntoProvider(IOService * provider, OSDictionary * dictionaryToMerge)
{
    const OSSymbol * 		dictionaryEntry = NULL;
    OSCollectionIterator * 	iter = NULL;
    bool                    result = false;


    if (!provider || !dictionaryToMerge)
        return false;

    // Get the dictionary whose entries we need to merge into our provider and get
    // an iterator to it.
    //
    iter = OSCollectionIterator::withCollection((OSDictionary *)dictionaryToMerge);
    if ( iter != NULL ) {
        // Iterate through the dictionary until we run out of entries
        //
        OSObject *	providerObject = NULL;
        while ( NULL != (dictionaryEntry = (const OSSymbol *)iter->getNextObject()) ) {
            OSDictionary *	sourceDictionary = NULL;
            OSDictionary *	providerDictionary = NULL;

            // Check to see if our destination already has the same entry.
            //
            OSSafeReleaseNULL(providerObject); // takes care of the one copied the previous time around
            providerObject = provider->copyProperty(dictionaryEntry);
            
            // See if our source entry is also a dictionary
            //
            sourceDictionary = OSDynamicCast(OSDictionary, dictionaryToMerge->getObject(dictionaryEntry));
            providerDictionary = OSDynamicCast(OSDictionary, providerDictionary);
            if ( providerDictionary &&  sourceDictionary )  {
                // Need to merge our entry into the provider's dictionary.  However, we don't have a copy of our dictionary, just
                // a reference to it.  So, we need to make a copy of our provider's dictionary so that we don't modify our provider's
                // dictionary using non-synchronize calls.
                //
                OSDictionary    *localCopyOfProvidersDictionary;
                UInt32          providerSize;
                UInt32          providerSizeAfterMerge;

                // A capacity of 0 indicates that the dictionary should have the same size as the source
                //
                localCopyOfProvidersDictionary = OSDictionary::withDictionary( providerDictionary, 0);
                if ( localCopyOfProvidersDictionary == NULL )
                    break;
                
                // Get the size of our provider's dictionary so that we can check later whether it changed
                //
                providerSize = providerDictionary->getCapacity();
                
                // Recursively merge the two dictionaries
                //
                result = mergeDictionaryIntoDictionary(  sourceDictionary, localCopyOfProvidersDictionary);
                if ( result ) {
                    // Get the size of our provider's dictionary so to see if it's changed  (Yes, the size could remain the same but the contents
                    // could have changed, but this gives us a first approximation.  We're not doing anything with this result, although we could
                    // remerge if the size changed)
                    //
                    providerSizeAfterMerge = providerDictionary->getCapacity();

                    // OK, now we can just set the property in our provider
                    //
                    result = provider->setProperty( dictionaryEntry, localCopyOfProvidersDictionary );
                    if ( !result )
                        break;
                }
                else
                    // If we got an error merging dictionaries, then just bail out without doing anything
                    //
                    break;
            } else {
                // Not a dictionary, so just set the property
                //
                result = provider->setProperty(dictionaryEntry, dictionaryToMerge->getObject(dictionaryEntry));
                if ( !result )
                    break;
            }
        }
        OSSafeReleaseNULL(providerObject); // takes care of the one from the last time around
        iter->release();
    }

    return result;
}


bool IOHIDUserClientIniter::mergeDictionaryIntoDictionary(OSDictionary * parentSourceDictionary,  OSDictionary * parentTargetDictionary)
{
    OSCollectionIterator*	srcIterator     = NULL;
    OSSymbol*               keyObject       = NULL;
    bool                    result          = false;

    if (!parentSourceDictionary || !parentTargetDictionary)
        return false ;

    // Get our source dictionary
    //
    srcIterator = OSCollectionIterator::withCollection(parentSourceDictionary);

    while (NULL != (keyObject = OSDynamicCast(OSSymbol, srcIterator->getNextObject()))) {
        OSDictionary *	childSourceDictionary   = NULL;
        OSDictionary *	childTargetDictionary   = NULL;
        OSObject *      childTargetObject       = NULL;

        // Check to see if our destination already has the same entry.
        //
        childTargetObject = parentTargetDictionary->getObject(keyObject);
        if ( childTargetObject )
            childTargetDictionary = OSDynamicCast(OSDictionary, childTargetObject);

        // See if our source entry is also a dictionary
        //
        childSourceDictionary = OSDynamicCast(OSDictionary, parentSourceDictionary->getObject(keyObject));

        if ( childTargetDictionary && childSourceDictionary) {
            // Our destination dictionary already has the entry for this same object AND our
            // source is also a dcitionary, so we need to recursively add it.
            //
            result = mergeDictionaryIntoDictionary(childSourceDictionary, childTargetDictionary) ;
            if ( !result )
                break;
        } else {
            // We have a property that we need to merge into our parent dictionary.
            //
            result = parentTargetDictionary->setObject(keyObject, parentSourceDictionary->getObject(keyObject)) ;
            if ( !result )
                break;
        }
    }

    srcIterator->release();

    return result;
}