/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-3
**  FileName: ConnectProtocol.c
**  Author:   EnGenius
**  Date:     2/25/2022
**  Email:    <support@autosinnovate.org>
**
**  Purpose:  SAE J1699-3 Vehicle OBD II Compliance Test Cases Source Code.
**            This source code is intended to run the tests described in
**            the SAE J1699-3 document in an automated manner, when compiled
**            and used with an SAE J2534-compatible pass-thru device.
**
**            File j1699.c contains information on building and running this test.
**
**  Description:
**
**  Modifications:  12/07/2022  Initial Version
**
********************************************************************************
*******************************************************************************/

#include <stdio.h>    // C Library input and output declarations
#include <stdlib.h>   // C Library general function declarations
#include <time.h>     // C Library time and date declarations
#include <windows.h>  // Windows API declarations
#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations


// Tester present message rate
#define OBD_TESTER_PRESENT_RATE  2000


// Varialbles
BOOL                   gbJ2534_2_Mode = FALSE;
PASSTHRU_MSG           gstTstrPrsntMsg = {0};
unsigned long          gMaxResponseTimeMsecs = 100;
unsigned long          gMinResponseTimeMsecs = 0;     // min response time
BYTE                   gstResponseTA[MAX_ECUS] = {0};
BYTE                   gOBDKeywords[2] = {0};
static BOOL            bPeriodicActive = FALSE;

extern BOOL            gbPeriodicMsgEnabled;              // Option to turn off Tester Present Message
extern BYTE            gNumOfECUs29Bit;
extern unsigned short  gECU_Ignore_List_Count;
extern unsigned long  *gpECU_Ignore_List;


/*******************************************************************************
**
**  Function:  ConnectOBDProtocol
**
**  Purpose:   Connect to a protocol
**
*******************************************************************************/
STATUS ConnectOBDProtocol ( void )
{
	PASSTHRU_MSG   MskMsg;
	PASSTHRU_MSG   PatMsg;
	PASSTHRU_MSG   FlwMsg;
	SCONFIG_LIST   CnfgList;
	SCONFIG        CnfgParameter;
	STATUS         eRetVal;
	BYTE           EcuIdx;
	unsigned long  eInitFlags;
	BOOL           bResetEcuIdx;
	BYTE           IgnoreECUIdx;
	BOOL           bIgnoreECUId;
	PASSTHRU_MSG   StartDiagReqMsg;
	PASSTHRU_MSG   StartDiagRespMsg;
	SBYTE_ARRAY    DataIn;
	SBYTE_ARRAY    DataOut;
	BYTE           ByteArray[8];


	// Set the request delay, maximum response time and init flags according to the protocol
	eInitFlags = 0;
	if ( gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
	{
		// 50msec for ISO15765
		gRequestDelayTimeMsecs = 50;  // 50 ms delay for ISO15765
		gMaxResponseTimeMsecs  = 50;  // 50 ms   max for ISO15765
		gMinResponseTimeMsecs  = 0;   //  0 ms   min for ISO15765
		eInitFlags = (gstProtocolList[gProtocolIdx].eInitFlags & CAN_29BIT_ID);
		if ( gbJ2534_2_Mode )
		{
			eInitFlags |= ISO15765_ADDR_TYPE;
		}
	}
	else if ( gstProtocolList[gProtocolIdx].eProtocol == ISO9141 ||
	          gstProtocolList[gProtocolIdx].eProtocol == ISO14230 )
	{
		// Spec times for ISO9141 / ISO14230, required to verify P2 Min and P2 Max
		gRequestDelayTimeMsecs = 300;  // 300 ms delay for ISO19141
		gMaxResponseTimeMsecs  = 50;   //  50 ms   max for ISO19141
		gMinResponseTimeMsecs  = 25;   //  25 ms   min for ISO19141
		eInitFlags = 0; // (gstProtocolList[gProtocolIdx]eInitFlags & ISO9141_K_LINE_ONLY);
	}
	else
	{
		// 100msec for all others
		gRequestDelayTimeMsecs = 100;  // 100 ms delay for J1850
		gMaxResponseTimeMsecs  = 100;  // 100 ms   max for J1850
		gMinResponseTimeMsecs  = 0;    //   0 ms   min for J1850
	}

	// Connect to protocol
	if ( (eRetVal = PassThruConnect ( gDeviceID,
	                                 gstProtocolList[gProtocolIdx].eProtocol,
	                                 eInitFlags,
	                                 gstProtocolList[gProtocolIdx].BaudRate,
	                                 &gstProtocolList[gProtocolIdx].ChannelID )) != STATUS_NOERROR )
	{
		if ( gstProtocolList[gProtocolIdx].eProtocol == ISO9141 )
		{
			Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "PassThru Device unable to connect ISO9141 using K & L Line initialization.  Attempting to connect using K Line Only\n" );
			// try K-Line only
			eRetVal = PassThruConnect ( gDeviceID,
			                           gstProtocolList[gProtocolIdx].eProtocol,
			                           gstProtocolList[gProtocolIdx].eInitFlags & ISO9141_K_LINE_ONLY,
			                           gstProtocolList[gProtocolIdx].BaudRate,
			                           &gstProtocolList[gProtocolIdx].ChannelID );
		}

		if ( eRetVal != STATUS_NOERROR )
		{
			Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%s returned %ld",
			      "PassThruConnect",
			      eRetVal);
			return FAIL;
		}
	}

	// Turn on LOOPBACK to see transmitted messages
	CnfgList.NumOfParams    = 1;
	CnfgList.ConfigPtr      = &CnfgParameter;
	CnfgParameter.Parameter = LOOPBACK;
	CnfgParameter.Value     = 1;
	if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
	                               SET_CONFIG,
	                               &CnfgList,
	                               NULL )) != STATUS_NOERROR )
	{
		Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "%s returned %ld",
		      "PassThruIoctl(LOOPBACK)",
		      eRetVal);
		return FAIL;
	}


	// IF in J2534_2 mode, set CAN_MIXED_FORMAT to CAN_MIXED_FORMAT_ON
	if ( gbJ2534_2_Mode )
	{
		CnfgList.NumOfParams    = 1;
		CnfgList.ConfigPtr      = &CnfgParameter;
		CnfgParameter.Parameter = CAN_MIXED_FORMAT;
		CnfgParameter.Value     = CAN_MIXED_FORMAT_ON;
		if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
		                               SET_CONFIG,
		                               &CnfgList,
		                               NULL )) != STATUS_NOERROR )
		{
			Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%s returned %ld",
			      "PassThruIoctl(CAN_MIXED_FORMAT)",
			      eRetVal );
			return FAIL;
		}
	}

	// Setup the node IDs, functional message ID and filter(s) based on protocol
	switch ( gstProtocolList[gProtocolIdx].eProtocol )
	{
		case ISO9141:
		{
			// Set W4
			CnfgList.NumOfParams    = 1;
			CnfgList.ConfigPtr      = &CnfgParameter;
			CnfgParameter.Parameter = W4;
			CnfgParameter.Value     = 40;
			if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
			                               SET_CONFIG,
			                               &CnfgList,
			                               NULL )) != STATUS_NOERROR )
			{
				Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%s returned %ld",
				      "PassThruIoctl(W4)",
				      eRetVal );
				return FAIL;
			}

			// Five baud initialization
			DataIn.NumOfBytes  = 1;
			DataIn.BytePtr     = ByteArray;
			DataOut.NumOfBytes = sizeof ( gOBDKeywords );
			DataOut.BytePtr    = gOBDKeywords;
			ByteArray[0]       = 0x33;
			if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
			                               FIVE_BAUD_INIT,
			                               &DataIn,
			                               &DataOut )) == STATUS_NOERROR )
			{
				if ( gOBDKeywords[0] != 0 || gOBDKeywords[1] != 0 )
				{
					Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "Keyword 1 = $%02X   Keyword 2 = $%02X\n",
					      gOBDKeywords[0],
					      gOBDKeywords[1] );

					if ( gOBDKeywords[0] == 0x94 && gOBDKeywords[1] == 0x94 )
					{
						gMinResponseTimeMsecs = 0;  // ISO9141 KB 94 94 allow 0 ms response time.
					}
					else if ( !(gOBDKeywords[0] == 0x08 && gOBDKeywords[1] == 0x08) )
					{
						// these are not OBD Key Bytes
						Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "Non-OBD Key Bytes detected\n" );
						return FAIL;
					}
				}
			}
			else
			{
				// Do not log J2534 errors.  If there is no ISO9141 ECU,
				// it is proper for there to be no response.
				return FAIL;
			}
		}
		// ISO9141 case falls through to J1850VPW to setup same message filter

		case J1850VPW:
		{
			// Setup pass filter to read only OBD requests / responses
			MskMsg.eProtocolID  = gstProtocolList[gProtocolIdx].eProtocol;
			MskMsg.TxFlags     = 0;
			MskMsg.DataSize    = 3;
			MskMsg.Data[0]     = 0x00;
			MskMsg.Data[1]     = 0xFE;
			MskMsg.Data[2]     = 0x00;

			PatMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			PatMsg.TxFlags     = 0;
			PatMsg.DataSize    = 3;
			PatMsg.Data[0]     = 0x00;
			PatMsg.Data[1]     = 0x6A;
			PatMsg.Data[2]     = 0x00;
			if ( (eRetVal = PassThruStartMsgFilter ( gstProtocolList[gProtocolIdx].ChannelID,
			                                        PASS_FILTER,
			                                        &MskMsg,
			                                        &PatMsg,
			                                        NULL,
			                                        &gstProtocolList[gProtocolIdx].FilterID )) != STATUS_NOERROR )
			{
				Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%s returned %ld",
				      "PassThruStartMsgFilter",
				      eRetVal );
				return FAIL;
			}

			// Setup tester present keep alive message using SID 1 PID 0
			gstTstrPrsntMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			gstTstrPrsntMsg.TxFlags     = 0;
			gstTstrPrsntMsg.DataSize    = 5;
			gstTstrPrsntMsg.Data[0]     = 0x68;
			gstTstrPrsntMsg.Data[1]     = 0x6A;
			gstTstrPrsntMsg.Data[2]     = TESTER_NODE_ADDRESS;
			gstTstrPrsntMsg.Data[3]     = 0x01;
			gstTstrPrsntMsg.Data[4]     = 0x00;
		}
		break;

		case J1850PWM:
		{
			// Setup node ID
			CnfgList.NumOfParams    = 1;
			CnfgList.ConfigPtr      = &CnfgParameter;
			CnfgParameter.Parameter = NODE_ADDRESS;
			CnfgParameter.Value     = TESTER_NODE_ADDRESS;
			if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
			                               SET_CONFIG,
			                               &CnfgList,
			                               NULL )) != STATUS_NOERROR )
			{
				Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%s returned %ld",
				      "PassThruIoctl(SET_CONFIG/NODE_ADDRESS)",
				      eRetVal );
				return FAIL;
			}

			// Setup functional message lookup table
			DataIn.NumOfBytes = 1;
			DataIn.BytePtr    = ByteArray;
			ByteArray[0]      = 0x6B;
			if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
			                               ADD_TO_FUNCT_MSG_LOOKUP_TABLE,
			                               &DataIn,
			                               NULL )) != STATUS_NOERROR )
			{
				Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%s returned %ld",
				      "PassThruIoctl(ADD_TO_FUNC_MSG_LOOKUP_TABLE)",
				      eRetVal );
				return FAIL;
			}

			// Setup pass filter to read only OBD requests / responses
			MskMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			MskMsg.TxFlags     = 0;
			MskMsg.DataSize    = 3;
			MskMsg.Data[0]     = 0x00;
			MskMsg.Data[1]     = 0xFE;     // Setup mask to see both request and response
			MskMsg.Data[2]     = 0x00;

			PatMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			PatMsg.TxFlags     = 0;
			PatMsg.DataSize    = 3;
			PatMsg.Data[0]     = 0x00;
			PatMsg.Data[1]     = 0x6A;
			PatMsg.Data[2]     = 0x00;
			if ( (eRetVal = PassThruStartMsgFilter ( gstProtocolList[gProtocolIdx].ChannelID,
			                                        PASS_FILTER,
			                                        &MskMsg,
			                                        &PatMsg,
			                                        NULL,
			                                        &gstProtocolList[gProtocolIdx].FilterID )) != STATUS_NOERROR )
			{
				Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%s returned %ld",
				      "PassThruStartMsgFilter",
				      eRetVal );
				return FAIL;
			}

			// Setup tester present keep alive message using SID 1 PID 0
			gstTstrPrsntMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			gstTstrPrsntMsg.TxFlags     = 0;
			gstTstrPrsntMsg.DataSize    = 5;
			gstTstrPrsntMsg.Data[0]     = 0x61;
			gstTstrPrsntMsg.Data[1]     = 0x6A;
			gstTstrPrsntMsg.Data[2]     = TESTER_NODE_ADDRESS;
			gstTstrPrsntMsg.Data[3]     = 0x01;
			gstTstrPrsntMsg.Data[4]     = 0x00;
		}
		break;

		case ISO14230:
		{
			// Handle both five baud and fast initialization cases
			if ( gstProtocolList[gProtocolIdx].eInitFlags == FAST_INIT )
			{
				// Fast initialization
				StartDiagReqMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				StartDiagReqMsg.TxFlags     = 0;
				StartDiagReqMsg.DataSize    = 4;
				StartDiagReqMsg.Data[0]     = 0xC1;
				StartDiagReqMsg.Data[1]     = 0x33;
				StartDiagReqMsg.Data[2]     = TESTER_NODE_ADDRESS;
				StartDiagReqMsg.Data[3]     = 0x81;

				StartDiagRespMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				StartDiagRespMsg.TxFlags     = 0;
				StartDiagRespMsg.DataSize    = 20;

				if ( PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
				                     FAST_INIT,
				                     &StartDiagReqMsg,
				                     &StartDiagRespMsg ) == STATUS_NOERROR )
				{
					if ( StartDiagRespMsg.Data[3] == 0xC1 &&
					     (StartDiagRespMsg.Data[4] != 0 ||
					      StartDiagRespMsg.Data[5] != 0) )
					{
						Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU $%02X   Keyword 1 = $%02X   Keyword 2 = $%02X\n",
						      StartDiagRespMsg.Data[2],
						      StartDiagRespMsg.Data[4],
						      StartDiagRespMsg.Data[5] );

						if ( !(StartDiagRespMsg.Data[4] == 0xE9 && StartDiagRespMsg.Data[5] == 0x8F) &&
						     !(StartDiagRespMsg.Data[4] == 0x6B && StartDiagRespMsg.Data[5] == 0x8F) &&
						     !(StartDiagRespMsg.Data[4] == 0x6D && StartDiagRespMsg.Data[5] == 0x8F) &&
						     !(StartDiagRespMsg.Data[4] == 0xEF && StartDiagRespMsg.Data[5] == 0x8F) )
						{
							// these are not OBD Key Bytes
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Non-OBD Key Bytes detected\n" );
							return FAIL;
						}
					}
					else
					{
						// this is not the right response or key bytes are 0
						return FAIL;
					}
				}
				else
				{
					// Do not log J2534 errors.  If there is no ISO14230 ECU,
					// it is proper for there to be no response.
					return FAIL;
				}
			}
			else
			{
				// Set W4
				CnfgList.NumOfParams    = 1;
				CnfgList.ConfigPtr      = &CnfgParameter;
				CnfgParameter.Parameter = W4;
				CnfgParameter.Value     = 40;
				if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
				                               SET_CONFIG,
				                               &CnfgList,
				                               NULL )) != STATUS_NOERROR )
				{
					Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "%s returned %ld",
					      "PassThruIoctl(W4)",
					      eRetVal );
					return FAIL;
				}

				// Five baud initialization
				DataIn.NumOfBytes  = 1;
				DataIn.BytePtr     = ByteArray;
				DataOut.NumOfBytes = sizeof ( gOBDKeywords );
				DataOut.BytePtr    = gOBDKeywords;
				ByteArray[0]       = 0x33;
				if ( PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
				                     FIVE_BAUD_INIT,
				                     &DataIn,
				                     &DataOut ) == STATUS_NOERROR )
				{
					if ( gOBDKeywords[0] != 0 || gOBDKeywords[1] != 0 )
					{
						Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "Keyword 1 = $%02X   Keyword 2 = $%02X\n",
						      gOBDKeywords[0],
						      gOBDKeywords[1] );

						if ( !(gOBDKeywords[0] == 0xE9 && gOBDKeywords[1] == 0x8F) &&
						     !(gOBDKeywords[0] == 0x6B && gOBDKeywords[1] == 0x8F) &&
						     !(gOBDKeywords[0] == 0x6D && gOBDKeywords[1] == 0x8F) &&
						     !(gOBDKeywords[0] == 0xEF && gOBDKeywords[1] == 0x8F) )
						{
							// these are not OBD Key Bytes
							Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "Non-OBD Key Bytes detected\n" );
							return FAIL;
						}
					}
					else
					{
						return FAIL;
					}
				}
				else
				{
					// Do not log J2534 errors.  If there is no ISO14230 ECU,
					// it is proper for there to be no response.
					return FAIL;
				}
			}

			// Setup tester present keep alive message using SID 1 PID 0
			gstTstrPrsntMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			gstTstrPrsntMsg.TxFlags     = 0;
			gstTstrPrsntMsg.DataSize    = 5;
			gstTstrPrsntMsg.Data[0]     = 0xC2;
			gstTstrPrsntMsg.Data[1]     = 0x33;
			gstTstrPrsntMsg.Data[2]     = TESTER_NODE_ADDRESS;
			gstTstrPrsntMsg.Data[3]     = 0x01;
			gstTstrPrsntMsg.Data[4]     = 0x00;

			// Setup pass filter to read only OBD requests / responses
			MskMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			MskMsg.TxFlags     = 0;
			MskMsg.DataSize    = 1;
			MskMsg.Data[0]     = 0x80;

			PatMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			PatMsg.TxFlags     = 0;
			PatMsg.DataSize    = 1;
			PatMsg.Data[0]     = 0x80;
			if ( (eRetVal = PassThruStartMsgFilter ( gstProtocolList[gProtocolIdx].ChannelID,
			                                        PASS_FILTER,
			                                        &MskMsg,
			                                        &PatMsg,
			                                        NULL,
			                                        &gstProtocolList[gProtocolIdx].FilterID )) != STATUS_NOERROR )
			{
				Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "%s returned %ld",
				      "PassThruStartMsgFilter",
				      eRetVal );
				return FAIL;
			}
		}
		break;

		case ISO15765:
		{
			// Handle both 11-bit and 29-bit ID cases
			if ( gstProtocolList[gProtocolIdx].eInitFlags & CAN_29BIT_ID )
			{
				// Setup ISO15765 flow control filters
				MskMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				MskMsg.TxFlags     = ISO15765_FRAME_PAD | CAN_29BIT_ID;
				MskMsg.DataSize    = 4;
				MskMsg.Data[0]     = 0xFF;
				MskMsg.Data[1]     = 0xFF;
				MskMsg.Data[2]     = 0xFF;
				MskMsg.Data[3]     = 0xFF;

				PatMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				PatMsg.TxFlags     = ISO15765_FRAME_PAD | CAN_29BIT_ID;
				PatMsg.DataSize    = 4;
				PatMsg.Data[0]     = 0x18;
				PatMsg.Data[1]     = 0xDA;
				PatMsg.Data[2]     = 0xF1;
				PatMsg.Data[3]     = 0x00;

				FlwMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				FlwMsg.TxFlags     = ISO15765_FRAME_PAD | CAN_29BIT_ID;
				FlwMsg.DataSize    = 4;
				FlwMsg.Data[0]     = 0x18;
				FlwMsg.Data[1]     = 0xDA;
				FlwMsg.Data[2]     = 0x00;
				FlwMsg.Data[3]     = 0xF1;

				bResetEcuIdx = FALSE;
				// Setup a flow control filter for each ECU that responded to SID 1 PID 0
				for ( EcuIdx = 0;
				      EcuIdx < gNumOfECUs29Bit || bResetEcuIdx;
				      EcuIdx++ )
				{
					if ( bResetEcuIdx )
					{
						EcuIdx = 0;
						bResetEcuIdx = FALSE;
					}

					bIgnoreECUId = FALSE;
					for ( IgnoreECUIdx = 0;
					      IgnoreECUIdx < gECU_Ignore_List_Count;
					      IgnoreECUIdx++ )
					{
						if ( gstResponseTA[EcuIdx] == (gpECU_Ignore_List[IgnoreECUIdx]&0x000000FF) )
						{
							bIgnoreECUId = TRUE;
							break;  // leave Ignore ECU for loop
						}
					}

					// IF the ECU ID is not to be ignored, setup flow control
					if ( bIgnoreECUId == FALSE )
					{
						PatMsg.Data[3] = gstResponseTA[EcuIdx];
						FlwMsg.Data[2] = gstResponseTA[EcuIdx];
						eRetVal = PassThruStartMsgFilter ( gstProtocolList[gProtocolIdx].ChannelID,
						                                  FLOW_CONTROL_FILTER,
						                                  &MskMsg,
						                                  &PatMsg,
						                                  &FlwMsg,
						                                  &gstProtocolList[gProtocolIdx].FlowFilterID[EcuIdx] );

						// If trying to define too many flow controls...
						if ( eRetVal == ERR_EXCEEDED_LIMIT )
						{
							// if currently J2534-1 mode, try J2534-2 mode
							if ( !gbJ2534_2_Mode )
							{
								Log ( WARNING, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "J2534-1 mode will not support required number of ECUs.\n"
								      "Atempting J2534-2 mode.\n" );

								// Disconnect Protocol
								DisconnectOBDProtocol ( );

								// Close J2534 device
								PassThruClose ( gDeviceID );

								// Try J2534-2 mode
								if ( (eRetVal = PassThruOpen ( "J2534-2:",
								                              &gDeviceID )) != STATUS_NOERROR )
								{
									Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "%s returned %ld",
									      "PassThruOpen (J2534-2:)",
									      eRetVal );
									return FAIL;
								}

								// restart flow control definitions
								bResetEcuIdx = TRUE;
								// Mode 2
								gbJ2534_2_Mode = TRUE;

								// Re-Connect to ISO15765 Protocol
								gRequestDelayTimeMsecs = 50;
								gMaxResponseTimeMsecs  = 50;
								gMinResponseTimeMsecs  = 0;
								eInitFlags = (gstProtocolList[gProtocolIdx].eInitFlags & CAN_29BIT_ID);
								eInitFlags |= ISO15765_ADDR_TYPE;
								if ( (eRetVal = PassThruConnect ( gDeviceID,
								                                 gstProtocolList[gProtocolIdx].eProtocol,
								                                 eInitFlags,
								                                 gstProtocolList[gProtocolIdx].BaudRate,
								                                 &gstProtocolList[gProtocolIdx].ChannelID )) != STATUS_NOERROR )
								{
									Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "%s returned %ld",
									      "PassThruConnect",
									      eRetVal );
									return FAIL;
								}

								// Re-Configure ISO15765 Protocol
								// Turn on LOOPBACK to see transmitted messages
								CnfgList.NumOfParams    = 1;
								CnfgList.ConfigPtr      = &CnfgParameter;
								CnfgParameter.Parameter = LOOPBACK;
								CnfgParameter.Value     = 1;
								if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
								                               SET_CONFIG,
								                               &CnfgList,
								                               NULL )) != STATUS_NOERROR )
								{
									Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "%s returned %ld",
									      "PassThruIoctl(LOOPBACK)",
									      eRetVal );
									return FAIL;
								}

								// Set CAN_MIXED_FORMAT to CAN_MIXED_FORMAT_ON
								CnfgList.NumOfParams    = 1;
								CnfgList.ConfigPtr      = &CnfgParameter;
								CnfgParameter.Parameter = CAN_MIXED_FORMAT;
								CnfgParameter.Value     = CAN_MIXED_FORMAT_ON;
								if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
								                               SET_CONFIG,
								                               &CnfgList,
								                               NULL )) != STATUS_NOERROR )
								{
									Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
									      "%s returned %ld",
									      "PassThruIoctl(CAN_MIXED_FORMAT)",
									      eRetVal );
									return FAIL;
								}
							}
							else  //  already in j2534-2 mode
							{
								Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
								      "J2534-2 mode will not support required number of ECUs.\n"
								      "%s returned %ld",
								      "PassThruStartMsgFilter",
								      eRetVal );
								return FAIL;
							}
						}  //  end if ( eRetVal == ERR_EXCEEDED_LIMIT )
						else if ( eRetVal != STATUS_NOERROR )
						{
							Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "%s returned %ld",
							      "PassThruStartMsgFilter",
							      eRetVal );
							return FAIL;
						}
					}
				}
			}
			else
			{
				// Setup ISO15765 flow control filters
				MskMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				MskMsg.TxFlags     = ISO15765_FRAME_PAD;
				MskMsg.DataSize    = 4;
				MskMsg.Data[0]     = 0xFF;
				MskMsg.Data[1]     = 0xFF;
				MskMsg.Data[2]     = 0xFF;
				MskMsg.Data[3]     = 0xFF;

				PatMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				PatMsg.TxFlags     = ISO15765_FRAME_PAD;
				PatMsg.DataSize    = 4;
				PatMsg.Data[0]     = 0x00;
				PatMsg.Data[1]     = 0x00;
				PatMsg.Data[2]     = 0x07;
				PatMsg.Data[3]     = 0xE8;

				FlwMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
				FlwMsg.TxFlags     = ISO15765_FRAME_PAD;
				FlwMsg.DataSize    = 4;
				FlwMsg.Data[0]     = 0x00;
				FlwMsg.Data[1]     = 0x00;
				FlwMsg.Data[2]     = 0x07;
				FlwMsg.Data[3]     = 0xE0;

				// Setup flow control filters for all allowable 11-bit OBD ID values
				for ( FlwMsg.Data[3] = 0xE0;
				      FlwMsg.Data[3] < 0xE8;
				      FlwMsg.Data[3]++, PatMsg.Data[3]++ )
				{
					bIgnoreECUId = FALSE;
					for ( IgnoreECUIdx = 0;
					      IgnoreECUIdx < gECU_Ignore_List_Count;
					      IgnoreECUIdx++ )
					{
						if ( ((PatMsg.Data[2] << 8) + PatMsg.Data[3]) == (unsigned short)(gpECU_Ignore_List[IgnoreECUIdx]) )
						{
							bIgnoreECUId = TRUE;
							break;  // leave ignore ECU for loop
						}
					}

					// IF the ECU ID is not to be ignored, setup flow control
					if ( bIgnoreECUId == FALSE )
					{
						if ( (eRetVal = PassThruStartMsgFilter( gstProtocolList[gProtocolIdx].ChannelID,
						                                       FLOW_CONTROL_FILTER,
						                                       &MskMsg, &PatMsg, &FlwMsg,
						                                       &gstProtocolList[gProtocolIdx].FlowFilterID[FlwMsg.Data[3] & 0x07] )) != STATUS_NOERROR )
						{
							Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "%s returned %ld",
							      "PassThruStartMsgFilter",
							      eRetVal );
							return FAIL;
						}
					}
				}
			}

			// Setup tester present keep alive message using SID 1 PID 0
			gstTstrPrsntMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
			gstTstrPrsntMsg.DataSize   = 6;
			if ( gstProtocolList[gProtocolIdx].eInitFlags & CAN_29BIT_ID )
			{
				gstTstrPrsntMsg.TxFlags = ISO15765_FRAME_PAD | CAN_29BIT_ID;
				gstTstrPrsntMsg.Data[0] = 0x18;
				gstTstrPrsntMsg.Data[1] = 0xDB;
				gstTstrPrsntMsg.Data[2] = 0x33;
				gstTstrPrsntMsg.Data[3] = TESTER_NODE_ADDRESS;
			}
			else
			{
				gstTstrPrsntMsg.TxFlags = ISO15765_FRAME_PAD;
				gstTstrPrsntMsg.Data[0] = 0x00;
				gstTstrPrsntMsg.Data[1] = 0x00;
				gstTstrPrsntMsg.Data[2] = 0x07;
				gstTstrPrsntMsg.Data[3] = 0xDF;
			}
			gstTstrPrsntMsg.Data[4] = 0x01;
			gstTstrPrsntMsg.Data[5] = 0x00;
		}  //  end case ISO15765
		break;

		default:
		{
			return FAIL;
		}
	}
	return PASS;
}


/*******************************************************************************
**
**  Function:  StartPeriodicMsg
**
**  Purpose:   Start tester present message
**
*******************************************************************************/
STATUS StartPeriodicMsg ( void )
{
	STATUS eRetVal;


	if ( gbPeriodicMsgEnabled == FALSE &&
	     gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
	{
		return PASS;
	}


#ifdef _DEBUG
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Enter StartPeriodicMsg - STATE %s, ID %x\n",
	      (bPeriodicActive == TRUE) ? "ON" : "OFF",
	      gstProtocolList[gProtocolIdx].TesterPresentID );

	if ( bPeriodicActive == TRUE )
	{
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "StartPeriodicMsg:: periodic already active - ID: %u\n",
		      gstProtocolList[gProtocolIdx].TesterPresentID );
	}
#endif


	if ( (eRetVal = PassThruStartPeriodicMsg ( gstProtocolList[gProtocolIdx].ChannelID,
	                                          &gstTstrPrsntMsg,
	                                          &gstProtocolList[gProtocolIdx].TesterPresentID,
	                                          OBD_TESTER_PRESENT_RATE )) != STATUS_NOERROR )
	{
		Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "%s returned %ld",
		      "PassThruStartPeriodicMsg",
		      eRetVal );
		return FAIL;
	}

	bPeriodicActive = TRUE;


#ifdef _DEBUG
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Leave StartPeriodicMsg - STATE %s, ID %x\n",
	      (bPeriodicActive == TRUE) ? "ON" : "OFF",
	      gstProtocolList[gProtocolIdx].TesterPresentID );
#endif


	return PASS;
}


/*******************************************************************************
**
**  Function:  StopPeriodicMsg
**
**  Purpose:   Stop tester present message
**
*******************************************************************************/
STATUS StopPeriodicMsg ( BOOL bLogError )
{
	STATUS eRetVal;

	if ( gbPeriodicMsgEnabled == FALSE &&
	     gstProtocolList[gProtocolIdx].eProtocol == ISO15765 )
	{
		return PASS;
	}


#ifdef _DEBUG
	// Leave this in for now... for debugging purposes
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Enter StopPeriodicMsg - STATE %s, ID %x\n",
	      (bPeriodicActive == TRUE) ? "ON" : "OFF",
	      gstProtocolList[gProtocolIdx].TesterPresentID );

	if ( bPeriodicActive == FALSE )
	{
		Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
		      "StopPeriodicMsg called, but periodics not active\n" );
	}
#endif


	if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
	                               CLEAR_PERIODIC_MSGS,
	                               NULL,
	                               NULL )) != STATUS_NOERROR )
	{
		if ( bLogError == TRUE )
		{
			Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%s returned %ld",
			      "PassThruIoctl(CLEAR_PERIODIC_MSGS)",
			      eRetVal );
			return FAIL;
		}
	}


	if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
	                               CLEAR_TX_BUFFER,
	                               NULL,
	                               NULL )) != STATUS_NOERROR )
	{
		if ( bLogError == TRUE )
		{
			Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%s returned %ld",
			      "PassThruIoctl(CLEAR_TX_BUFFER)",
			      eRetVal );
			return FAIL;
		}
	}

	bPeriodicActive = FALSE;


#ifdef _DEBUG
	// Leave this in for now... for debugging purposes
	Log ( INFORMATION, SCREENOUTPUTOFF, LOGOUTPUTON, NO_PROMPT,
	      "Leave StopPeriodicMsg - STATE %s, ID %x\n",
	      (bPeriodicActive == TRUE) ? "ON" : "OFF",
	      gstProtocolList[gProtocolIdx].TesterPresentID );
#endif


	return PASS;
}
