/*
 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This 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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 2004 Apple Computer, Inc.  All rights reserved.
 *
 */

#include "IOPlatformFunctionDriver.h"

#define super IOService
OSDefineMetaClassAndStructors(IOPlatformFunctionDriver, IOService)

bool IOPlatformFunctionDriver::start(IOService *nub) {
	instantiateFunctionSymbol = OSSymbol::withCString(kInstantiatePlatformFunctions);
	
	// Make ourselves known to the world
	publishResource(instantiateFunctionSymbol, this);
	
	kprintf ("IOPlatformFunctionDriver::start\n");
	return super::start (nub);
}

//*********************************************************************************
// instantiatePlatformFunctions
//*********************************************************************************
IOReturn IOPlatformFunctionDriver::callPlatformFunction(const OSSymbol *functionName,
					bool waitForFunction,
					void *param1, void *param2,
					void *param3, void *param4) {
	
    if (functionName == instantiateFunctionSymbol) 
		return instantiatePlatformFunctions ((IOService *) param1, (OSArray **)param2);
	
	// We should only be called as a published resource so anything else is unsupported
	return kIOReturnUnsupported;
}

//*********************************************************************************
// instantiatePlatformFunctions
//
// A call platform function call invoked by other drivers to tap into 
// IOPlatformFunction services.  They provide us their nub, which we search for
// available platform-do-xxx functions.  Any we find we parse and return to the
// caller in the pfArray parameter.
//*********************************************************************************
IOReturn IOPlatformFunctionDriver::instantiatePlatformFunctions (IOService *nub, OSArray **pfArray)
{
	OSDictionary			*propTable;
	OSCollectionIterator	*propIter;
	OSSymbol				*propKey;
	OSData					*propData, *moreData;
	OSArray					*tempArray;
	IOPlatformFunction		*platformFunction;
	IOReturn				result;
	
	result = kIOReturnSuccess;

	if ( ((propTable = nub->dictionaryWithProperties()) == 0) ||
	     ((propIter = OSCollectionIterator::withCollection(propTable)) == 0) ) {
		if (propTable) propTable->release();
		// Nothing to do, go home
		return result;
	}

	*pfArray = tempArray = NULL;
	
	/*
	 * Iterate through all the properties looking for any "platform-do-xxx" property names.  When we find
	 * one, we create one (or more) IOPlatformFunction objects based on the data and add the objects
	 * to an OSArray of IOPlatformFunction objects.  We do this for every "platform-do-xxx" property
	 * we find.
	 */
	while ((propKey = OSDynamicCast(OSSymbol, propIter->getNextObject())) != 0) {
		if (strncmp(kFunctionProvidedPrefix,
		            propKey->getCStringNoCopy(),
		            strlen(kFunctionProvidedPrefix)) == 0) {
			propData = OSDynamicCast(OSData, propTable->getObject(propKey));

			/*
			 * OK, we have a platform-do-xxx function property and create an IOPlatformFunction object based
			 * on the data.  Note that the data may indicate more than on action assigned to the function.
			 * For example, you might have a function named "platform-do-set-power" that requires
			 * different actions going down to sleep than waking from sleep.  In such a case, the additional
			 * data is returned as a new OSData object in moreData and we create separate objects based on 
			 * this data.
			 */
			do {
				moreData = NULL;
				if (platformFunction = IOPlatformFunction::withPlatformDoFunction(propKey, propData, &moreData)) {
					if (!tempArray) {
						tempArray = OSArray::withCapacity (1);
						if (!tempArray) {
							result =  kIOReturnNoMemory;
							break;
						}
					}
					// Add object to array, which will retain object
					tempArray->setObject(platformFunction);
					platformFunction->release();
					
					// If there's more data, loop and create additional platform function object(s) with remaining data
					propData = moreData;
				}
			} while (moreData);
		}
    }
	
	if (propTable) propTable->release();
	if (propIter) propIter->release();
	
	*pfArray = tempArray;
	return result;
}



