/*****************************************************************************
 * THIS PROGRAM IS COPYRIGHT (c) 2005 BY DREW TECHNOLOGIES INC.              *
 * THIS PROGRAM IS PROVIDED 'AS IS' WITHOUT WARRANTY OF ANY KIND, EITHER     *
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED          *
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.       *
 * IN NO EVENT SHALL DREWTECH BE LIABLE FOR DAMAGES, INCLUDING ANY GENERAL,  *
 * SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR    *
 * INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA   *
 * OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD     *
 * PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),  *
 * EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY    *
 * OF SUCH DAMAGES.                                                          *
 ****************************************************************************/

#ifndef __J2534_V0202BOILERPLATE_C
#define __J2534_V0202BOILERPLATE_C

#include <j2534_v0202.h>
#include "j2534_boilerplate.h"
#ifdef _WIN32

/* J2534 Interface API defines */
PTCONNECT PassThruConnect;
PTDISCONNECT PassThruDisconnect;
PTREADMSGS PassThruReadMsgs;
PTWRITEMSGS PassThruWriteMsgs;
PTSTARTPERIODICMSG PassThruStartPeriodicMsg;
PTSTOPPERIODICMSG PassThruStopPeriodicMsg;
PTSTARTMSGFILTER PassThruStartMsgFilter;
PTSTOPMSGFILTER PassThruStopMsgFilter;
PTSETPROGRAMMINGVOLTAGE PassThruSetProgrammingVoltage;
PTREADVERSION PassThruReadVersion;
PTGETLASTERROR PassThruGetLastError;
PTIOCTL PassThruIoctl;

#ifdef DREWTECHONLY
/* Drew Tech specific functions */
PTLOADFIRMWARE PassThruLoadFirmware;
PTRECOVERFIRMWARE PassThruRecoverFirmware;
PTREADIPSETUP PassThruReadIPSetup;
PTWRITEIPSETUP PassThruWriteIPSetup;
PTREADPCSETUP PassThruReadPCSetup;
PTGETPOINTER PassThruGetPointer;
PTGETNEXTCARDAQ PassThruGetNextCarDAQ;
#endif

/* Lists used to find the DLLs */
#define MAXKEYSIZE 			240
J2534DEVICELIST DeviceList[MAX_NUM_DEVICES];
char J2534BoilerplateErrorResult[MAX_STRING_LENGTH];
static HINSTANCE hDLL = NULL;

/*
*****************************************************************************
**
** FUNCTION AUTHOR(S):
**	Mark Wine
**
** FUNCTION NAME:
**  EnumerateJ2534DLLs
**
** FUNCTION INPUTS:
**	NONE.
**
** FUNCTION DESCRIPTION
**  This function looks through the registry and finds the supported
**  J2534 DLLs.  It returns -1 if an error.  If it finds the Drew Tech Device
**  DLL names.
**
** FUNCTION OUTPUTS:
**	returns the number of Drew Tech devices it found.
**
*****************************************************************************
*/
long EnumerateJ2534DLLs(void)
{
	unsigned long ListIndex;
	HKEY hKey1,hKey2,hKey3,hKey4,hKey5;
	FILETIME FTime;
    long hKey2RetVal, hKey4RetVal;
	DWORD VendorIndex, DeviceIndex;
	DWORD KeyType;
	DWORD KeySize;
	unsigned char KeyValue[MAXKEYSIZE];
    int fErrorIgnore;

	/* Find all available interfaces in the registry */
	strncpy(J2534BoilerplateErrorResult,"No Error",MAX_STRING_LENGTH);

	// Open HKEY_LOCAL_MACHINE/Software
	if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software", 0, KEY_READ, &hKey1) == ERROR_SUCCESS)
	{
		// Open HKEY_LOCAL_MACHINE/Software/PassThruSupport
		if (RegOpenKeyEx(hKey1, "PassThruSupport", 0, KEY_READ, &hKey2) == ERROR_SUCCESS)
		{
			// Open HKEY_LOCAL_MACHINE/Software/PassThruSupport/Vendor[i]
		    ListIndex = 0;
			VendorIndex = 0;
			do
			{
				KeySize = sizeof(KeyValue);
				hKey2RetVal = RegEnumKeyEx(hKey2, VendorIndex, (char *)KeyValue, &KeySize, NULL, NULL, NULL, &FTime);
				if (hKey2RetVal == ERROR_SUCCESS)
				{
		    		if (RegOpenKeyEx(hKey2, (char *)KeyValue, 0, KEY_READ, &hKey3) == ERROR_SUCCESS)
			    	{
						// Open HKEY_LOCAL_MACHINE/Software/PassThruSupport/Vendor[VendorIndex]/Devices
						if (RegOpenKeyEx(hKey3, "Devices", 0, KEY_READ, &hKey4) == ERROR_SUCCESS)
						{
							DeviceIndex = 0;
							do
							{
								KeySize = sizeof(KeyValue);
								hKey4RetVal = RegEnumKeyEx(hKey4, DeviceIndex, (char *)KeyValue, &KeySize, NULL, NULL, NULL, &FTime);
								if (hKey4RetVal == ERROR_SUCCESS)
								{
									// Open HKEY_LOCAL_MACHINE/Software/PassThru_Support/Vendor[VendorIndex]/Devices/Device[DeviceIndex]
							    	if (RegOpenKeyEx(hKey4, (char *)KeyValue, 0, KEY_READ, &hKey5) == ERROR_SUCCESS)
								    {
                                        fErrorIgnore = FALSE;
										// Read HKEY_LOCAL_MACHINE/Software/PassThru_Support/Vendor[VendorIndex]/Name
		        						KeySize = sizeof(KeyValue);
										if (RegQueryValueEx(hKey3, "Name", 0, &KeyType, KeyValue, &KeySize) == ERROR_SUCCESS)
		        						{
							                strncpy(DeviceList[ListIndex].VendorName,(char *)KeyValue,MAX_STRING_LENGTH);
										}
										else
										{
											strncpy(J2534BoilerplateErrorResult,"Can't open HKEY_LOCAL_MACHINE->..->Vendor[i]->Name key",MAX_STRING_LENGTH);
							                strncpy(DeviceList[ListIndex].VendorName,"No Vendor Name",MAX_STRING_LENGTH);
										}
#ifdef DREWTECHONLY
                    		        	if(strncmp("Drew Tech", (char *)KeyValue, 9) == 0)
                                        {
#endif
										// Read HKEY_LOCAL_MACHINE/Software/PassThru_Support/Vendor[VendorIndex]/Devices/Device[DeviceIndex]/Name
										KeySize = sizeof(KeyValue);
							       		if (RegQueryValueEx(hKey5, "Name", 0, &KeyType, KeyValue, &KeySize) == ERROR_SUCCESS)
								        {
							                strncpy(DeviceList[ListIndex].DeviceName,(char *)KeyValue,MAX_STRING_LENGTH);
							       		}
										else
										{
											strncpy(J2534BoilerplateErrorResult,"Can't open HKEY_LOCAL_MACHINE->..->Devices->Device[i]->Name key",MAX_STRING_LENGTH);
                                            fErrorIgnore = TRUE;
										}

										// Read HKEY_LOCAL_MACHINE/Software/PassThru_Support/Vendor[VendorIndex]/Devices/Device[DeviceIndex]/FunctionLibrary
							       		KeySize = sizeof(KeyValue);
								        if (RegQueryValueEx(hKey5, "FunctionLibrary", 0, &KeyType, KeyValue, &KeySize) == ERROR_SUCCESS)
							       		{
							                strncpy(DeviceList[ListIndex].LibraryName,(char *)KeyValue, MAX_STRING_LENGTH);
									    }
										else
										{
											strncpy(J2534BoilerplateErrorResult,"Can't open HKEY_LOCAL_MACHINE->..->FunctionLibrary key",MAX_STRING_LENGTH);
                                            fErrorIgnore = TRUE;
										}

                                        if(!fErrorIgnore)
                                        {
									        ListIndex++;
                                        }
#ifdef DREWTECHONLY
                                        }
#endif
								        RegCloseKey(hKey5);
									}
							    }
							    DeviceIndex++;
							}
							while((hKey4RetVal == ERROR_SUCCESS) && (ListIndex < MAX_NUM_DEVICES));
							RegCloseKey(hKey4);
						}
						else
						{
							strncpy(J2534BoilerplateErrorResult,"Can't open HKEY_LOCAL_MACHINE->..->Vendor[i]->Devices key",MAX_STRING_LENGTH);
						}
						RegCloseKey(hKey3);
				    }
		        }
		        VendorIndex++;
		    } while((hKey2RetVal == ERROR_SUCCESS) && (ListIndex < MAX_NUM_DEVICES));
			RegCloseKey(hKey2);
		}
		else
		{
			strncpy(J2534BoilerplateErrorResult,"Can't open HKEY_LOCAL_MACHINE->..->PassThruSupport key",MAX_STRING_LENGTH);
	        return(-1);
		}
		RegCloseKey(hKey1);
	}
	else
	{
		strncpy(J2534BoilerplateErrorResult,"Can't open HKEY_LOCAL_MACHINE->Software key",MAX_STRING_LENGTH);
        return(-1);
	}

#ifdef DREWTECHONLY
    if(ListIndex == 0)
    {
		strncpy(J2534BoilerplateErrorResult,"Can't find 'Drew Technologies Inc.' keyword in Registry.",MAX_STRING_LENGTH);
	    return(-1);
    }
#endif

    return(ListIndex);
}
/*
*****************************************************************************
**
** FUNCTION AUTHOR(S):
**	Mark Wine
**
** FUNCTION NAME:
**  LoadJ2534DLL
**
** FUNCTION INPUTS:
**	NONE.
**
** FUNCTION DESCRIPTION
**  This function loads the DLL and finds all the procedure addresses from the
**  loaded J2534 DLL.
**  It returns None-zero if an error.
**
** FUNCTION OUTPUTS:
**	NONE.
**
*****************************************************************************
*/
long LoadJ2534DLL(char * szDLL)
{

    if(szDLL == NULL)
    {
		strncpy(J2534BoilerplateErrorResult,"NULL string pointer to J2534 DLL location.",MAX_STRING_LENGTH);
        return(1);
    }

	if((hDLL = LoadLibrary(szDLL)) == NULL)
	{
		strncpy(J2534BoilerplateErrorResult,"No J2534 DLLs found.",MAX_STRING_LENGTH);
		return(1);
	}
	strncpy(J2534BoilerplateErrorResult,"DLL function GetProcAddress error.",MAX_STRING_LENGTH);
	if((PassThruConnect = (PTCONNECT)GetProcAddress(hDLL, "PassThruConnect")) == NULL){return(1);}
	if((PassThruDisconnect = (PTDISCONNECT)GetProcAddress(hDLL, "PassThruDisconnect")) == NULL){return(1);}
	if((PassThruReadMsgs = (PTREADMSGS)GetProcAddress(hDLL, "PassThruReadMsgs")) == NULL){return(1);}
	if((PassThruWriteMsgs = (PTWRITEMSGS)GetProcAddress(hDLL, "PassThruWriteMsgs")) == NULL){return(1);}
	if((PassThruStartPeriodicMsg = (PTSTARTPERIODICMSG)GetProcAddress(hDLL, "PassThruStartPeriodicMsg")) == NULL){return(1);}
	if((PassThruStopPeriodicMsg = (PTSTOPPERIODICMSG)GetProcAddress(hDLL, "PassThruStopPeriodicMsg")) == NULL){return(1);}
	if((PassThruStartMsgFilter = (PTSTARTMSGFILTER)GetProcAddress(hDLL, "PassThruStartMsgFilter")) == NULL){return(1);}
	if((PassThruStopMsgFilter = (PTSTOPMSGFILTER)GetProcAddress(hDLL, "PassThruStopMsgFilter")) == NULL){return(1);}
	if((PassThruSetProgrammingVoltage = (PTSETPROGRAMMINGVOLTAGE)GetProcAddress(hDLL, "PassThruSetProgrammingVoltage")) == NULL){return(1);}
	if((PassThruReadVersion = (PTREADVERSION)GetProcAddress(hDLL, "PassThruReadVersion")) == NULL){return(1);}
	if((PassThruGetLastError = (PTGETLASTERROR)GetProcAddress(hDLL, "PassThruGetLastError")) == NULL){return(1);}
	if((PassThruIoctl = (PTIOCTL)GetProcAddress(hDLL, "PassThruIoctl")) == NULL){return(1);}

#ifdef DREWTECHONLY
	if((PassThruLoadFirmware = (PTLOADFIRMWARE)GetProcAddress(hDLL, "PassThruLoadFirmware")) == NULL){return(1);};
	if((PassThruRecoverFirmware = (PTRECOVERFIRMWARE)GetProcAddress(hDLL, "PassThruRecoverFirmware")) == NULL){return(1);};
	if((PassThruReadIPSetup = (PTREADIPSETUP)GetProcAddress(hDLL, "PassThruReadIPSetup")) == NULL){return(1);};
	if((PassThruWriteIPSetup = (PTWRITEIPSETUP)GetProcAddress(hDLL, "PassThruWriteIPSetup")) == NULL){return(1);};
	if((PassThruReadPCSetup = (PTREADPCSETUP)GetProcAddress(hDLL, "PassThruReadPCSetup")) == NULL){return(1);};
	if((PassThruGetPointer = (PTGETPOINTER)GetProcAddress(hDLL, "PassThruGetPointer")) == NULL){return(1);};
	if((PassThruGetNextCarDAQ = (PTGETNEXTCARDAQ)GetProcAddress(hDLL, "PassThruGetNextCarDAQ")) == NULL)
	{
		/* not all CarDAQ DLLs support this */
		PassThruGetNextCarDAQ = NULL;
	}
#endif
	strncpy(J2534BoilerplateErrorResult,"No Error",MAX_STRING_LENGTH);
    return(0);
}
/*
*****************************************************************************
**
** FUNCTION AUTHOR(S):
**	Mark Wine
**
** FUNCTION NAME:
**  FreeJ2534DLL
**
** FUNCTION INPUTS:
**	NONE.
**
** FUNCTION DESCRIPTION
**  This function Frees the DLL that was loaded in the LoadJ2534Dll function.
**
** FUNCTION OUTPUTS:
**	NONE.
**
*****************************************************************************
*/
long FreeJ2534DLL(void)
{
	if(hDLL != NULL) FreeLibrary(hDLL);
	return(0);
}
#else
// Linux code
long EnumerateJ2534DLLs(void)
{
	// return 1 as if we found 1 cardaq device
	return(1);
}
#ifdef arm /* ARM Linux */
 #include <stdio.h>
 #include <stdlib.h> // exit
 #include <sys/resource.h> // for getpriority
 #include <sched.h> // for sched_setscheduler
 #include <errno.h>
long LoadJ2534DLL(char * szDLL)
{
	struct sched_param sp;
	errno=0;
	setpriority(PRIO_PROCESS, 0, -20);
	if(errno==-1) { printf("prio err\n"); perror(NULL); exit(1); }
	sp.sched_priority = sched_get_priority_max(SCHED_RR);
	if(sched_setscheduler(0, SCHED_RR, &sp) == -1)
		 { printf("sched err\n"); perror(NULL); exit(1); }
	return(0);
}
#else /* X86 Linux */
long LoadJ2534DLL(char * szChar)
{
	// do nothing
	return(0);
}
#endif
#endif

#endif

