/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: ConnectOBDProtocol.c
**  Author:   EnGenius
**  Date:     2/25/2022
**  Email:    <support@autosinnovate.org>
**
**  Purpose:  SAE J1699-5 Vehicle OBD II Compliance Test Cases Source Code.
**            This source code is intended to run the tests described in
**            the SAE J1699-5 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:  03/13/2023  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 <iphlpapi.h> // Windows and IP declarations

#include "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations
#include "sockets.h"  // j1699 project socket declarations


#pragma comment(lib, "iphlpapi.lib")


// Tester present message rate 
#define OBD_TESTER_PRESENT_RATE  2000


// Functions
extern STATUS StartDoIPChannel  ( void );


// 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;
SADAPTER_INFO           AdapterInfo;

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;

extern unsigned long    gFailureCount;

extern t_stAddressData  gstTesterAddressData;
extern t_stAddressData  gstVehicleAddressData;


/*******************************************************************************
**
**  Function:  ConnectOBDProtocol
**
**  Purpose:   Connect to a protocol
**
*******************************************************************************/
STATUS ConnectOBDProtocol ( void )
{
	PASSTHRU_MSG  MskMsg;
	PASSTHRU_MSG  PatMsg;
	PASSTHRU_MSG  FlwMsg;
	SCONFIG_LIST  CnfgList;
	SCONFIG       CnfgParameter;
	unsigned long PullUpValue = 4700;  // Accepted values include 4700, 1000, 510 and -1.  -1 is used to disconnect the pull-up from the activation line
	unsigned long PinNum = 8;          // Activation line is on pin 8.
	unsigned long Voltage = 0;         // Voltage read on the activation line
	STATUS        eRetVal;
	BYTE          EcuIdx;
	unsigned long eInitFlags;
	BOOL          bResetEcuIdx;
	BYTE          IgnoreECUIdx;
	BOOL          bIgnoreECUId;

	char ErrorString[MAX_LOG_STRING_SIZE];

	// Set the request delay, maximum response time and init flags according to the protocol
	eInitFlags = 0;
	if (gstProtocolList[gProtocolIdx].eProtocol == ISO15765)
	{
		// 50 msec 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 == DOIP ||
	          gstProtocolList[gProtocolIdx].eProtocol == DOIP_NDIS )
	{
		gRequestDelayTimeMsecs = 2000;  // 2 s delay for DoIP
		gMaxResponseTimeMsecs  = 2000;  // 2 s max   for DoIP
		gMinResponseTimeMsecs  = 0;     // 0 s min   for DoIP);
	}


	if (gstProtocolList[gProtocolIdx].eProtocol == ISO15765)
	{
		// Connect to protocol
		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 ERRORS;
		}

		// 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 ERRORS;
		}
	
	
		// 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 ERRORS;
			}
		}
	
		// Setup the node IDs, functional message ID and filter(s)
		// 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 22 request
			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 ERRORS;
							}
	
							// 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 ERRORS;
							}
	
							// 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 ERRORS;
							}
	
							// 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 ERRORS;
							}
						}
						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 ERRORS;
						}
					}  //  end if ( eRetVal == ERR_EXCEEDED_LIMIT )
					else if ( eRetVal != STATUS_NOERROR )
					{
						Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "%s returned %ld",
						      "PassThruStartMsgFilter",
						      eRetVal );
						return ERRORS;
					}
				}
			}
		}
		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] = 0x00;
			PatMsg.Data[3] = 0x00;
	
			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] = 0xDF;
	
			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 ERRORS;
			}
	
	
			// 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 ERRORS;
					}
				}
			}
		}
	
		// Setup tester present keep alive message using PID $F400
		gstTstrPrsntMsg.eProtocolID = gstProtocolList[gProtocolIdx].eProtocol;
		gstTstrPrsntMsg.DataSize = 7;
		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] = 0x22;  // SID
		gstTstrPrsntMsg.Data[5] = 0xF4;  // DID MSB
		gstTstrPrsntMsg.Data[6] = 0x00;  // DID LSB
	}

	else if ( gstProtocolList[gProtocolIdx].eProtocol == DOIP_NDIS )
	{
		BYTE    Idx;
		DWORD   origFailCnt = gFailureCount;
		STATUS  eRetCode = PASS;
		DWORD   NDISPinOptionValue[] =
		{
			0x00000000,  // NDIS_PINS_AUTO_DETECT
			0x00010000,  // NDIS_PINS_OPTION1
			0x00020000   // NDIS_PINS_OPTION2
		};
		char *NDISPinOptionString[] =
		{
			"NDIS_PINS_AUTO_DETECT",
			"NDIS_PINS_OPTION1",
			"NDIS_PINS_OPTION2",
		};

//		// 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:)\n",
//			      eRetVal );
//			return ERRORS;
//		}
//		else
//		{
//			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
//			      "%s Successful\n",
//			      "PassThruOpen(J2534-2:)" );
//		}

//		// Bring up the vehicle's Ethernet
//		if ( (eRetVal = PassThruIoctl ( gDeviceID,
//		                                ETH_ACTIVATION_PULLUP,
//		                                &PullUpValue,
//		                                NULL )) != STATUS_NOERROR )
//		{
//			Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
//			      "%s returned %ld\n",
//			      "PassThruIoctl(ETH_ACTIVATION_PULLUP)",
//			      eRetVal );
////			return FAIL;
//		}
//		else
//		{
//			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
//			      "%s Successful\n",
//			      "PassThruIoctl(ETH_ACTIVATION_PULLUP)" );
//		}
	
		// wait at least 200ms
		Sleep ( 200 );  // delay for 200 ms

//		// read the voltage on the activation line
//		if ( (eRetVal = PassThruIoctl ( gDeviceID,
//		                                READ_PIN_VOLTAGE,
//		                                &PinNum,
//		                                &Voltage )) != STATUS_NOERROR )
//		{
//			Log ( J2534_FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
//			      "%s returned %ld\n",
//			      "PassThruIoctl(READ_PIN_VOLTAGE)",
//			      eRetVal );
//			eRetCode = ERRORS;
//		}
//		else
//		{
//			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
//			      "%s Successful\n",
//			      "PassThruIoctl(READ_PIN_VOLTAGE)" );
//
//			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
//			      "Activation Line: %.3f V\n",
//			      (float)Voltage/1000 );
//		}

		// Brings up the J2534 NDIS Ethernet device
		for ( Idx = 0;
		      Idx < 3;
		      Idx++ )
		{
			if ( (eRetVal = PassThruConnect ( gDeviceID,
			                                  gstProtocolList[gProtocolIdx].eProtocol,
			                                  NDISPinOptionValue[Idx],
			                                  gstProtocolList[gProtocolIdx].BaudRate,
			                                  &gstProtocolList[gProtocolIdx].ChannelID )) != STATUS_NOERROR )
			{
				PassThruGetLastError ( ErrorString );
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "PassThruConnect(%s) returned %ld - %s\n",
				      NDISPinOptionString[Idx],
				      eRetVal,
				      ErrorString );
			}
			else
			{
				Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "PassThruConnect(%s) Successful\n",
				      NDISPinOptionString[Idx] );
				break;
			}
		}
		// if no NDIS Pin option worked, protocol not supported
		if ( Idx == 3 )
		{
			return FAIL;
 		}
	
		// Get the IP address of the new J2534 NDIS adapter (IPV4_Address) as assigned by the vehicle
		if ( (eRetVal = PassThruIoctl ( gstProtocolList[gProtocolIdx].ChannelID,
		                                GET_NDIS_ADAPTER_INFO,
		                                NULL,
		                                &AdapterInfo )) != STATUS_NOERROR )
		{
			PassThruGetLastError ( ErrorString );
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "PassThruIoctl(GET_NDIS_ADAPTER_INFO) returned %ld - %s\n",
			      eRetVal,
			      ErrorString );
			eRetCode = ERRORS;
		}
		else
		{
			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "%s Successful\n",
			      "PassThruIoctl(GET_NDIS_ADAPTER_INFO)" );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "NDIS_ADAPTER_NAME:  %s\n",
			      AdapterInfo.Name );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "NDIS_ADAPTER_DEVICE_NAME:  %s\n",
			      AdapterInfo.DeviceName );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "NDIS_ADAPTER_STATUS:     %s\n",
			      AdapterInfo.Status == 0 ? "Disabled" : "Enabled" );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "NDIS_ADAPTER_MAC_ADDR:   %d.%d.%d.%d.%d.%d\n",
			      AdapterInfo.MAC_Address[0],
			      AdapterInfo.MAC_Address[1],
			      AdapterInfo.MAC_Address[2],
			      AdapterInfo.MAC_Address[3],
			      AdapterInfo.MAC_Address[4],
			      AdapterInfo.MAC_Address[5] );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "NDIS_ADAPTER_IPV6_ADDR:  %d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d.%d\n",
			      AdapterInfo.IPV6_Address[0],
			      AdapterInfo.IPV6_Address[1],
			      AdapterInfo.IPV6_Address[2],
			      AdapterInfo.IPV6_Address[3],
			      AdapterInfo.IPV6_Address[4],
			      AdapterInfo.IPV6_Address[5],
			      AdapterInfo.IPV6_Address[6],
			      AdapterInfo.IPV6_Address[7],
			      AdapterInfo.IPV6_Address[8],
			      AdapterInfo.IPV6_Address[9],
			      AdapterInfo.IPV6_Address[10],
			      AdapterInfo.IPV6_Address[11],
			      AdapterInfo.IPV6_Address[12],
			      AdapterInfo.IPV6_Address[13],
			      AdapterInfo.IPV6_Address[14],
			      AdapterInfo.IPV6_Address[15] );

			Log ( INFORMATION, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "NDIS_ADAPTER_IPV4_ADDR:  %d.%d.%d.%d\n",
			      AdapterInfo.IPV4_Address[0],
			      AdapterInfo.IPV4_Address[1],
			      AdapterInfo.IPV4_Address[2],
			      AdapterInfo.IPV4_Address[3] );
		}


		if ( origFailCnt < gFailureCount )
		{
//			return FAIL;
		}
		else if ( eRetCode != PASS )
		{
			return FAIL;
		}

		// indicate valid MAC address data
		gstTesterAddressData.bValidMACAddress = TRUE;
		// copy byte array
		memcpy ( &gstTesterAddressData.MACAddress, &AdapterInfo.MAC_Address, sizeof ( gstTesterAddressData.MACAddress ) );
		// convert to string
		getMACAddressStringFromArray ( (BYTE *)&AdapterInfo.MAC_Address, (BYTE *)&gstTesterAddressData.MACAddressString );

		// indicate valid IPv4 address data
		gstTesterAddressData.bValidIPv4Address = TRUE;
		// copy byte array
		memcpy ( &gstTesterAddressData.IPv4Address, &AdapterInfo.IPV4_Address, sizeof ( gstTesterAddressData.IPv4Address ) );
		// convert byte array to string and ulong
		gstTesterAddressData.ulIPv4Address = getIPv4AddressStringFromArray ( (BYTE *)&gstTesterAddressData.IPv4Address, (char *)&gstTesterAddressData.IPv4AddressString );

		// create udp and tcp sockets on that adapter's interface
		if ( (eRetCode = StartDoIPChannel ( )) != PASS )
		{
			return eRetCode;
		}
	}

	else if ( gstProtocolList[gProtocolIdx].eProtocol == DOIP )
	{
		STATUS  eRetCode = PASS;

//		if ( (eRetCode = FindDirectConnectInterface ( )) != PASS )
//		{
//			return eRetCode;
//		}

		// create udp and tcp sockets on that adapter's interface
		if ( (eRetCode = StartDoIPChannel ( )) != PASS )
		{
			return eRetCode;
		}
	}

	return PASS;
}


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


	if ( gbPeriodicMsgEnabled == FALSE )
	{
		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 ERRORS;
	}

	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 )
	{
		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 ERRORS;
		}
	}


	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 ERRORS;
		}
	}

	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;
}
