/*
 * Copyright (c) 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 Apple Computer, Inc.  All rights reserved.
 *
 */

#include "PB5_1_PlatformMonitor.h"

static const OSSymbol 			*gIOPMonPowerStateKey;
static const OSSymbol 			*gIOPMonThermalStateKey;
static const OSSymbol 			*gIOPMonClamshellStateKey;
static const OSSymbol 			*gIOPMonCPUActionKey;
static const OSSymbol 			*gIOPMonGPUActionKey;
	
static const OSSymbol			*gIOPMonState0;
static const OSSymbol			*gIOPMonState1;
static const OSSymbol			*gIOPMonState2;
static const OSSymbol			*gIOPMonState3;
static const OSSymbol			*gIOPMonFull;
static const OSSymbol			*gIOPMonReduced;
static const OSSymbol			*gIOPMonSlow;

static 	IOService			*provider;

// Possible platform actions
static bool actionFullPower ();
static bool actionPower1 ();
static bool actionPower1GPU1 ();
static bool actionPower1GPU2 ();

/*
 * The platformActionGrid, which is platform-dependent, is an n-dimension array, where n corresponds
 * to kMaxSensors.  The depth of each array dimension corresponds to the maxStates value for each sensor.
 * Each element in the array indicates the action to take (one of the above possible platform actions)
 * based on the current overall state of the system.
 *
 * Note that "sensor" can be intrepreted fairly loosely.  For example, user selection of "Highest" vs.
 * "Reduced" processor performance in the Energy Saver preferences is viewed as "sensory input" and
 * is handled in the table accordingly.
 */
static IOPlatformMonitorAction platformActionGrid[kMaxPowerStates][kMaxThermalStates][kNumClamshellStates] =
	{
		{
			{
				actionFullPower,		// kPowerState0 / kThermalState0 / kClamShellStateOpen
				actionPower1			// kPowerState0 / kThermalState0 / kClamShellStateClosed
			},
			{
				actionPower1,			// kPowerState0 / kThermalState1 / kClamShellStateOpen
				actionPower1			// kPowerState0 / kThermalState1 / kClamShellStateClosed
			},
			{
				actionPower1GPU1,		// kPowerState0 / kThermalState2 / kClamShellStateOpen
				actionPower1GPU1		// kPowerState0 / kThermalState2 / kClamShellStateClosed
			},
			{
				actionPower1GPU2,		// kPowerState0 / kThermalState3 / kClamShellStateOpen
				actionPower1GPU2		// kPowerState0 / kThermalState3 / kClamShellStateClosed
			},
		},
		{
			{
				actionPower1,			// kPowerState1 / kThermalState0 / kClamShellStateOpen
				actionPower1			// kPowerState1 / kThermalState0 / kClamShellStateClosed
			},
			{
				actionPower1,			// kPowerState1 / kThermalState1 / kClamShellStateOpen
				actionPower1			// kPowerState1 / kThermalState1 / kClamShellStateClosed
			},
			{
				actionPower1GPU1,		// kPowerState1 / kThermalState2 / kClamShellStateOpen
				actionPower1GPU1		// kPowerState1 / kThermalState2 / kClamShellStateClosed
			},
			{
				actionPower1GPU2,		// kPowerState1 / kThermalState3 / kClamShellStateOpen
				actionPower1GPU2		// kPowerState1 / kThermalState3 / kClamShellStateClosed
			},
		}
	};

/*
 * conSensorArray, like platformActionArray is platform-dependent.  One element for each primary
 * controller/sensor and each must register itself with us before we can use it.
 *
 * conSensorArray is initialized in the start routine.
 */
static ConSensorInfo conSensorArray[kMaxConSensors];

/*
 * The subSensorArray contains information about secondary sensors and maps those sensors into the primary 
 * sensor.  There are kMaxSensorIndex subSensors and each must map to a primary sensor.  On this platform, 
 * all individual sensors are thermal so all map to kThermalSensor, although that may not always be the case.
 * If more than one thermal zone is to be managed then a primary thermal sensor would be created for each 
 * zone and the subSensorArray can be used to map each sensor into a particular zone.
 *
 * subSensorArray is initialized in the start routine.
 */
static ConSensorInfo subSensorArray[kMaxSensorIndex];

/*
 * The thermalThresholdInfoArray is another n-dimensional array detailing threshold and state information
 * for each sensor.  For this platform, an additional factor is clamshell state, so that is one of the
 * dimensions here.
 */
static ThresholdInfo	thermalThresholdInfoArray[kMaxSensorIndex][kNumClamshellStates][kMaxThermalStates] =
{
	{	// Sensor 0
		{	// Clamshell open
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
		{	// Clamshell closed
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
	},
	{	// Sensor 1
		{	// Clamshell open
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
		{	// Clamshell closed
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
	},
	{	// Sensor 2
		{	// Clamshell open
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(73), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(68),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
		{	// Clamshell closed
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(73), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(68),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
	},
	{	// Sensor 3
		{	// Clamshell open
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
		{	// Clamshell closed
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
	},
	{	// Sensor 4
		{	// Clamshell open
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
		{	// Clamshell closed
			//	thresholdLow,			nextStateLow,		thresholdHigh,			nextStateHigh		// currentState
			{	 TEMP_SENSOR_FMT(0),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState1 },	// kThermalState0 
			{	 TEMP_SENSOR_FMT(95),	kThermalState0, 	TEMP_SENSOR_FMT(100), 	kThermalState2 },	// kThermalState1 
			{	 TEMP_SENSOR_FMT(78),	kThermalState1, 	TEMP_SENSOR_FMT(93), 	kThermalState3 },	// kThermalState2 
			{	 TEMP_SENSOR_FMT(88),	kThermalState2, 	TEMP_SENSOR_FMT(117), 	kThermalState3 },	// kThermalState3 
		},
	},
};

#ifndef sub_iokit_graphics
#	define sub_iokit_graphics           err_sub(5)
#endif
#ifndef kIOFBLowPowerAggressiveness
#	define kIOFBLowPowerAggressiveness iokit_family_err(sub_iokit_graphics,1)
#endif

#define super IOPlatformMonitor
OSDefineMetaClassAndStructors(PB5_1_PlatformMonitor, IOPlatformMonitor)

// **********************************************************************************
// actionFullPower
//
// **********************************************************************************
static bool actionFullPower ()
{
	IOService *serv;
	
	// CPU at full power if not already there
	if ((conSensorArray[kCPUController].state != kCPUPowerState0) && 
		(serv = conSensorArray[kCPUController].conSensor)) {
		conSensorArray[kCPUController].state = kCPUPowerState0;
		IOLog ("IOPMon::actionFullPower - sending CPU aggressiveness 2\n");
		serv->setAggressiveness (kPMSetProcessorSpeed, 2);
		provider->setProperty (gIOPMonCPUActionKey, (OSObject *)gIOPMonFull);
	}
	
	// GPU at full power if not already there
	if ((conSensorArray[kGPUController].state != kGPUPowerState0) &&
		(serv = conSensorArray[kGPUController].conSensor)) {
		conSensorArray[kGPUController].state = kGPUPowerState0;
		IOLog ("IOPMon::actionFullPower - sending GPU aggressiveness 0\n");
		serv->setAggressiveness (kIOFBLowPowerAggressiveness, 0);
		provider->setProperty (gIOPMonGPUActionKey, (OSObject *)gIOPMonFull);
	}

	return true;
}

// **********************************************************************************
// actionPower1
//
// **********************************************************************************
static bool actionPower1 ()
{	
	IOService *serv;
	
	
	// Step down CPU if not already slow
	if ((conSensorArray[kCPUController].state != kCPUPowerState1) && 
		(serv = conSensorArray[kCPUController].conSensor)) {
		conSensorArray[kCPUController].state = kCPUPowerState1;
		IOLog ("IOPMon::actionPower1 - sending CPU aggressiveness 3\n");
		serv->setAggressiveness (kPMSetProcessorSpeed, 3);
		provider->setProperty (gIOPMonCPUActionKey, (OSObject *)gIOPMonReduced);
	}
	
	// GPU is still at full power
	if ((conSensorArray[kGPUController].state != kGPUPowerState0) &&
		(serv = conSensorArray[kGPUController].conSensor)) {
		conSensorArray[kGPUController].state = kGPUPowerState0;
		IOLog ("IOPMon::actionPower1 - sending GPU aggressiveness 0\n");
		serv->setAggressiveness (kIOFBLowPowerAggressiveness, 0);
		provider->setProperty (gIOPMonGPUActionKey, (OSObject *)gIOPMonFull);
	}

	return true;
}

// **********************************************************************************
// actionPower1GPU1
//
// **********************************************************************************
static bool actionPower1GPU1 ()
{	
	IOService *serv;
		
	// Step down CPU if not already slow
	if ((conSensorArray[kCPUController].state != kCPUPowerState1) && 
		(serv = conSensorArray[kCPUController].conSensor)) {
		conSensorArray[kCPUController].state = kCPUPowerState1;
		IOLog ("IOPMon::actionPower1GPU1 - CPU sending aggressiveness 3\n");
		serv->setAggressiveness (kPMSetProcessorSpeed, 3);
		provider->setProperty (gIOPMonCPUActionKey, (OSObject *)gIOPMonReduced);
	}
	
	// GPU to reduced power
	if ((conSensorArray[kGPUController].state != kGPUPowerState1) &&
		(serv = conSensorArray[kGPUController].conSensor)) {
		conSensorArray[kGPUController].state = kGPUPowerState1;
		IOLog ("IOPMon::actionPower1GPU1 - sending GPU aggressiveness 1\n");
		serv->setAggressiveness (kIOFBLowPowerAggressiveness, 1);
		provider->setProperty (gIOPMonGPUActionKey, (OSObject *)gIOPMonReduced);
	}

	return true;
}

// **********************************************************************************
// actionPower1GPU2
//
// **********************************************************************************
static bool actionPower1GPU2 ()
{	
	IOService *serv;
	
	// Step down CPU if not already slow
	if ((conSensorArray[kCPUController].state != kCPUPowerState1) && 
		(serv = conSensorArray[kCPUController].conSensor)) {
		conSensorArray[kCPUController].state = kCPUPowerState1;
		IOLog ("IOPMon::actionPower1GPU2 - CPU sending aggressiveness 3\n");
		serv->setAggressiveness (kPMSetProcessorSpeed, 3);
		provider->setProperty (gIOPMonCPUActionKey, (OSObject *)gIOPMonReduced);
	}
	
	// GPU to lowest power
	if ((conSensorArray[kGPUController].state != kGPUPowerState2) &&
		(serv = conSensorArray[kGPUController].conSensor)) {
		conSensorArray[kGPUController].state = kGPUPowerState2;
		IOLog ("IOPMon::actionPower1GPU2 - sending GPU aggressiveness 2\n");
		serv->setAggressiveness (kIOFBLowPowerAggressiveness, 2);
		provider->setProperty (gIOPMonGPUActionKey, (OSObject *)gIOPMonSlow);
	}

	return true;
}

// **********************************************************************************
// start
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::start ( IOService * nub )
{
	UInt32 i;
	
	if (!initSymbols())
		return false;
		
	if (!initPlatformState ())
		return false;
	
	provider = nub;
	
	commandGateCaller = &iopmonCommandGateCaller;	// Inform superclass about our caller
	
	// Initialize our controller/sensor types (platform-dependent)
	// Primary sensors
	conSensorArray[kPowerSensor].conSensorType = kIOPMonPowerSensor;			// platform power monitor
	conSensorArray[kPowerSensor].conSensor = this;						// platform power monitor
	conSensorArray[kPowerSensor].numStates = kMaxPowerStates;
	conSensorArray[kPowerSensor].sensorValid = true;
	conSensorArray[kPowerSensor].registered = true;						// built-in
	
	conSensorArray[kThermalSensor].conSensorType = kIOPMonThermalSensor;			// primary thermal sensor
	conSensorArray[kThermalSensor].conSensor = this;
	conSensorArray[kThermalSensor].numStates = kMaxThermalStates;
	conSensorArray[kThermalSensor].sensorValid = true;
	conSensorArray[kThermalSensor].registered = true;					// built-in aggregate sensor
	
	conSensorArray[kClamshellSensor].conSensorType = kIOPMonClamshellSensor;		// pmu clamshell sensor
	conSensorArray[kClamshellSensor].numStates = kNumClamshellStates;
	conSensorArray[kClamshellSensor].sensorValid = true;
	conSensorArray[kClamshellSensor].registered = false;

	// Controllers
	conSensorArray[kCPUController].conSensorType = kIOPMonCPUController;			// cpu controller
	conSensorArray[kCPUController].registered = false;

	conSensorArray[kGPUController].conSensorType = kIOPMonGPUController;			// gpu controller
	conSensorArray[kGPUController].state = kGPUPowerState0;
	conSensorArray[kGPUController].registered = true;					// built-in
	
	// Subsensors (all are thermal sensors)
	for (i = 0; i < kMaxSensorIndex; i++) {
		subSensorArray[i].conSensorType = kIOPMonThermalSensor;
		subSensorArray[i].conSensorParent = kThermalSensor;				// Index into primary array of our parent
		subSensorArray[i].numStates = kMaxThermalStates;
		// If a sensor is not to be used, set sensorValid false;
		subSensorArray[i].sensorValid = true;
		subSensorArray[i].registered = false;
	}
	
	if (!(dictPowerLow = OSDictionary::withCapacity (3)))
		return false;
	
	if (!(dictPowerHigh = OSDictionary::withCapacity (3)))
		return false;
	
	if (!(dictClamshellOpen = OSDictionary::withCapacity (2)))
		return false;
	
	if (!(dictClamshellClosed = OSDictionary::withCapacity (2)))
		return false;
	
	// On platforms with more than one CPU these dictionaries must accurately reflect which cpu is involved
	dictPowerLow->setObject (gIOPMonCPUIDKey, OSNumber::withNumber ((unsigned long long)0, 32));
	dictPowerLow->setObject (gIOPMonCurrentValueKey, OSNumber::withNumber ((unsigned long long)kPowerState1, 32));
	dictPowerLow->setObject (gIOPMonTypeKey, gIOPMonTypePowerSens);
	
	dictPowerHigh->setObject (gIOPMonCPUIDKey, OSNumber::withNumber ((unsigned long long)0, 32));
	dictPowerHigh->setObject (gIOPMonCurrentValueKey, OSNumber::withNumber ((unsigned long long)kPowerState0, 32));
	dictPowerHigh->setObject (gIOPMonTypeKey, gIOPMonTypePowerSens);
		
	dictClamshellOpen->setObject (gIOPMonCurrentValueKey, OSNumber::withNumber ((unsigned long long)kClamshellStateOpen, 32));
	dictClamshellOpen->setObject (gIOPMonTypeKey, gIOPMonTypeClamshellSens);
		
	dictClamshellClosed->setObject (gIOPMonCurrentValueKey, OSNumber::withNumber ((unsigned long long)kClamshellStateClosed, 32));
	dictClamshellClosed->setObject (gIOPMonTypeKey, gIOPMonTypeClamshellSens);
	
	if (!gIOPMonPowerStateKey)
		gIOPMonPowerStateKey 				= OSSymbol::withCString (kIOPMonPowerStateKey);
	if (!gIOPMonThermalStateKey)
		gIOPMonThermalStateKey 				= OSSymbol::withCString (kIOPMonThermalStateKey);
	if (!gIOPMonClamshellStateKey)
		gIOPMonClamshellStateKey 			= OSSymbol::withCString (kIOPMonClamshellStateKey);

	if (!gIOPMonCPUActionKey)
		gIOPMonCPUActionKey 				= OSSymbol::withCString (kIOPMonCPUActionKey);
	if (!gIOPMonGPUActionKey)
		gIOPMonGPUActionKey 				= OSSymbol::withCString (kIOPMonGPUActionKey);

	if (!gIOPMonState0)
		gIOPMonState0 						= OSSymbol::withCString (kIOPMonState0);
	if (!gIOPMonState1)
		gIOPMonState1 						= OSSymbol::withCString (kIOPMonState1);
	if (!gIOPMonState2)
		gIOPMonState2 						= OSSymbol::withCString (kIOPMonState2);
	if (!gIOPMonState3)
		gIOPMonState3 						= OSSymbol::withCString (kIOPMonState3);

	if (!gIOPMonFull)
		gIOPMonFull 						= OSSymbol::withCString (kIOPMonFull);
	if (!gIOPMonReduced)
		gIOPMonReduced 						= OSSymbol::withCString (kIOPMonReduced);
	if (!gIOPMonSlow)
		gIOPMonSlow 						= OSSymbol::withCString (kIOPMonSlow);

	lastPowerState = kMaxPowerStates;
	lastThermalState = kMaxThermalStates;
	lastClamshellState = kNumClamshellStates;
	
	// Put initial state and action info into the IORegistry
	updateIOPMonStateInfo(kIOPMonPowerSensor, currentPowerState);
	updateIOPMonStateInfo(kIOPMonThermalSensor, currentThermalState);
	updateIOPMonStateInfo(kIOPMonClamshellSensor, currentClamshellState);
	provider->setProperty (gIOPMonCPUActionKey, (OSObject *)gIOPMonReduced);
	provider->setProperty (gIOPMonGPUActionKey, (OSObject *)gIOPMonFull);

	// Let the world know we're open for business
	publishResource ("IOPlatformMonitor", this);

	if (super::start (nub)) {
		// pmRootDomain (found by our parent) is the recipient of GPU controller messages
		conSensorArray[kGPUController].conSensor = pmRootDomain;
		return true;
	} else
		return false;
}

// **********************************************************************************
// powerStateWillChangeTo
//
// **********************************************************************************
IOReturn PB5_1_PlatformMonitor::powerStateWillChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService*)
{	
    if ( ! (theFlags & IOPMPowerOn) ) {
        // Sleep sequence:
		IOLog ("PB5_1_PlatformMonitor::powerStateWillChangeTo - sleep\n");
		savePlatformState();

    }
    
    return IOPMAckImplied;
}

// **********************************************************************************
// powerStateDidChangeTo
//
// **********************************************************************************
IOReturn PB5_1_PlatformMonitor::powerStateDidChangeTo (IOPMPowerFlags theFlags, unsigned long, IOService*)
{	
    if (theFlags & IOPMPowerOn) {
        // Wake sequence:
		IOLog ("PB5_1_PlatformMonitor::powerStateDidChangeTo - wake\n");
		restorePlatformState();
    } 
    
    return IOPMAckImplied;
}

// **********************************************************************************
// setAggressiveness
//
// setAggressiveness is called by the Power Manager to change our power state
// It is most commonly called when the user changes the Energy Saver preferences
// to change the power state but it can be called for other reasons.
//
// One of these reasons is a forced-reduced-speed condition but we now detect and
// act on this condition immediately (through a clamshell event) so by the time the
// PM calls us here we have already altered the condition and there is nothing to do.
//
// **********************************************************************************
IOReturn PB5_1_PlatformMonitor::setAggressiveness(unsigned long selector, unsigned long newLevel)
{
	IOPMonEventData 	event;
	IOReturn			result;
	    
    result = super::setAggressiveness(selector, newLevel);

	if (selector == kPMSetProcessorSpeed) {
		IOLog ("PB5_1_PlatformMonitor::setAggressiveness - newLevel %ld\n", newLevel);
		if (newLevel != currentPowerState) {	// This only works if we have two power states
			// create and transmit internal event
			event.event = kIOPMonMessageStateChanged;
			event.conSensor = this;
			event.eventDict = (newLevel == 0) ? dictPowerHigh : dictPowerLow;
	
			// send it
			handleEvent (&event);
		}
	}
	
	return result;
}

// **********************************************************************************
// initPowerState
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::initPowerState ()
{
	currentPowerState = kPowerState0;			// We will have booted fast
	return true;
}

// **********************************************************************************
// savePowerState
//
// **********************************************************************************
void PB5_1_PlatformMonitor::savePowerState ()
{
	return;			// Currently nothing to do
}

// **********************************************************************************
// restorePowerState
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::restorePowerState ()
{
	IOService *serv;

	// This platform is always fast coming out of sleep, so if our current power
	// state is slow (kPowerState1) return true to force an update.
	conSensorArray[kCPUController].state = kCPUPowerState0;	// Set controller state
        
        // This ensures that the CPU speed and L3 are in sync, its possible that the L3 was disabled
        // previously which means when we wake it will still be disabled which would not be in sync
        // with the CPU speed (fast on wake)
        serv = conSensorArray[kCPUController].conSensor;
        IOLog ("IOPMon::actionFullPower - sending CPU aggressiveness 2\n");
        serv->setAggressiveness (kPMSetProcessorSpeed, 2);
        
	return (currentPowerState == kPowerState1);	
}
	
// **********************************************************************************
// initThermalState
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::initThermalState ()
{
	currentThermalState = kThermalState0;
	return true;
}

// **********************************************************************************
// saveThermalState
//
// **********************************************************************************
void PB5_1_PlatformMonitor::saveThermalState ()
{
	return;	// Nothing to do
}

// **********************************************************************************
// restoreThermalState
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::restoreThermalState ()
{
	OSNumber			*threshLow, *threshHigh;
	UInt32				i;

	// need to get updated info from sensors
	for (i = 0; i < kMaxSensorIndex; i++) {
		if (subSensorArray[i].registered) { // Is sensor registered?
			subSensorArray[i].state = kMaxThermalStates;	//Set indeterminate state
			
			// Set low thresholds - this will cause sensor to update info
			threshLow = (OSNumber *)subSensorArray[i].threshDict->getObject (gIOPMonLowThresholdKey);
			threshLow->setValue((long long)0);
			threshHigh = (OSNumber *)subSensorArray[i].threshDict->getObject (gIOPMonHighThresholdKey);
			threshHigh->setValue((long long)0);
			// Send thresholds to sensor
			subSensorArray[i].conSensor->setProperties (subSensorArray[i].threshDict);
		}
	}
	
	/*
	 * This routine returns false because we don't want to force an adjust of the platform
	 * state just yet.  That will happen automatically when the sensors respond to the low
	 * thresholds
	 */
	return false;
}

// **********************************************************************************
// initClamshellState
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::initClamshellState ()
{
	currentClamshellState = kClamshellStateOpen;
	
	return true;
}

// **********************************************************************************
// saveClamshellState
//
// **********************************************************************************
void PB5_1_PlatformMonitor::saveClamshellState ()
{
	return;				// Nothing to do
}

// **********************************************************************************
// restoreClamshellState
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::restoreClamshellState ()
{
	// Currently nothing to do.  
	// We'll get the right state next time monitor power is called
	return false;
}

// **********************************************************************************
// monitorPower
//
// **********************************************************************************
IOReturn PB5_1_PlatformMonitor::monitorPower (OSDictionary *dict, IOService *provider)
{
	UInt32 				type, index, subIndex, value;
	OSNumber			*num;
	    
	if (lookupConSensorInfo (dict, provider, &type, &index, &subIndex)) {
		// See if any low power conditions exist.
		if (num = OSDynamicCast (OSNumber, dict->getObject (gIOPMonCurrentValueKey))) {
			value = num->unsigned32BitValue();
			value &= ~kIOPMForceLowSpeed;  		// Clear low speed bit
                        
                        // Check if we have the correct adapter plugged in. The high byte of "value" contains the wattage of the adapter
                        // Set the "I have the wrong adapter plugged in" bit if we have less than a 65 Watt adapter plugged in.

// Setting this bit causes the battery monitor to put a dialog indicating to the user that they have inserted the 
// wrong adapter.  Marketing has decided they no longer like this dialog, so I'm commenting out this code because
// I'm sure someone somewhere will change they're mind and want this back....
//                        if ((((value & 0xFF000000) >> 24) > 0) && (((value & 0xFF000000) >> 24) < 0x41))
//                            value |= 1<<11;
//                        else
//                            value &= ~(1<<11);
			
			num->setValue((long long)value);		// Send updated value back
                }
	}
	return kIOReturnBadArgument;
}

// **********************************************************************************
// updateIOPMonStateInfo
//
// **********************************************************************************
void PB5_1_PlatformMonitor::updateIOPMonStateInfo (UInt32 type, UInt32 state)
{
	const OSSymbol		*stateKey, *stateValue;
	
	stateKey = stateValue = NULL;
	
	if (type == kIOPMonPowerSensor)
		stateKey = gIOPMonPowerStateKey;
	else if (type == kIOPMonThermalSensor)
		stateKey = gIOPMonThermalStateKey;
	else if (type == kIOPMonClamshellSensor)
		stateKey = gIOPMonClamshellStateKey;

	// Thermal has the most states so we use that to look up state values
	if (state == kThermalState0)
		stateValue = gIOPMonState0;
	else if (state == kThermalState1)
		stateValue = gIOPMonState1;
	else if (state == kThermalState2)
		stateValue = gIOPMonState2;
	else if (state == kThermalState3)
		stateValue = gIOPMonState3;

	if (stateKey && stateValue)
		provider->setProperty (stateKey, (OSObject *)stateValue);
	
	return;
}

// **********************************************************************************
// initPlatformState
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::initPlatformState ()
{
	if (initPowerState() &&
		initThermalState() &&
		initClamshellState())
		return true;
	else
		return false;
}

// **********************************************************************************
// savePlatformState
//
//		-- protected by CommandGate - call via IOPlatformMonitor::savePlatformState
//
// **********************************************************************************
void PB5_1_PlatformMonitor::savePlatformState ()
{
	savePowerState();
	saveThermalState();
	saveClamshellState();
	
	return;
}

// **********************************************************************************
// restorePlatformState
//
//		-- protected by CommandGate - call via IOPlatformMonitor::restorePlatformState
//
// **********************************************************************************
void PB5_1_PlatformMonitor::restorePlatformState ()
{
	bool doAdjust;
	
	// Last states are indeterminate
	lastPowerState = kMaxPowerStates;
	lastThermalState = kMaxThermalStates;
	lastClamshellState = kNumClamshellStates;

	// If restoration of any of the states requires action (i.e., if they returned
	// true), force an update
	doAdjust = restorePowerState();
	doAdjust |= restoreThermalState();
	doAdjust |= restoreClamshellState();
	if (doAdjust) {
		lastAction = 0;		// Force update
		adjustPlatformState ();
	}
	
	return;
}

// **********************************************************************************
// adjustPlatformState
//
// This gets called whenever there is a possible state change.  A state change doesn't
// imply that you always have to take an action - in fact, most of the time you don't -
// so only if the action we should take is different from the last action we took do
// we do something.
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::adjustPlatformState ()
{
	IOPlatformMonitorAction		actionToTake;
	bool						result = true;
	
	IOLog ("PB5_1_PlatformMonitor::adjustPlatformState - entered, cps %ld, cts %ld, ccs %ld\n",
		currentPowerState, currentThermalState, currentClamshellState);
	// Look up action to take for current state
	actionToTake = platformActionGrid[currentPowerState][currentThermalState][currentClamshellState];
	
	// Current state becomes the last state, update info
	if (lastPowerState != currentPowerState) {
		lastPowerState = currentPowerState;
		updateIOPMonStateInfo(kIOPMonPowerSensor, currentPowerState);
	}
	if (lastThermalState != currentThermalState) {
                lastAction = 0;
		lastThermalState = currentThermalState;
		updateIOPMonStateInfo(kIOPMonThermalSensor, currentThermalState);
	}
	if (lastClamshellState != currentClamshellState) {
		lastClamshellState = currentClamshellState;
		updateIOPMonStateInfo(kIOPMonClamshellSensor, currentClamshellState);
	}
	
	if (actionToTake != lastAction) {
		result = (*actionToTake)();		// Do it
		lastAction = actionToTake;		// Remember what it was we did
	}
	
	return result;
	
}


// **********************************************************************************
// registerConSensor
//
// **********************************************************************************
IOReturn PB5_1_PlatformMonitor::registerConSensor (OSDictionary *dict, IOService *conSensor)
{
	UInt32 				csi, subsi, type, initialState, initialValue;
	ConSensorInfo		*csInfo;
		
	if (!lookupConSensorInfo (dict, conSensor, &type, &csi, &subsi))
		return kIOReturnBadArgument;
		
	IOLog ("PB5_1_PlatformMonitor::registerConSensor - type %ld, csi %ld, subsi %ld\n", type, csi, subsi);
	
	if (subsi < kMaxSensorIndex)	// Is subsensor index valid? If so use subSensorArray
		csInfo = &subSensorArray[subsi];
	else
		csInfo = &conSensorArray[csi];
	csInfo->conSensor = conSensor;
	csInfo->dict = dict;
	dict->retain ();
	
	initialState = initialValue = 0;
	// type dependent initialization
	switch (csInfo->conSensorType) {
		case kIOPMonThermalSensor:
			/*
			 * Thermal sensors get aggregated through the subSensorArray.  The main thermal sensor
			 * entry in conSensorArray only tracks the overall state
			 */
			if (!csInfo->sensorValid)
				return kIOReturnUnsupported;		// Don't need this sensor - tell it to go away
				
			//if (!retrieveCurrentValue (dict, &initialThermalValue))
			//	return kIOReturnBadArgument;
			
			// Figure out our initial state
                        // Just initialize the initial state/initial value to 0 for now and let the normal HandleThermalEvent get called if it needs to later...
			//initialState = lookupThermalStateFromValue (subsi, initialThermalValue);
			
			if (!(csInfo->threshDict = OSDictionary::withCapacity(3)))
				return kIOReturnNoMemory;
				
			csInfo->threshDict->setObject (gIOPMonIDKey, OSNumber::withNumber (subsi, 32));
			csInfo->threshDict->setObject (gIOPMonLowThresholdKey, 
				OSNumber::withNumber (thermalThresholdInfoArray[subsi][currentClamshellState][initialState].thresholdLow, 32));
			csInfo->threshDict->setObject (gIOPMonHighThresholdKey, 
				OSNumber::withNumber (thermalThresholdInfoArray[subsi][currentClamshellState][initialState].thresholdHigh, 32));
			
			// Send thresholds to sensor
			conSensor->setProperties (csInfo->threshDict);
			
			csInfo->registered = true;
			break;
		
		case kIOPMonPowerSensor:
			initialState = kPowerState0;			// Booted fast
			break;
		
		case kIOPMonClamshellSensor:
			if (!retrieveCurrentValue (dict, &initialValue))
				return kIOReturnBadArgument;

			csInfo->registered = true;
			break;
		
		case kIOPMonCPUController:
			initialState = kCPUPowerState0;			// Booted fast
			csInfo->registered = true;
			break;
		
		case kIOPMonGPUController:
			initialState = kGPUPowerState0;			// GPU starts out fast
			break;
		
		default:
			break;
	}
	
	csInfo->value = initialValue;
	csInfo->state = initialState;

	return kIOReturnSuccess;
}

// **********************************************************************************
// unregisterSensor
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::unregisterSensor (UInt32 sensorID)
{
	if (sensorID >= kMaxSensors)
		return false;
	
	conSensorArray[sensorID].conSensor = NULL;
	return true;
}

// **********************************************************************************
// lookupConSensorInfo
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::lookupConSensorInfo (OSDictionary *dict, IOService *conSensor, 
	UInt32 *type, UInt32 *index, UInt32 *subIndex)
{
	OSSymbol	*typeString;
	UInt32		tempType;
	
	OSObject				*obj;
	
	*index = kMaxConSensors;					// assume not found
	*subIndex = kMaxSensorIndex;				// assume not found
	// See if we have a type
	*type = tempType = kIOPMonUnknownSensor;	// assume not
	obj = dict->getObject (gIOPMonTypeKey);
	if (obj && (typeString = OSDynamicCast (OSSymbol, obj))) {
	//if (typeString = OSDynamicCast (OSSymbol, dict->getObject (gIOPMonTypeKey))) {
		if (typeString->isEqualTo (gIOPMonTypePowerSens))
			tempType = kIOPMonPowerSensor;
		else if (typeString->isEqualTo (gIOPMonTypeThermalSens))
			tempType = kIOPMonThermalSensor;
		else if (typeString->isEqualTo (gIOPMonTypeClamshellSens))
			tempType = kIOPMonClamshellSensor;
		else if (typeString->isEqualTo (gIOPMonTypeCPUCon))
			tempType = kIOPMonCPUController;
		else if (typeString->isEqualTo (gIOPMonTypeGPUCon))
			tempType = kIOPMonGPUController;
		else if (typeString->isEqualTo (gIOPMonTypeSlewCon))
			tempType = kIOPMonSlewController;
		else if (typeString->isEqualTo (gIOPMonTypeFanCon))
			tempType = kIOPMonFanController;
	} 
		
	if (retrieveSensorIndex (dict, subIndex)) {
		if (*subIndex >= kMaxSensorIndex)
			return false;
			
		// We're dealing with a subSensor, so set the type and validate it in the subSensor array by index
		*type = (tempType == kIOPMonUnknownSensor) ? subSensorArray[*subIndex].conSensorType : tempType;
			
		return true;
	}
	
	/*
	 * Treat anything else as a primary sensor.
	 * For this platform there is only one of each primary controller/sensor so if we find the type
	 * we have the index.
	 */
	for (*index = 0; *index < kMaxConSensors; (*index)++) {
		if (conSensorArray[*index].conSensorType == tempType) {
			*type = tempType;
			break;
		}
	}
	
	return (*index < kMaxConSensors);
}

// **********************************************************************************
// lookupThermalStateFromValue
//
// **********************************************************************************
UInt32 PB5_1_PlatformMonitor::lookupThermalStateFromValue (UInt32 sensorIndex, ThermalValue value)
{
	UInt32 i;
	
	if (value > thermalThresholdInfoArray[sensorIndex][currentClamshellState][0].thresholdLow) {
		for (i = 0 ; i < subSensorArray[sensorIndex].numStates; i++)
			if (value > thermalThresholdInfoArray[sensorIndex][currentClamshellState][i].thresholdLow &&
				value < thermalThresholdInfoArray[sensorIndex][currentClamshellState][i].thresholdHigh)
					return i;

		// This is bad as sensor's already over the limit - need to figure right response
		IOLog ("PB5_1_PlatformMonitor::lookupStateFromValue - sensor %ld over limit\n", sensorIndex);
		return kMaxSensorIndex;
	}
	
	// Safely below the lowest threshold
	return kThermalState0;
}

// **********************************************************************************
// handlePowerEvent
//
//		-- protected by CommandGate - call through handleEvent
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::handlePowerEvent (IOPMonEventData *eventData)
{
	UInt32		nextPowerState;
	bool		result;

	if (!(retrieveCurrentValue (eventData->eventDict, &nextPowerState)))
		return false;
		
	result = true;
	if (currentPowerState != nextPowerState) {
		currentPowerState = nextPowerState;
		result = adjustPlatformState ();
	}
	
	return result;
}

// **********************************************************************************
// handleThermalEvent
//
//		-- protected by CommandGate - call through handleEvent
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::handleThermalEvent (IOPMonEventData *eventData)
{
	UInt32					type, csi, subsi, myThermalState;
	ThermalValue			value;
	OSNumber				*threshLow, *threshHigh;
	bool					result;

	result = true; 
	if (!(retrieveCurrentValue (eventData->eventDict, &value)))
		return false;
	
	switch (eventData->event) {		
		case kIOPMonMessageLowThresholdHit:		
		case kIOPMonMessageHighThresholdHit:
			if (lookupConSensorInfo (eventData->eventDict, eventData->conSensor, &type, &csi, &subsi) &&
				subSensorArray[subsi].registered) {
				myThermalState = lookupThermalStateFromValue (subsi, value);
				if (myThermalState >= kMaxSensorIndex)
					result = false;
			} else
				result = false;
			break;
				
		default:
			IOLog ("PB5_1_PlatformMonitor::handleThermalEvent - event %ld not handled\n", 
				eventData->event);
			result = false;
			break;
	}
	
	if (result) {
		if (myThermalState != subSensorArray[subsi].state) {
			subSensorArray[subsi].state = myThermalState;
			if (!subSensorArray[subsi].registered) 
				// Not registered
				return false;
			
			threshLow = (OSNumber *)subSensorArray[subsi].threshDict->getObject (gIOPMonLowThresholdKey);
			threshLow->setValue((long long)thermalThresholdInfoArray[subsi][currentClamshellState][myThermalState].thresholdLow);
			threshHigh = (OSNumber *)subSensorArray[subsi].threshDict->getObject (gIOPMonHighThresholdKey);
			threshHigh->setValue((long long)thermalThresholdInfoArray[subsi][currentClamshellState][myThermalState].thresholdHigh);
			// Send thresholds to sensor
			subSensorArray[subsi].conSensor->setProperties (subSensorArray[subsi].threshDict);
		}
		
		if (currentThermalState != myThermalState) {
			UInt32 i, maxState;
			
			// Find the current max state among all the sensors
			maxState = 0;
			for (i = 0; i < kMaxSensorIndex; i++) 
				if (subSensorArray[i].registered)
					maxState = (maxState > subSensorArray[i].state) ? maxState : subSensorArray[i].state;
			
			// If new max state is different than current, update platform state
			if (currentThermalState != maxState) {
				currentThermalState = maxState;
				result = adjustPlatformState ();
			}
		}
	}
	
	return result;
}

// **********************************************************************************
// handleClamshellEvent
//
//		-- protected by CommandGate - call through handleEvent
//
// **********************************************************************************
bool PB5_1_PlatformMonitor::handleClamshellEvent (IOPMonEventData *eventData)
{
	UInt32		nextClamshellState;
	bool		result;

	if (!(retrieveCurrentValue (eventData->eventDict, &nextClamshellState)))
		return false;
		
	result = true;
	if (currentClamshellState != nextClamshellState) {
		currentClamshellState = nextClamshellState;
		result = adjustPlatformState ();
	}
	
	return result;
}

// **********************************************************************************
// iopmonCommandGateCaller - invoked by commandGate->runCommand
//
//		This is single threaded!!
//
// **********************************************************************************
//static
IOReturn PB5_1_PlatformMonitor::iopmonCommandGateCaller(OSObject *object, void *arg0, void *arg1, void *arg2, void *arg3)
{
	PB5_1_PlatformMonitor	*me;
	IOPMonCommandThreadSet	*commandSet;
	UInt32					type, conSensorID, subSensorID;
	bool					result;
	
	// Pull our event data and, since we're a static function, a reference to our object
	// out of the parameters
	if (((commandSet = (IOPMonCommandThreadSet *)arg0) == NULL) ||
		((me = OSDynamicCast(PB5_1_PlatformMonitor, commandSet->me)) == NULL))
		return kIOReturnError;
	
	result = true; 

	switch (commandSet->command) {
		case kIOPMonCommandHandleEvent:
			if (!(commandSet->eventData.eventDict)) {
				IOLog ("PB5_1_PlatformMonitor::iopmonCommandGateCaller - bad dictionary\n");
				return kIOReturnBadArgument;
			}
			if (!(commandSet->eventData.conSensor)) {
				IOLog ("PB5_1_PlatformMonitor::iopmonCommandGateCaller - bad conSensor\n");
				return kIOReturnBadArgument;
			}
			if (!me->lookupConSensorInfo (commandSet->eventData.eventDict, commandSet->eventData.conSensor, 
				&type, &conSensorID, &subSensorID)) {
				IOLog ("PB5_1_PlatformMonitor::iopmonCommandGateCaller - bad sensor info lookup\n");
				return kIOReturnBadArgument;
			}
			
			switch (type) {
				case kIOPMonPowerSensor:			// platform power monitor
					result = me->handlePowerEvent (&commandSet->eventData);
					break;
				case kIOPMonThermalSensor:			// zone 1 thermal sensor
					result = me->handleThermalEvent (&commandSet->eventData);
					break;
				case kIOPMonClamshellSensor:		// pmu clamshell sensor
					result = me->handleClamshellEvent (&commandSet->eventData);
					break;
				
				default:
					IOLog ("PB5_1_PlatformMonitor::iopmonCommandGateCaller -  bad sensorType(%ld), sensorID(%ld), subsi(%ld)\n", 
						type, conSensorID, subSensorID);
					result = false;
					break;
			}
			break;
		
		case kIOPMonCommandSaveState:
			me->savePlatformState ();
			break;
			
		case kIOPMonCommandRestoreState:
			me->restorePlatformState ();
			break;
		
		default:
			IOLog ("PB5_1_PlatformMonitor::iopmonCommandGateCaller - bad command %ld\n", 
				commandSet->command);
			result = false;
			break;
	}			
	
	return result ? kIOReturnSuccess : kIOReturnError;
}
