// VT1103.cpp : Defines the initialization routines for the DLL.
//
// created by Unwinder
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"

#include <float.h>
#include <afxdllx.h>
#include <shlwapi.h>

#include "Context.h"
#include "VT1103.h"
#include "RivaTunerExports.h"
#include "MonitoringSourceDesc.h"
/////////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
static AFX_EXTENSION_MODULE VT1103DLL = { NULL, NULL };
/////////////////////////////////////////////////////////////////////////////
// define Volterra VRM device IDs
/////////////////////////////////////////////////////////////////////////////
#define VT1103_DEVICE_ID												0x07
#define VT1105_DEVICE_ID												0x08
#define VT1165_DEVICE_ID												0x0A
/////////////////////////////////////////////////////////////////////////////
#define MAX_GPU															16
/////////////////////////////////////////////////////////////////////////////

// define and init global variables

LOCALIZEWND_PROC			g_pLocalizeWnd						= NULL;
LOCALIZESTR_PROC			g_pLocalizeStr						= NULL;

GET_DEVICE_ID_EX_PROC		g_pGetDeviceIDEx					= NULL;
GET_I2C_BUS_NUM_EX_PROC		g_pGetI2CBusNumEx					= NULL;
READ_I2C_EX_PROC			g_pReadI2CEx						= NULL;
WRITE_I2C_EX_PROC			g_pWriteI2CEx						= NULL;

CContext					g_context[MAX_GPU];

char						g_szVT1103[]						= "VT1103";
char						g_szVT1105[]						= "VT1105";
char						g_szVT1165[]						= "VT1165";
char						g_szVTxxxx[]						= "VTxxxx";

HINSTANCE					g_hModule							= 0;
/////////////////////////////////////////////////////////////////////////////
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
	UNREFERENCED_PARAMETER(lpReserved);

	if (dwReason == DLL_PROCESS_ATTACH)
	{
		if (!AfxInitExtensionModule(VT1103DLL, hInstance))
			return 0;

		new CDynLinkLibrary(VT1103DLL);

		g_hModule = hInstance;
	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
		AfxTermExtensionModule(VT1103DLL);
	}
	return 1;
}
//////////////////////////////////////////////////////////////////////
// This helper function returns VRM name
//////////////////////////////////////////////////////////////////////
LPCSTR GetVRMName(DWORD dwGpu)
{
	switch (g_context[dwGpu].m_devID)
	{
	case VT1103_DEVICE_ID:
		return g_szVT1103;
	case VT1105_DEVICE_ID:
		return g_szVT1105;
	case VT1165_DEVICE_ID:
		return g_szVT1165;
	}

	return g_szVTxxxx;
}
/////////////////////////////////////////////////////////////////////////////
// This helper function rips required string param from specified
//<Param0><Separator><Param1><Separator>...<ParamN>-styled string
/////////////////////////////////////////////////////////////////////////////
BOOL GetParam(int iParam, LPCSTR lpSrc, CString &strParam, LPCSTR lpSeparator)
{
	CString strSrc = lpSrc;

	int iFirst = 0;

	while (iParam)
	{
		iFirst = strSrc.Find(lpSeparator, iFirst);

		if (iFirst == -1)
			return FALSE;

		iFirst++;
		iParam--;
	}

	int iLast = strSrc.Find(lpSeparator ,iFirst);

	if (iLast == -1)
		iLast = strlen(lpSrc);

	strParam = strSrc.Mid(iFirst, iLast - iFirst);

	strParam.TrimRight();
	strParam.TrimLeft();

	return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// This helper function compares two strings using whildcard character
/////////////////////////////////////////////////////////////////////////////
int CompareWithWildcard(LPCSTR str0, LPCSTR str1, char wildcard)
{
	int nLen0	= strlen(str0);
	int nLen1	= strlen(str1);

	if (nLen0 > nLen1)
		return 1;

	if (nLen0 < nLen1)
		return -1;

	for (int i=0; i<nLen0; i++)
	{
		char c0 = str0[i];
		char c1 = str1[i];

		if ((c0 != wildcard) &&
			(c1 != wildcard))
			if (c0 != c1)
				return (c0 > c1) ? 1 : -1;
	}

	return 0;
}
/////////////////////////////////////////////////////////////////////////////
// This helper function reads the first matching entry for the currently
// selected display adapter from the specified section of .cfg
//
// Note: the entries in .cfg can be specified with wildcard character '?'
/////////////////////////////////////////////////////////////////////////////
CString GetMatchingHardwareEntry(DWORD dwGpu, LPCSTR lpSection)
{
	char szSection[MAX_PATH];
	sprintf(szSection, "%s%s", lpSection, GetVRMName(dwGpu));
		//format VRM specific section name

	char szCfgPath[MAX_PATH];
	GetModuleFileName(g_hModule, szCfgPath, MAX_PATH);
	
	PathRenameExtension(szCfgPath, ".cfg");
		//get fully qualified path to .cfg

	LPSTR	lpBuf		= new char[32768];
		//allocate buffer for section
	if (GetPrivateProfileSection(szSection, lpBuf, 32768, szCfgPath))
		//try to read section, continue if succeeded
	{
		CString strDeviceID	= g_pGetDeviceIDEx(dwGpu);	
			//get display adapter's PCI DeviceID string

		LPSTR	lpStr	= (LPSTR)lpBuf;
 		int		nLen	= strlen(lpStr);

		CString strLValue, strRValue;

		while (nLen)
			//loop through all section's lines
		{

			if (GetParam(0, lpStr, strLValue, "=") &&
				GetParam(1, lpStr, strRValue, "="))
				//try to rip <LValue>=<RValue>-formatted line and proceed
				//if format is correct
			{
				if (!CompareWithWildcard(strLValue, strDeviceID, '?'))
					//compare LValue with PCI DeviceID string
					//take a note that we'll use wildcards to allow multiple
					//display adapters to be specified with a single .cfg line
				{
					delete [] lpBuf;
						//deallocate buffer we've allocated before

					return strRValue;
				}
			}

			lpStr	+=	nLen + 1;
			nLen	=	strlen(lpStr);
				//proceed with next section's line
		}
	}

	delete [] lpBuf;
		//deallocate buffer we've allocated before

	return "";
		//return empty string if we've failed to find matching hardware entry
}
//////////////////////////////////////////////////////////////////////
// This helper function reads configuration resistor code from ADC
//////////////////////////////////////////////////////////////////////
BYTE GetRADC(DWORD dwGpu, int index)
{
	BYTE data;
	BYTE temp;

	if (g_context[dwGpu].m_rADC[index] != 0xff)
		return g_context[dwGpu].m_rADC[index];

	switch (index)
	{
	case 0:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x02, &data))
			g_context[dwGpu].m_rADC[0] = data & 0x1F;
		break;
	case 1:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x02, &data))
		{
			temp = data>>5;

			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x03, &data))
			{
				g_context[dwGpu].m_rADC[1] = ((data<<3) | temp	) & 0x1F;
				g_context[dwGpu].m_rADC[2] = ((data>>2)			) & 0x1F;
			}
		}
		break;
	case 2:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x03, &data))
			g_context[dwGpu].m_rADC[2] = (data>>2) & 0x1F;
		break;
	case 3:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x03, &data))
		{
			temp = data>>7;

			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x04, &data))
				g_context[dwGpu].m_rADC[3] = ((data<<1) | temp) & 0x1F;
		}
		break;
	case 4:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x04, &data))
		{
			temp = data>>4;

			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x05, &data))
			{
				g_context[dwGpu].m_rADC[4] = ((data<<4) | temp	) & 0x1F;
				g_context[dwGpu].m_rADC[5] = ((data>>1)			) & 0x1F;
			}
		}
		break;
	case 5:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x05, &data))
			g_context[dwGpu].m_rADC[5] = (data>>1) & 0x1F;
		break;
	case 6:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x05, &data))
		{
			temp = data>>6;

			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x06, &data))
			{
				g_context[dwGpu].m_rADC[6] = ((data<<2) | temp	) & 0x1F;
				g_context[dwGpu].m_rADC[7] = ((data>>3)			) & 0x1F;
			}
		}
		break;
	case 7:
		if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x06, &data))
			g_context[dwGpu].m_rADC[7] = (data>>3) & 0x1F;
		break;
	}

	return g_context[dwGpu].m_rADC[index];
}
//////////////////////////////////////////////////////////////////////
// This helper function reads VID mode via the configuration resistor
//////////////////////////////////////////////////////////////////////
BYTE GetVIDMode(DWORD dwGpu)
{
	if (g_context[dwGpu].m_vidMode != 0xFF)
		return g_context[dwGpu].m_vidMode;

	BYTE data = GetRADC(dwGpu, 3);

	if (data != 0xFF)
		g_context[dwGpu].m_vidMode = data>>2;

	return g_context[dwGpu].m_vidMode;
}
//////////////////////////////////////////////////////////////////////
// This helper function reads active slaves number
//////////////////////////////////////////////////////////////////////
BYTE GetActiveSlaves(DWORD dwGpu)
{
	if (g_context[dwGpu].m_activeSlaves != 0xFF)
		return g_context[dwGpu].m_activeSlaves;

	BYTE data;

	if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x0E, &data))
		g_context[dwGpu].m_activeSlaves = data;

	return g_context[dwGpu].m_activeSlaves;
}
//////////////////////////////////////////////////////////////////////
// This helper function reads maximum output current per slave via the 
// configuration resistor and calculates total maximum output current
// using active slaves number
//////////////////////////////////////////////////////////////////////
FLOAT GetMaxOutputCurrent(DWORD dwGpu)
{
	if (g_context[dwGpu].m_maxOutputCurrent != -1.0f)
		return g_context[dwGpu].m_maxOutputCurrent;

	BYTE data = GetRADC(dwGpu, 4);

	if (data != 0xFF)
	{
		switch ((data>>2) & 7)
		{
		case 0:
			data = 24;
			break;
		case 1:
			data = 36;
			break;
		case 2:
			data = 32;
			break;
		case 3:
			data = 28;
			break;
		case 4:	
			data = 26;
			break;
		case 5:
			data = 38;
			break;
		case 6:
			data = 34;
			break;
		case 7:
			data = 30;
			break;
		}

		g_context[dwGpu].m_maxOutputCurrent = (FLOAT)data * GetActiveSlaves(dwGpu);
	}

	return g_context[dwGpu].m_maxOutputCurrent;
}
//////////////////////////////////////////////////////////////////////
// This helper function maps VID to voltage using VID mode defined via
// configuration resistor
//////////////////////////////////////////////////////////////////////
FLOAT MapVID(DWORD dwGpu, BYTE vid)
{
	switch (GetVIDMode(dwGpu))
	{
	case 0:	//VR 10.X
		{
			FLOAT map[0x40] =
			{
				1.0875f,
				1.0625f,
				1.0375f,
				1.0125f,
				0.9875f,
				0.9625f,
				0.9375f,
				0.9125f,
				0.8875f,
				0.8625f,
				0.8375f,
				1.5875f,
				1.5625f,
				1.5375f,
				1.5125f,
				1.4875f,
				1.4625f,
				1.4375f,
				1.4125f,
				1.3875f,
				1.3625f,
				1.3375f,
				1.3125f,
				1.2875f,
				1.2625f,
				1.2375f,
				1.2125f,
				1.1875f,
				1.1625f,
				1.1375f,
				1.1125f,
				0.0000f,
				1.0750f,
				1.0500f,
				1.0250f,
				1.0000f,
				0.9750f,
				0.9500f,
				0.9250f,
				0.9000f,
				0.8750f,
				0.8500f,
				1.6000f,
				1.5750f,
				1.5500f,
				1.5250f,
				1.5000f,
				1.4750f,
				1.4500f,
				1.4250f,
				1.4000f,
				1.3750f,
				1.3500f,
				1.3250f,
				1.3000f,
				1.2750f,
				1.2500f,
				1.2250f,
				1.2000f,
				1.1750f,
				1.1500f,
				1.1250f,
				1.1000f,
				0.0000f
			};

			return map[vid & 0x3f];
		}
	case 1:	//Opteron
		{
			FLOAT map[0x20] = 
			{
				1.55000f,
				1.52500f,
				1.50000f,
				1.47500f,
				1.45000f,
				1.42500f,
				1.40000f,
				1.37500f,
				1.35000f,
				1.32500f,
				1.30000f,
				1.27500f,
				1.25000f,
				1.22500f,
				1.20000f,
				1.17500f,
				1.15000f,
				1.12500f,
				1.10000f,
				1.07500f,
				1.05000f,
				1.02500f,
				1.00000f,
				0.97500f,
				0.95000f,
				0.92500f,
				0.90000f,
				0.87500f,
				0.85000f,
				0.82500f,
				0.80000f,
				0.00000f
			};

			return map[vid & 0x1f];
		}
	case 2:	//VR 9
		{
			FLOAT map[0x20] = 
			{
				1.85000f,
				1.82500f,
				1.80000f,
				1.77500f,
				1.75000f,
				1.72500f,
				1.70000f,
				1.67500f,
				1.65000f,
				1.62500f,
				1.60000f,
				1.57500f,
				1.55000f,
				1.52500f,
				1.50000f,
				1.47500f,
				1.45000f,
				1.42500f,
				1.40000f,
				1.37500f,
				1.35000f,
				1.32500f,
				1.30000f,
				1.27500f,
				1.25000f,
				1.22500f,
				1.20000f,
				1.17500f,
				1.15000f,
				1.12500f,
				1.10000f,
				0.00000f
			};

			return map[vid & 0x1f];
		}
	}

	return FLT_MAX;
}
//////////////////////////////////////////////////////////////////////
// This helper function loads settings from .cfg file
//////////////////////////////////////////////////////////////////////
void LoadSettings(DWORD dwGpu)
{
	char szCfgPath[MAX_PATH];
	GetModuleFileName(g_hModule, szCfgPath, MAX_PATH);
	
	PathRenameExtension(szCfgPath, ".cfg");
		//get fully qualified path to .cfg

	sscanf(GetMatchingHardwareEntry(dwGpu, "MaxOutputCurrent"), "%f", &g_context[dwGpu].m_maxOutputCurrent);
		//read max output current from .cfg and convert it to float
}
/////////////////////////////////////////////////////////////////////////////
// This helper funtrion is used to localize string if host application 
// supports localization
/////////////////////////////////////////////////////////////////////////////
LPCSTR LocalizeStr(LPCSTR lpStr)
{
	if (g_pLocalizeStr)
		return g_pLocalizeStr(lpStr);

	return lpStr;
}
//////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to get a number of
// data sources in this plugin
//////////////////////////////////////////////////////////////////////
VT1103_API DWORD GetSourcesNum()
{
	return 3 + MAX_SLAVE;
}
//////////////////////////////////////////////////////////////////////
// This helper function formats multiGPU friendly group name
////////////////////////////////////////////////////////////////////////
CString FormatMultiGpuGroupName(LPCSTR lpName, DWORD dwGpu)
{
	CString strName;

	if (dwGpu)
		strName.Format("%s%d", lpName, dwGpu - 1);
	else
		strName = lpName;

	return strName;
}
//////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to get descriptor
// for the specified data source
//////////////////////////////////////////////////////////////////////
VT1103_API BOOL GetSourceDesc(DWORD dwIndex, LPMONITORING_SOURCE_DESC pDesc)
{
	//get host application module handle

	HMODULE hHost = GetModuleHandle(NULL);
	if (!hHost)
		return FALSE;

	DWORD dwGpu		= dwIndex>>16;
	DWORD dwSource	= dwIndex & 0xFFFF;
		//extract GPU and data source indices from macro index

	if (dwGpu >= MAX_GPU)
		//validate GPU index
		return FALSE;

	if (!g_context[dwGpu].m_dev)
		//small performance optimization: once the VRM is detected, we'll
		//precache its' location and will no longer perform I2C scanning
	{
		//get ptr to host application ID routine and return error if the host doesn't 
		//export it

		//we'll use this condition to verify if we're under RivaTuner v2.0 RC 15.7
		//(older versions are not supported) or any other compatible host

		GET_HOST_APP_ID_PROC pGetHostAppID = (GET_HOST_APP_ID_PROC)GetProcAddress(hHost, "GetHostAppID");
		if (!pGetHostAppID)
			return FALSE;

		//get ptrs to required plugin API functions

		g_pLocalizeWnd		= (LOCALIZEWND_PROC)GetProcAddress(hHost, "LocalizeWnd");
		g_pLocalizeStr		= (LOCALIZESTR_PROC)GetProcAddress(hHost, "LocalizeStr");

		g_pGetDeviceIDEx	= (GET_DEVICE_ID_EX_PROC)GetProcAddress(hHost, "GetDeviceIDEx");
		if (!g_pGetDeviceIDEx)
			return FALSE;

		g_pGetI2CBusNumEx	= (GET_I2C_BUS_NUM_EX_PROC)GetProcAddress(hHost, "GetI2CBusNumEx");
		if (!g_pGetI2CBusNumEx)
			return FALSE;

		g_pReadI2CEx		= (READ_I2C_EX_PROC)GetProcAddress(hHost, "ReadI2CEx");
		if (!g_pReadI2CEx)
			return FALSE;

		g_pWriteI2CEx		= (WRITE_I2C_EX_PROC)GetProcAddress(hHost, "WriteI2CEx");
		if (!g_pWriteI2CEx)
			return FALSE;

		//once we've got all function ptrs, we can proceed with I2C bus scanning

		//first, we'll get number of I2C buses for the selected display device

		BYTE busNum		= g_pGetI2CBusNumEx(dwGpu);
		if (!busNum)
			return FALSE;

		//scan all I2C buses and check VT1103 presence

		for (BYTE bus=0; bus<busNum; bus++)
		{
			if (g_pReadI2CEx(dwGpu, bus, 0x70, 0x1A, &g_context[dwGpu].m_devID))
			{
				if ((g_context[dwGpu].m_devID == VT1103_DEVICE_ID) ||
					(g_context[dwGpu].m_devID == VT1105_DEVICE_ID) ||
					(g_context[dwGpu].m_devID == VT1165_DEVICE_ID))
				{
					g_context[dwGpu].m_bus = bus;
					g_context[dwGpu].m_dev = 0x70;
					break;
				}
			}
		}

		//load settings from .cfg

		LoadSettings(dwGpu);
	}

	if (!g_context[dwGpu].m_dev)
		//return FALSE if we've not detected VT1103 voltage regulator
		return FALSE;

	CString strDesc;

	switch (dwSource)
		//now the VRM's location is detected,
		//so we can return requested information to RivaTuner
	{
	case 0:
		//fill voltage regulator output source descriptor

		strcpy(pDesc->szName	, "Voltage regulator output");
		strcpy(pDesc->szDim		, "V");
		if (pDesc->dwVersion >= 0x00010002)
			strcpy(pDesc->szGroup, "VRM");

		pDesc->fltMaxLimit	= 2.0f;
		pDesc->fltMinLimit	= 0.0f;
		pDesc->fltGridDim	= 10.0f;

	
		strDesc.Format(LocalizeStr(" Output voltage reported by %s voltage regulator "), GetVRMName(dwGpu));
		strncpy(pDesc->szDesc, strDesc, sizeof(pDesc->szDesc) - 1);

		return TRUE;

	case 1:
		//fill voltage regulator temperature source descriptor

		if (g_context[dwGpu].m_devID == VT1103_DEVICE_ID)
		{
			strcpy(pDesc->szName	, "Voltage regulator temperature");
			strcpy(pDesc->szDim		, "°C");
			if (pDesc->dwVersion >= 0x00010002)
				strcpy(pDesc->szGroup, FormatMultiGpuGroupName("VRM", dwGpu));

			pDesc->fltMaxLimit	= 200.0f;
			pDesc->fltMinLimit	= 0.0f;
			pDesc->fltGridDim	= 10.0f;

			strDesc.Format(LocalizeStr(" Local temperature reported by %s voltage regulator "), GetVRMName(dwGpu));
			strncpy(pDesc->szDesc, strDesc, sizeof(pDesc->szDesc) - 1);

			return TRUE;
		}

		break;

	case 2:
		//fill voltage regulator average current source descriptor

		if (g_context[dwGpu].m_maxOutputCurrent != 0.0f)
		{
			strcpy(pDesc->szName	, "Voltage regulator current");
			strcpy(pDesc->szDim		, "A");
			if (pDesc->dwVersion >= 0x00010002)
				strcpy(pDesc->szGroup, FormatMultiGpuGroupName("VRM", dwGpu));

			pDesc->fltMaxLimit	= 150.0f;
			pDesc->fltMinLimit	= 0.0f;
			pDesc->fltGridDim	= 10.0f;

			strDesc.Format(LocalizeStr(" Average current reported by %s voltage regulator "), GetVRMName(dwGpu));
			strncpy(pDesc->szDesc, strDesc, sizeof(pDesc->szDesc) - 1);

			return TRUE;
		}

		break;

	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
		//fill voltage regulator slave temperature source descriptor

		if ((g_context[dwGpu].m_devID == VT1105_DEVICE_ID) ||
			(g_context[dwGpu].m_devID == VT1165_DEVICE_ID))
		{
			BYTE slave = (BYTE)(dwIndex - 3);

			if (slave < GetActiveSlaves(dwGpu))
			{
				sprintf(pDesc->szName	, "VRM phase %d temperature", slave + 1);
				strcpy(pDesc->szDim		, "°C");
				if (pDesc->dwVersion >= 0x00010002)
					strcpy(pDesc->szGroup, FormatMultiGpuGroupName("VRM", dwGpu));

				pDesc->fltMaxLimit	= 200.0f;
				pDesc->fltMinLimit	= 0.0f;
				pDesc->fltGridDim	= 10.0f;

				strDesc.Format(LocalizeStr(" Temperature reported by phase %d of %s voltage regulator "), slave + 1, GetVRMName(dwGpu));
				strncpy(pDesc->szDesc, strDesc, sizeof(pDesc->szDesc) - 1);

				return TRUE;
			}
		}

		break;
	}

	return FALSE;
}
//////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to get capabilities
// for the specified data source
//////////////////////////////////////////////////////////////////////
VT1103_API BOOL GetSourceCaps(DWORD dwIndex, DWORD dwCaps, LPDWORD lpResult)
{

	//all sources support multiGPU monitoring so it is safe to ignore passed
	//index and check just the capability

	switch (dwCaps)
	{
	case  MONITORING_SOURCE_CAPS_MULTIGPU_SUPPORT:
		*lpResult = 1;
		return TRUE;
	}

	return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to poll data sources
/////////////////////////////////////////////////////////////////////////////
VT1103_API FLOAT GetSourceData(DWORD dwIndex)
{
	DWORD dwGpu		= dwIndex>>16;
	DWORD dwSource	= dwIndex & 0xFFFF;
		//extract GPU and data source indices from macro index

	if (dwGpu >= MAX_GPU)
		//validate GPU index
		return FALSE;

	FLOAT	result	= FLT_MAX;	
	BYTE	temp;
	BYTE	data;

	switch (dwSource)
	{
	case 0:
		//voltage regulator output

		if (GetVIDMode(dwGpu) == 0xFF)
			break;

		if (g_context[dwGpu].m_devID == VT1103_DEVICE_ID)
		{
			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x1C, &data))
				result = MapVID(dwGpu, data);

			//perform misreading filtering

			if (result != FLT_MAX)
				g_context[dwGpu].m_fltVoltage = result;

			//return last correctly read value

			return g_context[dwGpu].m_fltVoltage;
		}

		if ((g_context[dwGpu].m_devID == VT1105_DEVICE_ID) ||
			(g_context[dwGpu].m_devID == VT1165_DEVICE_ID))
		{
			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x19, &data))
			{
				if (g_context[dwGpu].m_vidMode == 3)
				{
					if (data & 1)
					{
						if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x1C, &data))
						{
							temp = 0x15 + ((data>>4) & 3);

							if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, temp, &data))
								result = 0.0125f * (data & 0x7f) + 0.450f;
						}
					}
					else
						if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x1C, &data))
						{
							switch (data & 3)
							{
							case 0:
								result = 1.00f;
								break;
							case 1:
								result = 1.05f;
								break;
							case 2:
								result = 1.10f;
								break;
							case 3:
								result = 1.15f;
								break;
							}
						}
				}
				else
				{
					if (data & 1)
					{
						if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x18, &data))
							result = 0.0125f * (data & 0x7f) + 0.450f * (data >> 7);
					}
					else
					{
						if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x1C, &data))
							result = MapVID(dwGpu, data);
					}
				}
			}

			//perform misreading filtering

			if (result != FLT_MAX)
				g_context[dwGpu].m_fltVoltage = result;

			//return last correctly read value

			return g_context[dwGpu].m_fltVoltage;
		}

		break;

	case 1:
		//voltage regulator temperature

		if (g_context[dwGpu].m_devID == VT1103_DEVICE_ID)
		{
			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x17, &data))
				result = data;

			//perform misreading filtering

			if (result != FLT_MAX)
				g_context[dwGpu].m_fltTemperatureLocal = result;

				//return last correctly read value

			return g_context[dwGpu].m_fltTemperatureLocal;
		}

		break;

	case 2:
		//voltage regulator average current

		if (g_context[dwGpu].m_devID == VT1103_DEVICE_ID)
		{
			if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x14, &data) &&
				g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x15, &data))
			{
				temp = data>>1;

				if (temp)
				{
					if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x11, &data) &&
						g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x12, &data))
						result = g_context[dwGpu].m_maxOutputCurrent * (data>>1) / temp;
				}
			}
		}

		if ((g_context[dwGpu].m_devID == VT1105_DEVICE_ID) ||
			(g_context[dwGpu].m_devID == VT1165_DEVICE_ID))
		{
			if (GetMaxOutputCurrent(dwGpu) != -1.0f)
			{
				if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x1E, &data))
					result = g_context[dwGpu].m_maxOutputCurrent * (data & 0x7f) / 124.0f;
			}
		}

		//perform misreading filtering

		if (result != FLT_MAX)
			g_context[dwGpu].m_fltAverageCurrent = result;

		//return last correctly read value

		return g_context[dwGpu].m_fltAverageCurrent;

	case 3:
	case 4:
	case 5:
	case 6:
	case 7:
	case 8:
		//voltage regulator slave temperature

		if ((g_context[dwGpu].m_devID == VT1105_DEVICE_ID) ||
			(g_context[dwGpu].m_devID == VT1165_DEVICE_ID))
		{
			BYTE slave = (BYTE)(dwIndex - 3);

			if (slave < GetActiveSlaves(dwGpu))
			{
				data = 0x00;

				if (g_pWriteI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x08, &data))
				{
					data = 0xA7;

					if (g_pWriteI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x07, &data))
					{
						data = 0x80 | (slave + 1);

						if (g_pWriteI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x08, &data))
						{
							if (g_pReadI2CEx(dwGpu, g_context[dwGpu].m_bus, g_context[dwGpu].m_dev, 0x0A, &data))
								result = data;
						}
					}
				}

				//perform misreading filtering

				if (result != FLT_MAX)
					g_context[dwGpu].m_fltTemperatureSlave[slave] = result;

					//return last correctly read value

				return g_context[dwGpu].m_fltTemperatureSlave[slave];
			}
		}

		break;
	}

	return result;
}
/////////////////////////////////////////////////////////////////////////////
