/*
 * Copyright (c) 2002-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) 2002-2004 Apple Computer, Inc.  All rights reserved.
 *
 *  DRI: Dave Radcliffe
 *
 */

#include "IOPlatformFunction.h"

/*
 * Table descriptions:  scanSubCommand is driven by two tables.  The first, the commandArray is indexed
 * by command number and each entry is just the address of the associated commandDescriptorArray. The
 * command descriptor array is a variable length array laid out as follows (each pN is a SInt32):
 *
 * p0, p1, p2, p3..., 0
 *
 * p0 is a summary descriptor - for a fixed length command it is a positive number representing the
 * 		length of the command, in longwords.  This need be the only entry in the descriptor.  For a variable 
 * 		length command it is the negative of the fixed length portion of the variable command and will be
 *		followed by descriptions of the individual parameters.
 * p1 to pN - for a variable length command, p1 up to p10 describe each parameter as follows:
 *		a number in the fixed portion of the command is just treated as side of the corresponding parameter.
 *		A negative number in the variable portion of the command is treated as the negative index of
 *		the length parameter associated with the variable data
 *
 *	For example, the command kCommandRMWConfig is laid out as follows:
 * 		1) offset
 * 		2) number of bytes to mask
 * 		3) number of bytes in value
 * 		4) number of bytes to transfer
 * 		5) array of bytes containing mask
 * 		6) array of bytes containing value
 *
 * The corresponding command descriptor array would be:
 *
 *	p0		-kCommandRMWConfigLength			// Negative of length of fixed portion (longwords)
 *  p1		4									// offset (4 bytes)
 *  p2		4									// number of bytes of mask (4 bytes)
 *  p3		4									// number of bytes of value (4 bytes)
 *  p4		4									// number of bytes to transfer (4 bytes)
 *  p5		-2									// index of number of bytes of mask data (i.e., p2)
 *  p6		-3									// index of number of bytes of value data (i.e., p3)
 *  		0									// Zero array terminator
 */
#define kCommandDescTerminator 0

static SInt32 gCommandCommandListDesc[] 		= { kCommandCommandListLength, kCommandDescTerminator };
static SInt32 gCommandWriteGPIODesc[] 			= { kCommandWriteGPIOLength, kCommandDescTerminator };
static SInt32 gCommandReadGPIODesc[] 			= { kCommandReadGPIOLength, kCommandDescTerminator };
static SInt32 gCommandWriteReg32Desc[] 			= { kCommandWriteReg32Length, kCommandDescTerminator };
static SInt32 gCommandReadReg32Desc[] 			= { kCommandReadReg32Length, kCommandDescTerminator };
static SInt32 gCommandWriteReg16Desc[] 			= { kCommandWriteReg16Length, kCommandDescTerminator };
static SInt32 gCommandReadReg16Desc[] 			= { kCommandReadReg16Length, kCommandDescTerminator };
static SInt32 gCommandWriteReg8Desc[] 			= { kCommandWriteReg8Length, kCommandDescTerminator };
static SInt32 gCommandReadReg8Desc[] 			= { kCommandReadReg8Length, kCommandDescTerminator };
static SInt32 gCommandDelayDesc[] 				= { kCommandDelayLength, kCommandDescTerminator };
static SInt32 gCommandWaitReg32Desc[] 			= { kCommandWaitReg32Length, kCommandDescTerminator };
static SInt32 gCommandWaitReg16Desc[] 			= { kCommandWaitReg16Length, kCommandDescTerminator };
static SInt32 gCommandWaitReg8Desc[] 			= { kCommandWaitReg8Length, kCommandDescTerminator };
static SInt32 gCommandReadI2CDesc[] 			= { kCommandReadI2CLength, kCommandDescTerminator };
static SInt32 gCommandWriteI2CDesc[] 			= { -kCommandWriteI2CLength, 4, -1, kCommandDescTerminator };
static SInt32 gCommandRMWI2CDesc[] 				= { -kCommandRMWI2CLength, 4, 4, 4, -1, -2, kCommandDescTerminator };
static SInt32 gCommandGeneralI2CDesc[] 			= { kCommandGeneralI2CLength, kCommandDescTerminator };
static SInt32 gCommandShiftBytesRightDesc[] 	= { kCommandShiftBytesRightLength, kCommandDescTerminator };
static SInt32 gCommandShiftBytesLeftDesc[] 		= { kCommandShiftBytesLeftLength, kCommandDescTerminator };
static SInt32 gCommandReadConfigDesc[] 			= { kCommandReadConfigLength, kCommandDescTerminator };
static SInt32 gCommandWriteConfigDesc[] 		= { -kCommandWriteConfigLength, 4, 4, -2, kCommandDescTerminator };
static SInt32 gCommandRMWConfigDesc[] 			= { -kCommandRMWConfigLength, 4, 4, 4, 4, -2, -3, kCommandDescTerminator };
static SInt32 gCommandReadI2CSubAddrDesc[] 		= { kCommandReadI2CSubAddrLength, kCommandDescTerminator };
static SInt32 gCommandWriteI2CSubAddrDesc[] 	= { -kCommandWriteI2CSubAddrLength, 4, 4, -2, kCommandDescTerminator };
static SInt32 gCommandI2CModeDesc[] 			= { kCommandI2CModeLength, kCommandDescTerminator }; 
static SInt32 gCommandRMWI2CSubAddrDesc[] 		= { -kCommandRMWI2CSubAddrLength, 4, 4, 4, 4, -2, -3, kCommandDescTerminator }; 
static SInt32 gCommandReadReg32MaskShRtXORDesc[] = { kCommandReadReg32MaskShRtXORLength, kCommandDescTerminator };
static SInt32 gCommandReadReg16MaskShRtXORDesc[] = { kCommandReadReg16MaskShRtXORLength, kCommandDescTerminator };
static SInt32 gCommandReadReg8MaskShRtXORDesc[] = { kCommandReadReg8MaskShRtXORLength, kCommandDescTerminator };
static SInt32 gCommandWriteReg32ShLtMaskDesc[] 	= { kCommandWriteReg32ShLtMaskLength, kCommandDescTerminator };
static SInt32 gCommandWriteReg16ShLtMaskDesc[] 	= { kCommandWriteReg16ShLtMaskLength, kCommandDescTerminator };
static SInt32 gCommandWriteReg8ShLtMaskDesc[] 	= { kCommandWriteReg8ShLtMaskLength, kCommandDescTerminator };
static SInt32 gCommandMaskandCompareDesc[] 		= { -kCommandMaskandCompareLength, 4, -1, -1,  kCommandDescTerminator };

static SInt32 gCommandShiftBitStreamRightDesc[]	= { kCommandShiftBitStreamRightLength, kCommandDescTerminator };
static SInt32 gCommandShiftBitStreamLeftDesc[]	= { kCommandShiftBitStreamLeftLength, kCommandDescTerminator };
static SInt32 gCommandMaskByteStreamDesc[]		= { -kCommandMaskByteStreamLength, 4, -1, kCommandDescTerminator };
static SInt32 gCommandXorByteStreamDesc[]		= { -kCommandXorByteStreamLength, 4, -1, kCommandDescTerminator };
static SInt32 gCommandWriteI2CValueToSubAddrDesc[] = { kCommandWriteI2CValueToSubAddrLength, kCommandDescTerminator };
static SInt32 gCommandXORValueToByteStreamDesc[]	= { kCommandXORValueToByteStreamLength, kCommandDescTerminator };
static SInt32 gCommandImplementationSpecificDesc[] = { kCommandImplementationSpecificLength, kCommandDescTerminator };

// Command array - one descriptor entry for each command, indexed by command
static SInt32 *gCommandArray[] = {
	gCommandCommandListDesc,			// kCommandCommandList [ 0 ]
	gCommandWriteGPIODesc,				// kCommandWriteGPIO [ 1 ]
	gCommandReadGPIODesc,				// kCommandReadGPIO [ 2 ]
	gCommandWriteReg32Desc,				// kCommandWriteReg32 [ 3 ]
	gCommandReadReg32Desc,				// kCommandReadReg32 [ 4 ]
	gCommandWriteReg16Desc,				// kCommandWriteReg16 [ 5 ]
	gCommandReadReg16Desc,				// kCommandReadReg16 [ 6 ]
	gCommandWriteReg8Desc,				// kCommandWriteReg8 [ 7 ]
	gCommandReadReg8Desc,				// kCommandReadReg8 [ 8 ]
	gCommandDelayDesc,					// kCommandDelay [ 9 ]
	gCommandWaitReg32Desc,				// kCommandWaitReg32 [ 10 ]
	gCommandWaitReg16Desc,				// kCommandWaitReg16 [ 11 ]
	gCommandWaitReg8Desc,				// kCommandWaitReg8	[ 12 ]
	gCommandReadI2CDesc,				// kCommandReadI2C [ 13 ]
	gCommandWriteI2CDesc,				// kCommandWriteI2C [ 14 ]
	gCommandRMWI2CDesc,					// kCommandRMWI2C [ 15 ]
	gCommandGeneralI2CDesc,				// kCommandGeneralI2C [ 16 ]
	gCommandShiftBytesRightDesc,		// kCommandShiftBytesRight [ 17 ]
	gCommandShiftBytesLeftDesc,			// kCommandShiftBytesLeft [ 18 ]
	gCommandReadConfigDesc,				// kCommandReadConfig [ 19 ]
	gCommandWriteConfigDesc,			// kCommandWriteConfig [ 20 ]
	gCommandRMWConfigDesc,				// kCommandRMWConfig [ 21 ]
	gCommandReadI2CSubAddrDesc,			// kCommandReadI2CSubAddr [ 22 ]
	gCommandWriteI2CSubAddrDesc,		// kCommandWriteI2CSubAddr [ 23 ]
	gCommandI2CModeDesc,				// kCommandI2CMode [ 24] 
	gCommandRMWI2CSubAddrDesc,			// kCommandRMWI2CSubAddr [ 25] 
	gCommandReadReg32MaskShRtXORDesc,	// kCommandReadReg32MaskShRtXOR	[ 26 ]
	gCommandReadReg16MaskShRtXORDesc,	// kCommandReadReg16MaskShRtXOR	[ 27 ]
	gCommandReadReg8MaskShRtXORDesc,	// kCommandReadReg8MaskShRtXOR [ 28 ]
	gCommandWriteReg32ShLtMaskDesc,		// kCommandWriteReg32ShLtMask [ 29 ]
	gCommandWriteReg16ShLtMaskDesc,		// kCommandWriteReg16ShLtMask [ 30 ]
	gCommandWriteReg8ShLtMaskDesc,		// kCommandWriteReg8ShLtMask [ 31 ]
	gCommandMaskandCompareDesc,			// kCommandMaskandCompare [ 32 ]

	gCommandShiftBitStreamRightDesc,	// kCommandMaskandCompare [ 33 ]
	gCommandShiftBitStreamLeftDesc,		// kCommandMaskandCompare [ 34 ]
	gCommandMaskByteStreamDesc,			// kCommandMaskandCompare [ 35 ]
	gCommandXorByteStreamDesc,			// kCommandMaskandCompare [ 36 ]
	gCommandWriteI2CValueToSubAddrDesc,	// kCommandMaskandCompare [ 37 ]
	gCommandXORValueToByteStreamDesc,	// kCommandMaskandCompare [ 38 ]
	gCommandImplementationSpecificDesc	// kCommandMaskandCompare [ 39 ]
};

#ifndef PFPARSE
/********************************************************************************************************/
/* class IOPlatformFunction																				*/
/********************************************************************************************************/
#define super OSObject

OSDefineMetaClassAndStructors(IOPlatformFunction, OSObject);

/********************************************************************************************************/
/* IOPlatformFunction::initWithPlatformDoFunction														*/
/********************************************************************************************************/
bool IOPlatformFunction::initWithPlatformDoFunction(OSSymbol *functionName, OSData *functionData,
                            OSData **moreFunctionData)
{
	char		tmpFunctionName[128], *tmpNamePtr, *funcNamePtr;
	UInt32		*moreData;
	UInt32		totalLen, result;

    if (!super::init() || !functionName || !functionData)
        return false;
	
	platformFunctionData = functionData;
	platformFunctionPtr = (UInt32 *)functionData->getBytesNoCopy();
	platformFunctionDataLen = functionData->getLength();
	
	// Create an associated iterator - we'll keep this around as long as we exist
	iterator = IOPlatformFunctionIterator::withIOPlatformFunction (this);
	if (!iterator)
		return false;

	/* 
	 * Make a preliminary scan of the command data.  If scanCommand returns "moreData" then we pass
	 * that back to the caller so the caller can make a new IOPlatformFunction object based on that data
	 */
	moreData = iterator->scanCommand (platformFunctionPtr, platformFunctionDataLen, &totalLen,
		&flags, &pHandle, &result);
	
	if (result != kIOPFNoError)
		return false;
	
	/*
	 * If more data was found, create a new OSData object with the remaining data.
	 * This new object is returned to the caller, who is responsible for using it to
	 * create a new IOPlatformFunction object to handle it
	 */
	if (moreData) {
		totalLen += (sizeof(UInt32) * 2);	// Account for pHandle & flags
		*moreFunctionData = OSData::withBytes (moreData, (platformFunctionDataLen - totalLen));
		
		platformFunctionDataLen = totalLen;				// Save adjusted length
	} else {
		// Nothing else to see here, move along...
		*moreFunctionData = NULL;
	}
		
	platformFunctionData->retain();
	
	/*
	 * If this is an on-demand or interrupt function, create the necessary function name
	 */
	if (flags & (kIOPFFlagOnDemand | kIOPFFlagIntGen)) {
		*tmpFunctionName = '\0';
		tmpNamePtr = tmpFunctionName;
		funcNamePtr = (char *)functionName->getCStringNoCopy();
		
		// Generate new function name from "platform-do-functionName", but without the "-do"
		while (*funcNamePtr != '-')		// Copy start of string
			*(tmpNamePtr++) = *(funcNamePtr++);
		funcNamePtr += 3;				// Skip -do
		while (*funcNamePtr)			// Copy rest of string
			*(tmpNamePtr++) = *(funcNamePtr++);
		
		sprintf (tmpNamePtr, "-%08lx", pHandle);		// Append pHandle
        platformFunctionSymbol = OSSymbol::withCString(tmpFunctionName);
		DLOG ("IOPF::initWithPlatformDoFunction(%lx) - creating platformFunctionSymbol '%s'\n", mypfobject, tmpFunctionName);
	} else
		// Don't bother for other cases as we don't need to publish a name
		platformFunctionSymbol = NULL;
	
    return true;
}

/********************************************************************************************************/
/* IOPlatformFunction::withPlatformDoFunction															*/
/********************************************************************************************************/
IOPlatformFunction *IOPlatformFunction::withPlatformDoFunction(OSSymbol *functionName, OSData *functionData,
                            OSData **moreFunctionData)
{
    IOPlatformFunction *me = new IOPlatformFunction;

    if (me && !me->initWithPlatformDoFunction(functionName, functionData, moreFunctionData)) {
        me->free();
        return NULL;
    }

    return me;
}

/********************************************************************************************************/
/* IOPlatformFunction::free																				*/
/********************************************************************************************************/
void IOPlatformFunction::free()
{
    if (iterator) {
        iterator->release();
        iterator = NULL;
    }
	
    if (platformFunctionSymbol) {
        platformFunctionSymbol->release();
        platformFunctionSymbol = NULL;
    }
	
    if (platformFunctionData) {
        platformFunctionData->release();
        platformFunctionData = NULL;
    }

    super::free();
}

/********************************************************************************************************/
/* IOPlatformFunction::validatePlatformFunction															*/
/********************************************************************************************************/
bool IOPlatformFunction::validatePlatformFunction(UInt32 flagsMask, UInt32 pHandleValue)
{
	// Only validate against pHandle if caller cares about pHandle
	if (pHandleValue && (pHandle != pHandleValue))
		return false;
	
	// Validate against flags
	return ((flags & flagsMask) != 0);
}

/********************************************************************************************************/
/* IOPlatformFunction::platformFunctionMatch															*/
/********************************************************************************************************/
bool IOPlatformFunction::platformFunctionMatch(const OSSymbol *funcSym, UInt32 flagsMask, UInt32 pHandleValue)
{
	/*
	 * platformFunctionMatch only makes sense for on-demand and interrupt generating functions
	 * Those are the only functions for which we generate a platformFunctionSymbol
	 * So if we don't have a symbol, just return false
	 */
	if (!(platformFunctionSymbol && funcSym))
		return false;
		
	return (platformFunctionSymbol->isEqualTo(funcSym) && validatePlatformFunction (flagsMask, pHandleValue));
}

/********************************************************************************************************/
/* IOPlatformFunction::getPlatformFunctionName															*/
/********************************************************************************************************/
const OSSymbol *IOPlatformFunction::getPlatformFunctionName() const
{
	return platformFunctionSymbol;
}

/********************************************************************************************************/
/* IOPlatformFunction::getCommandFlags																	*/
/********************************************************************************************************/
UInt32 IOPlatformFunction::getCommandFlags() const
{
	return flags;
}

/********************************************************************************************************/
/* IOPlatformFunction::getCommandPHandle																*/
/********************************************************************************************************/
UInt32 IOPlatformFunction::getCommandPHandle() const
{
	return pHandle;
}

/********************************************************************************************************/
/* IOPlatformFunction::getCommandIterator																*/
/********************************************************************************************************/
IOPlatformFunctionIterator *IOPlatformFunction::getCommandIterator()
{
	/*
	 * The iterator is created once in our start routine, what we do here is
	 * reset it to the beginning and retain it on behalf of the caller (who
	 * should release it when done)
	 */
	if (iterator) {
		iterator->retain();
		iterator->reset();
	}
	return iterator;
}

/********************************************************************************************************/
/* IOPlatformFunction::publishPlatformFunction															*/
/********************************************************************************************************/
void IOPlatformFunction::publishPlatformFunction(IOService *handler)
{
	if (platformFunctionSymbol)
		handler->publishResource(platformFunctionSymbol, handler);

	return;
}

    OSMetaClassDefineReservedUnused(IOPlatformFunction, 0);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 1);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 2);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 3);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 4);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 5);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 6);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 7);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 8);
    OSMetaClassDefineReservedUnused(IOPlatformFunction, 9);

/********************************************************************************************************/
/* class IOPlatformFunctionIterator																		*/
/********************************************************************************************************/
#undef super
#define super OSIterator

OSDefineMetaClassAndStructors(IOPlatformFunctionIterator, OSIterator);

/********************************************************************************************************/
/* IOPlatformFunctionIterator::withIOPlatformFunction													*/
/********************************************************************************************************/
IOPlatformFunctionIterator *IOPlatformFunctionIterator::withIOPlatformFunction(const IOPlatformFunction *inFunc)
{
    IOPlatformFunctionIterator *me = new IOPlatformFunctionIterator;

    if (me && !me->initWithIOPlatformFunction(inFunc)) {
        me->free();
        return 0;
    }

    return me;
}

/********************************************************************************************************/
/* IOPlatformFunctionIterator::initWithIOPlatformFunction												*/
/********************************************************************************************************/
bool IOPlatformFunctionIterator::initWithIOPlatformFunction(const IOPlatformFunction *inFunc)
{
    if (!inFunc)
        return false;

    platformFunction = inFunc;
    commandPtr = NULL;
    totalCommandCount = 0;
    currentCommandCount = 0;
	isCommandList = false;
    valid = true;

    return true;
}

/********************************************************************************************************/
/* IOPlatformFunctionIterator::free																		*/
/********************************************************************************************************/
void IOPlatformFunctionIterator::free()
{
	valid = false;
	
    super::free();
	return;
}

/********************************************************************************************************/
/* IOPlatformFunctionIterator::reset																	*/
/********************************************************************************************************/
void IOPlatformFunctionIterator::reset()
{
	commandPtr = platformFunction->platformFunctionPtr;
	dataLengthRemaining = platformFunction->platformFunctionDataLen;
	commandDone = false;
	totalCommandCount = currentCommandCount = 0;
	return;
}

/********************************************************************************************************/
/* IOPlatformFunctionIterator::isValid																	*/
/********************************************************************************************************/
bool IOPlatformFunctionIterator::isValid()
{
	return valid;
}

/********************************************************************************************************/
/* IOPlatformFunctionIterator::getNextObject															*/
/********************************************************************************************************/
OSObject *IOPlatformFunctionIterator::getNextObject()
{
	// N/A for this class
	return NULL;
}
#endif // !PFPARSE

/********************************************************************************************************/
/* IOPlatformFunctionIterator::getNextCommand															*/
/********************************************************************************************************/
bool 
#ifndef PFPARSE
IOPlatformFunctionIterator::
#endif
	getNextCommand(UInt32 *cmd, UInt32 *cmdLen,
					UInt32 *param1, UInt32 *param2, UInt32 *param3, UInt32 *param4, 
					UInt32 *param5, UInt32 *param6, UInt32 *param7, UInt32 *param8, 
					UInt32 *param9, UInt32 *param10, UInt32 *result)
{
	if (commandDone) {
		*result = kIOPFNoError;
		return false;
	}
	
	// If this is our first real iteration (i.e., we've been reset), then skip pHandle and flags
#ifdef PFPARSE
	if (commandPtr == platformFunctionPtr) {
#else
	if (commandPtr == platformFunction->platformFunctionPtr) {
#endif
		commandPtr += 2;		// Skip pHandle and flags and adjust length remaining
		dataLengthRemaining -= 2 * sizeof(UInt32);

		// If this is also a command list then skip over the commandList command and return the first real command
		if (isCommandList) {
			// Skip past command list command
			commandPtr = scanSubCommand (commandPtr, dataLengthRemaining, true, cmd, cmdLen, NULL, NULL, NULL, NULL, NULL,
				NULL, NULL, NULL, NULL, NULL, result); 
			dataLengthRemaining -= *cmdLen;
		}
	}
	
	/*
	 * Return the next command in the command string as determined by the current commandPtr.  Also
	 * return the associated data and update commandPtr accordingly.
	 */
	commandPtr = scanSubCommand (commandPtr, dataLengthRemaining, false, cmd, cmdLen, param1, param2, param3, param4, param5,
		param6, param7, param8, param9, param10, result);
	
	if (*result != kIOPFNoError)
		return false;
	
	dataLengthRemaining -= *cmdLen;
	if (commandPtr == NULL) {
		commandDone = true;
		DLOG ("IOPFI::getNextCommand - cmd %ld, dataLenRemaining %ld, cmdLen %ld, commandPtr NULL\n", 
			*cmd, dataLengthRemaining, *cmdLen);
	} else {
		commandDone = false;
		DLOG ("IOPFI::getNextCommand - cmd %ld, dataLenRemaining %ld, cmdLen %ld, *commandPtr 0x%x\n", 
			*cmd, dataLengthRemaining, *cmdLen, *commandPtr);
	}
	
	return true;
}

/********************************************************************************************************/
/* IOPlatformFunctionIterator::scanSubCommand															*/
/********************************************************************************************************/
UInt32 *
#ifndef PFPARSE
IOPlatformFunctionIterator::
#endif
	scanSubCommand (UInt32 *cmdPtr, UInt32 lenRemaining,
					bool quickScan, UInt32 *cmd, UInt32 *cmdLen,
					UInt32 *param1, UInt32 *param2, UInt32 *param3, UInt32 *param4, 
					UInt32 *param5, UInt32 *param6, UInt32 *param7, UInt32 *param8, 
					UInt32 *param9, UInt32 *param10, UInt32 *result)
{
	SInt32		fixedLen, *cmdDescPtr, paramDesc, descIndex;
	UInt32		*cmdBase, cmdLongLen, paramsUsed, tmpLen, *paramPtr;
	
	*result = kIOPFNoError;
	cmdBase = cmdPtr;
	*cmd = *(cmdPtr++);			// command is first word
	if (*cmd > kCommandMaxCommand) {
		*result = kIOPFUnknownCmd;
		return cmdPtr;
	}

	cmdLongLen = 1;				// Include command itself in total length
	if (*cmd == kCommandCommandList) {
		/*
		 * A commandList is treated specially as it is never really returned to the client.
		 * All we do is note that we're dealing with a command list and keep track of how
		 * many subCommands are in the list
		 */
		isCommandList = true;
		totalCommandCount = *(cmdPtr++);
		DLOG ("IOPFI::scanSubCommand(0x%lx) - got commandList, totalCmdCount %ld\n", mypfobject, totalCommandCount);
		cmdLongLen += kCommandCommandListLength;
		*cmdLen = cmdLongLen * sizeof(UInt32);
		if (*cmdLen > lenRemaining)
			*result = kIOPFBadCmdLength;		// Too many parameters to return or not enough data
		else
			paramsUsed = 0;
	} else {
		cmdDescPtr = gCommandArray[*cmd];			// Look up command descriptor array
		fixedLen = cmdDescPtr[0];
		if (fixedLen >= 0)						// Fixed length command
			cmdLongLen += fixedLen;
		else
			cmdLongLen += (-fixedLen);			// Fixed portion of variable length command
		/*
			* If we're here, then we're dealing with a known, fixed length command
			* so we return the appropriate amount of data
			*/
		tmpLen = cmdLongLen;
		//DLOG ("fixed command: tmpLen %ld, cmdLongLen %ld, lenRemaining %ld\n", tmpLen, cmdLongLen, lenRemaining);
		if (((tmpLen - 1) > kIOPFMaxParams) || ((tmpLen * sizeof(UInt32)) > lenRemaining))
			*result = kIOPFBadCmdLength;		// Too many parameters to return or not enough data
		else {
			*cmdLen = cmdLongLen * sizeof(UInt32);		// return byte length
			tmpLen--;
			if (quickScan) {
				// Just need to calculate the length, not return any data
				cmdPtr += tmpLen;
			} else {
				DLOG ("IOPFI::scanSubCommand(%lx) - copying %ld parameters, 1st param 0x%lx\n", mypfobject, tmpLen, *cmdPtr);
				paramsUsed = tmpLen;
				while (1) {
					// Copy the right number of parameters into place
					*param1 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param2 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param3 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param4 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param5 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param6 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param7 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param8 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param9 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					*param10 = *(cmdPtr++);
					if (!(--tmpLen)) break;
					
					// Should not get here
					DLOG ("IOPFI::scanSubCommand - passed param10, OOPS!\n");
					break;
				}
			}
		}
		if (fixedLen < 0) {				// Deal with variable portion of command - the current assumption is all other data is variable
			*cmdLen = cmdLongLen * sizeof (UInt32);	// byte length, so far
			descIndex = cmdLongLen;		// Index of start of variable data in command descriptor array
			while ((paramDesc = cmdDescPtr[descIndex]) != kCommandDescTerminator) {
				// paramDesc is the index into the fixed portion of the command sequence for the word
				// that contains the length of the current parameter.  It is negative to signify a
				// variable length parameter.  However, we do not currently accommodate a fixed length
				// parameter within the variable length portion.
				if (paramDesc < 0)
					paramDesc = -paramDesc;
					
				*cmdLen += cmdBase[paramDesc];		// Add length for this parameter by looking up previous data
				if (!quickScan) {
					switch (descIndex) {
						case 1:
							paramPtr = param1;
							break;
						case 2:
							paramPtr = param2;
							break;
						case 3:
							paramPtr = param3;
							break;
						case 4:
							paramPtr = param4;
							break;
						case 5:
							paramPtr = param5;
							break;
						case 6:
							paramPtr = param6;
							break;
						case 7:
							paramPtr = param7;
							break;
						case 8:
							paramPtr = param8;
							break;
						case 9:
							paramPtr = param9;
							break;
						case 10:
							paramPtr = param10;
							break;
						default:
							break;
					}
					*paramPtr = (UInt32)cmdPtr;
					paramsUsed++;
				}
				cmdPtr = (UInt32 *)((UInt8 *)cmdPtr + cmdBase[paramDesc]);
				
				descIndex++;
			}
		}
		if (isCommandList)
			currentCommandCount++;
		
		if (!quickScan) {
			/*
			 * Clean up unused parameters - this is primarily a diagnostic aid
			 * Leaving garbage in parameters can be suggestive that they represent
			 * real information.  Making them zero eliminates that.
			 */
			switch (paramsUsed) {
				case 0:
					if (param1) *param1 = 0;
				case 1:
					if (param2) *param2 = 0;
				case 2:
					if (param3) *param3 = 0;
				case 3:
					if (param4) *param4 = 0;
				case 4:
					if (param5) *param5 = 0;
				case 5:
					if (param6) *param6 = 0;
				case 6:
					if (param7) *param7 = 0;
				case 7:
					if (param8) *param8 = 0;
				case 8:
					if (param9) *param9 = 0;
				case 9:
					if (param10) *param10 = 0;
				default:
					break;
			}
		}
	}
	
	// If there was an error or there is no data remaining, return NULL, otherwise return remaining data
	if ((*result != kIOPFNoError) || (*cmdLen == lenRemaining))
		cmdPtr = NULL;
	
	if (cmdPtr)
		DLOG ("IOPFI::scanSubCommand - done, *result %ld, *cmdLen %ld, lenRemaining %ld, *cmdPtr\n",
			*result, *cmdLen, lenRemaining, cmdPtr);
	else
		DLOG ("IOPFI::scanSubCommand - done, *result %ld, *cmdLen %ld, lenRemaining %ld, cmdPtr NULL\n",
			*result, *cmdLen, lenRemaining);
	return cmdPtr;
}

/********************************************************************************************************/
/* IOPlatformFunctionIterator::scanCommand																*/
/********************************************************************************************************/
UInt32 *
#ifndef PFPARSE
IOPlatformFunctionIterator::
#endif
	scanCommand (UInt32 *cmdPtr, UInt32 dataLen, UInt32 *cmdTotalLen,
					UInt32 *flags, UInt32 *pHandle, UInt32 *result)
{
	UInt32	cmdLen, cmd;
	
	// Minimum of three longwords required (pHandle, flags, command)
	if (dataLen < 3) {
		*result = kIOPFBadCmdLength;
		return NULL;
	}
	
	// Remember pHandle and flags
	*pHandle = *(cmdPtr++);
	*flags = *(cmdPtr++);
	dataLen -= 2 * sizeof(UInt32);		// Adjust for pHandle and flags
	
	DLOG ("IOPFI::scanCommand: pHandle 0x%lx, flags 0x%lx, len left %ld\n", *pHandle, *flags, dataLen);
	
	*cmdTotalLen = 0;
	
	// Sanity check first command
	if ((*cmdPtr < 0) || (*cmdPtr > kCommandMaxCommand)) {
		*result = kIOPFBadCmdLength;
		return NULL;
	}

	/*
	 * Make a pass through the entire command, ignoring data in the command for now.
	 * The purpose here is to validate the command and also to see if there are additional
	 * commands beyond the current set.  This allows us to break a separate command out into
	 * its own IOPlatformFunction object.
	 */
	do {
		cmdPtr = scanSubCommand (cmdPtr, dataLen, true, &cmd, &cmdLen, NULL, NULL, NULL, NULL, NULL,
			NULL, NULL, NULL, NULL, NULL, result);
		
		*cmdTotalLen += cmdLen;

		if ((*result != kIOPFNoError) || !cmdPtr)
			break;
		
		dataLen -= cmdLen;
		if (dataLen < 0) {
			*result = kIOPFBadCmdLength;
			return NULL;
		}
		
		// If this is single command or it's a command list and we've seen the required number of subcommands, we're done
		//if (isCommandList && (currentCommandCount == totalCommandCount))
		if (!isCommandList || (currentCommandCount == totalCommandCount))
			break;
		
	} while (dataLen);

	if (cmdPtr)
		DLOG ("IOPFI::scanCommand: done, *cmdPtr 0x%lx\n", *cmdPtr);
	else
		DLOG ("IOPFI::scanCommand: done, cmdPtr NULL\n");

	// return updated cmdPtr (meaning there are more commands), or NULL if we're completely done
	return cmdPtr;
}

#ifndef PFPARSE
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 0);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 1);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 2);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 3);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 4);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 5);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 6);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 7);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 8);
    OSMetaClassDefineReservedUnused(IOPlatformFunctionIterator, 9);
#endif
