/*
 * Copyright (c) 2002-2003 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-2003 Apple Computer, Inc.  All rights reserved.
 *
 *
 */
//		$Log: IOPlatformCtrlLoop.cpp,v $
//		Revision 1.6  2003/07/16 02:02:09  eem
//		3288772, 3321372, 3328661
//		
//		Revision 1.5  2003/06/07 01:30:56  eem
//		Merge of EEM-PM72-ActiveFans-2 branch, with a few extra tweaks.  This
//		checkin has working PID control for PowerMac7,2 platforms, as well as
//		a first shot at localized strings.
//		
//		Revision 1.4.2.10  2003/06/06 11:00:42  eem
//		Drive Bay and U3 zones now also under PID control.
//		
//		Revision 1.4.2.9  2003/06/06 08:17:56  eem
//		Holy Motherfucking shit.  PID is really working.
//		
//		Revision 1.4.2.8  2003/06/04 10:21:10  eem
//		Supports forced PID meta states.
//		
//		Revision 1.4.2.7  2003/06/04 00:00:51  eem
//		More PID stuff, working towards support for forced meta states.
//		
//		Revision 1.4.2.6  2003/06/01 14:52:51  eem
//		Most of the PID algorithm is implemented.
//		
//		Revision 1.4.2.5  2003/05/31 08:11:34  eem
//		Initial pass at integrating deadline-based timer callbacks for PID loops.
//		
//		Revision 1.4.2.4  2003/05/29 03:51:34  eem
//		Clean up environment dictionary access.
//		
//		Revision 1.4.2.3  2003/05/23 06:36:57  eem
//		More registration notification stuff.
//		
//		Revision 1.4.2.2  2003/05/23 05:44:40  eem
//		Cleanup, ctrlloops not get notification for sensor and control registration.
//		
//		Revision 1.4.2.1  2003/05/22 01:31:04  eem
//		Checkin of today's work (fails compilations right now).
//		
//		Revision 1.4  2003/05/21 21:58:49  eem
//		Merge from EEM-PM72-ActiveFans-1 branch with initial crack at active fan
//		control on Q37.
//		
//		Revision 1.3.2.3  2003/05/17 12:55:37  eem
//		Active fan control works on RPM channels!!!!!!
//		
//		Revision 1.3.2.2  2003/05/16 07:08:45  eem
//		Table-lookup active fan control working with this checkin.
//		
//		Revision 1.3.2.1  2003/05/14 22:07:48  eem
//		Implemented state-driven sensor, cleaned up "const" usage and header
//		inclusions.
//		
//		Revision 1.3  2003/05/13 02:13:51  eem
//		PowerMac7_2 Dynamic Power Step support.
//		
//		Revision 1.2.2.1  2003/05/12 11:21:10  eem
//		Support for slewing.
//		
//		Revision 1.2  2003/05/10 06:50:33  eem
//		All sensor functionality included for PowerMac7_2_PlatformPlugin.  Version
//		is 1.0.1d12.
//		
//		Revision 1.1.2.1  2003/05/01 09:28:40  eem
//		Initial check-in in progress toward first Q37 checkpoint.
//		
//		

#include "IOPlatformPluginDefs.h"
#include "IOPlatformPluginSymbols.h"
#include "IOPlatformCtrlLoop.h"

#define super OSObject
OSDefineMetaClassAndStructors( IOPlatformCtrlLoop, OSObject )

bool IOPlatformCtrlLoop::init( void )
{
	if (!super::init()) return(false);

	// allocate the info dictionary
	if ((infoDict = OSDictionary::withCapacity(1)) == NULL) return(false);

	controls = NULL;
	sensors = NULL;

	// zero the deadline
	AbsoluteTime_to_scalar(&deadline) = 0;

	return true;
}

void IOPlatformCtrlLoop::free( void )
{
	if (infoDict) { infoDict->release(); infoDict = NULL; }
	if (controls) { controls->release(); controls = NULL; }
	if (sensors) { sensors->release(); sensors = NULL; }

	super::free();
}

IOReturn IOPlatformCtrlLoop::initPlatformCtrlLoop(const OSDictionary *dict)
{
	const OSArray * array;
	const OSString * string;
	const OSNumber * number;

	if ( !dict || !init() ) return(kIOReturnBadArgument);

	// id
	if ((number = OSDynamicCast(OSNumber, dict->getObject(gIOPPluginCtrlLoopIDKey))) != NULL)
	{
		//SENSOR_DLOG("IOPlatformSensor::initPlatformSensor Sensor ID %08lX\n", number->unsigned32BitValue());
		infoDict->setObject(gIOPPluginCtrlLoopIDKey, number);
	}
	else
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::initPlatformCtrlLoop Invalid Thermal Profile omits CtrlLoop ID\n");
		return(kIOReturnBadArgument);
	}

	// read the description
	if ((string = OSDynamicCast(OSString, dict->getObject(kIOPPluginThermalGenericDescKey))) != NULL)
	{
		//CTRLLOOP_DLOG("IOPlatformCtrlLoop::initPlatformCtrlLoop found %s\n", string->getCStringNoCopy());
		infoDict->setObject(kIOPPluginThermalGenericDescKey, string);
	}
	else
	{
		//CTRLLOOP_DLOG("IOPlatformCtrlLoop::initPlatformCtrlLoop has no description\n");
		string = OSSymbol::withCString("Unknown Control Loop");
		infoDict->setObject(kIOPPluginThermalGenericDescKey, string);
		string->release();
	}

	// Pull in the meta state format if specified
	if ((string = OSDynamicCast(OSString, dict->getObject("meta-state-format"))) != NULL)
	{
		infoDict->setObject("meta-state-format", string);
	}

	// grab the meta state array
	if ((array = OSDynamicCast(OSArray, dict->getObject(gIOPPluginThermalMetaStatesKey))) != NULL)
	{
		infoDict->setObject(gIOPPluginThermalMetaStatesKey, array);
	}
	else
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::initPlatformCtrlLoop NO META STATES!!\n");
	}

	// set the initial meta-state to zero -- subclasses should change as needed
	setMetaState(gIOPPluginZero);

	ctrlloopState = kIOPCtrlLoopNotReady;

	return(kIOReturnSuccess);
}

void IOPlatformCtrlLoop::didWake( void )
{
	ctrlloopState = kIOPCtrlLoopDidWake;
}

const AbsoluteTime IOPlatformCtrlLoop::getDeadline( void )
{
	return(deadline);
}

void IOPlatformCtrlLoop::deadlinePassed( void )
{
	CTRLLOOP_DLOG("IOPlatformCtrlLoop::deadlinePassed - entered\n");
	// zero the deadline - this will ensure that this routine won't get called again unless some other method sets a deadline.  Subclasses should override this, especially if they need a periodic timer callback
	AbsoluteTime_to_scalar(&deadline) = 0;
}	

OSDictionary * IOPlatformCtrlLoop::getInfoDict( void )
{
	return infoDict;
}

OSNumber *IOPlatformCtrlLoop::getCtrlLoopID( void )
{
	return OSDynamicCast(OSNumber, infoDict->getObject(gIOPPluginCtrlLoopIDKey));
}

OSNumber * IOPlatformCtrlLoop::getMetaState( void )
{
	return OSDynamicCast(OSNumber, infoDict->getObject(gIOPPluginCtrlLoopMetaState));
}

void IOPlatformCtrlLoop::setMetaState( const OSNumber * state )
{
	if (state)
		infoDict->setObject(gIOPPluginCtrlLoopMetaState, state);
}

bool IOPlatformCtrlLoop::updateMetaState( void )
{
	// subclasses need to be taught how to determine metastate from the pluginEnvInfo dict.
	return(false);
}

void IOPlatformCtrlLoop::adjustControls( void )
{
	// subclasses need to override this
	return;
}

/*
SensorValue IOPlatformCtrlLoop::getCurrentSensorValue( void )
{
	IOPlatformSensor * tmpSensor;
	SensorValue maxVal, tmpVal;
	unsigned int index, count;

	tmpVal.sensValue = -1;
	if (!sensors) return(tmpVal);

	// return the max value of all sensors
	count = sensors->getCount();
	for (index = 0; index < count; index++)
	{
		tmpSensor = OSDynamicCast(IOPlatformSensor, sensors->getObject(index));
	
		if (index == 0)
		{
			maxVal = tmpSensor->fetchCurrentSensorValue();
		}
		else
		{
			tmpVal = tmpSensor->fetchCurrentSensorValue();
			if (tmpVal.sensValue > maxVal.sensValue)
				maxVal = tmpVal;
		}
	}

	return(maxVal);
}
*/

void IOPlatformCtrlLoop::sensorRegistered( IOPlatformSensor * aSensor )
{
	// subclasses should use this to figure out when all the dependent sensor/control drivers are present
}

void IOPlatformCtrlLoop::controlRegistered( IOPlatformControl * aControl )
{
	// subclasses should use this to figure out when all the dependent sensor/control drivers are present
}


bool IOPlatformCtrlLoop::addSensor( IOPlatformSensor * aSensor )
{
	if (!aSensor)
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::addSensor bad argument\n");
		return(false);
	}

	if (!sensors)
	{
		sensors = OSArray::withObjects((const OSObject **) &aSensor, 1);
	}
	else
	{
		// Make sure this sensor isn't already listed
		unsigned index, count;
		count = sensors->getCount();
		for (index = 0; index < count; index++)
		{
			if (sensors->getObject(index) == aSensor)
			{
				CTRLLOOP_DLOG("IOPlatformCtrlLoop::addSensor already an input\n");
				return(false);
			}
		}

		sensors->setObject(aSensor);
	}

	if (!aSensor->joinedCtrlLoop(this))
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::addSensor joinedCtrlLoop failed!\n");
	}

	return(true);
}

bool IOPlatformCtrlLoop::removeSensor( IOPlatformSensor * aSensor )
{
	if (!aSensor)
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::removeSensor bad argument\n");
		return(false);
	}

	if (!sensors)
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::removeSensor no sensors\n");
		return(false);
	}

	bool removed = false;
	int index, count;
	count = sensors->getCount();
	for (index = 0; index < count; index++)
	{
		if (sensors->getObject(index) == aSensor)
		{
			sensors->removeObject(index);
			removed = true;
		}
	}

	if (removed)
	{
		aSensor->leftCtrlLoop(this);
	}
	else
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::removeSensor not found\n");
	}

	return(removed);
}

bool IOPlatformCtrlLoop::addControl( IOPlatformControl * aControl )
{
	if (!aControl)
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::addControl bad argument\n");
		return(false);
	}

	if (!controls)
	{
		controls = OSArray::withObjects((const OSObject **) &aControl, 1);
	}
	else
	{
		// Make sure this control isn't already listed
		unsigned index, count;
		count = controls->getCount();
		for (index = 0; index < count; index++)
		{
			if (controls->getObject(index) == aControl)
			{
				CTRLLOOP_DLOG("IOPlatformCtrlLoop::addControl already an output\n");
				return(false);
			}
		}

		controls->setObject(aControl);
	}

	if (!aControl->joinedCtrlLoop(this))
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::addControl joinedCtrlLoop failed!\n");
	}

	return(true);
}

bool IOPlatformCtrlLoop::removeControl( IOPlatformControl * aControl )
{
	if (!aControl)
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::removeControl bad argument\n");
		return(false);
	}

	if (!controls)
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::removeControl no controls\n");
		return(false);
	}

	bool removed = false;
	int index, count;
	count = controls->getCount();
	for (index = 0; index < count; index++)
	{
		if (controls->getObject(index) == aControl)
		{
			controls->removeObject(index);
			removed = true;
		}
	}

	if (removed)
	{
		aControl->leftCtrlLoop(this);
	}
	else
	{
		CTRLLOOP_DLOG("IOPlatformCtrlLoop::removeControl not found\n");
	}

	return(removed);
}
