/*******************************************************************************
********************************************************************************
**
**  Copyright(c) 2022, Alliance for Automotive Innovation
**  Used only under license from the Alliance for Automotive Innovation. All Rights Reserved.
**
**  Project:  J1699-5
**  FileName: CheckIDSupport.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 "j2534.h"    // j1699 project j2534 declarations
#include "j1699.h"    // j1699 project general declarations


// DID type string length
#define MAX_TYPE_LENGTH  5

// ID type strings
char IDTypeString[NUM_ID_SUP_TYPES][MAX_TYPE_LENGTH] =
{
	"PID",   // PID $F4xx
	"PID",   // PID $F5xx
	"FF",    // FF
	"MID",   // MID $F6xx
	"TID",   // TID
	"INF"    // INF $F8xx
 };


/*******************************************************************************
**
**  Function:  VerifyIDSupportData
**
**  Purpose:   Verify each controller supports at a minimum one PID.
**             Any ECU that responds that does not support at least
**             one PID is flagged as an error.
**
*******************************************************************************/
STATUS VerifyIDSupportData ( IDTYPE IdType )
{
	ID_SUPPORT    *pSup;
	BYTE           SupSize;
	BYTE           EcuIdx;
	STATUS         eRetCode = PASS;

	BOOL           bIDSupport;
	unsigned long  Index;
	BYTE           IDMSB;


	// For each ECU
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( IdType == PIDREQUEST )
		{
			SupSize = gstResponse[EcuIdx].PIDSupportSize;
			pSup = &gstResponse[EcuIdx].PIDSupport[0];
			IDMSB = 0xF4;
		}
		else if ( IdType == PF5REQUEST )
		{
			SupSize = gstResponse[EcuIdx].PF5SupportSize;
			pSup = &gstResponse[EcuIdx].PF5Support[0];
			IDMSB = 0xF5;
		}
		else if ( IdType == MIDREQUEST )
		{
			SupSize = gstResponse[EcuIdx].MIDSupportSize;
			pSup = &gstResponse[EcuIdx].MIDSupport[0];
			IDMSB = 0xF6;
		}
		else
		{
			SupSize = gstResponse[EcuIdx].INFSupportSize;
			pSup = &gstResponse[EcuIdx].INFSupport[0];
			IDMSB = 0xF8;
		}

		bIDSupport = FALSE;
		// PID $00 (index 0) not required to indicate PIDs Supported
		for ( Index = 1;
		      Index < SupSize;
		      Index++ )
		{
			// If no PID is supported, fail
			if ( pSup[Index].SupBits[0] == 0 &&
			     pSup[Index].SupBits[1] == 0 &&
			     pSup[Index].SupBits[2] == 0 &&
			     pSup[Index].SupBits[3] == 0 &&
			     SupSize > 1 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  DID-Supported DID $%02X%02X indicates no %ss supported\n",
				      GetEcuId ( EcuIdx ),
				      IDMSB,
				      pSup[Index].ID,
				      IDTypeString[IdType] );
				eRetCode = FAIL;
			}
		}
	}
	return eRetCode;
}


/*******************************************************************************
**
**  Function:  RequestIDSupportData
**
**  Purpose:   Identify support for Service Support IDs
**
*******************************************************************************/
STATUS RequestIDSupportData ( IDTYPE IdType,
                              BOOL   bVerifyECUCommunication )
{
	REQ_MSG       stReqMsg;
	ID_SUPPORT   *pSup;
	WORD          SupSize;
	BYTE          EcuIdx = 0;
	STATUS        RetCode;

	unsigned int  IdIdx;
	unsigned int  DidMsb;
	BOOL          bIDSupport = TRUE;
	BOOL          bDataError = FALSE;
	BOOL          bTestFailed = FALSE;


	if ( IdType == PIDREQUEST )
	{
		DidMsb = 0xF400;
	}
	else if ( IdType == PF5REQUEST )
	{
		DidMsb = 0xF500;
	}
	else if ( IdType == MIDREQUEST )
	{
		DidMsb = 0xF600;
	}
	else
	{
		DidMsb = 0xF800;
	}

	// Request SID 22 support data
	for ( IdIdx = 0x00;
	      IdIdx <= 0xE0;
	      IdIdx += 0x20 )
	{
		bDataError = FALSE;

		stReqMsg.SID      = 0x22;
		stReqMsg.NumIds   = 1;
		stReqMsg.u.DID[0] = DidMsb + IdIdx;
		if ( (gbPhysicalAddressing == FALSE &&
		      (RetCode = RequestSID ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE )) != FAIL ) ||
		     (gbPhysicalAddressing == TRUE  &&
		      (RetCode = RequestSID_PhysicallyAddressed_All ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE )) != FAIL) )
		if ( bIDSupport == TRUE &&
		     RetCode == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "Error in response to DID-Supported DID $%04X request\n",
			      DidMsb + IdIdx );
			bTestFailed = TRUE;
			bDataError = TRUE;
		}


		bIDSupport = FALSE;
		
		// Check if we need to request the next group
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( IdType == PIDREQUEST )
			{
				pSup = &gstResponse[EcuIdx].PIDSupport[0];
				SupSize = gstResponse[EcuIdx].PIDSize;
			}
			else if ( IdType == PF5REQUEST )
			{
				pSup = &gstResponse[EcuIdx].PF5Support[0];
				SupSize = gstResponse[EcuIdx].PIDSize;
			}
			else if ( IdType == MIDREQUEST )
			{
				pSup = &gstResponse[EcuIdx].MIDSupport[0];
				SupSize = gstResponse[EcuIdx].MIDSize;
			}
			else
			{
				pSup = &gstResponse[EcuIdx].INFSupport[0];
				SupSize = gstResponse[EcuIdx].INFSize;
			}

			// verify that this supported DID-Support DID has response data
			if ( IdIdx != 0x00 &&
			     pSup[(IdIdx >> 5)-1 ].SupBits[3] & 0x01 )
			{
				// if received a nak
				if ( gstResponse[EcuIdx].bNAKReceived )
				{
					// nak $31 (ID not supported)
					if ( gstResponse[EcuIdx].NAKCode == NAK_ID_NOT_SUPPORTED )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  DID $%04X  supported but received NAK $31 (Unsupported DID request)!\n",
						      GetEcuId ( EcuIdx ),
						      DidMsb + IdIdx );
					}
					// other nak codes handles in RequestSID.Process___Msg
					bDataError = TRUE;
				}
				else if ( pSup[(IdIdx >> 5)].ID == 0 )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  DID-Support DID $%04X indicates support of DID-Support DID $%04X, but did not respond!\n",
					      GetEcuId ( EcuIdx ),
					      DidMsb + IdIdx - 0x20,
					      DidMsb + IdIdx );
					bTestFailed = TRUE;
					bDataError = TRUE;
				}

				// if $F4E0 indicates support for $F500, fail
				if ( IdType == PIDREQUEST &&
				     IdIdx == 0xE0 &&
				     (gstResponse[EcuIdx].PIDSupport[(IdIdx >> 5)].SupBits[3] & 0x01) )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "DID $F4E0 indicates support of $F500 (Not allowed after MY 2026)\n" );
					bTestFailed = TRUE;
				}
				
			}
			// verify that this unsupported DID-Support DID has no response
			else if ( IdIdx != 0x00 &&
			          (pSup[(IdIdx >> 5)-1 ].SupBits[3] & 0x01) == 0 )
			{
				// if received a nak
				if ( gstResponse[EcuIdx].bNAKReceived == TRUE )
				{
					// nak $31 (ID not supported)
					if ( gstResponse[EcuIdx].NAKCode == NAK_ID_NOT_SUPPORTED &&
					     gbPhysicalAddressing == FALSE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  DID $%04X  NAK $31 (Unsupported DID request) for functionally addressed request!\n",
						      GetEcuId ( EcuIdx ),
						      DidMsb + IdIdx );
					}
					// other nak codes handles in RequestSID.Process___Msg
				}
				// if a response with data was received
				else if ( SupSize != 0 )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Unexpected response to unsupported SID $22 DID $%04X!\n",
					      GetEcuId ( EcuIdx ),
					      DidMsb + IdIdx );
					bTestFailed = TRUE;
				}
				// if a response with no data was received
				else if ( SupSize == 0 &&
				          gstResponse[EcuIdx].bResponseReceived == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  No data in response to unsupported SID $22 DID $%04X!\n",
					      GetEcuId ( EcuIdx ),
					      DidMsb + IdIdx );
					bTestFailed = TRUE;
				}
			}

			// if this DID-Support DID indicates that the next DID-Support DID is supported
			if ( pSup[(IdIdx >> 5)].SupBits[3] & 0x01 )
			{
				bIDSupport = TRUE;
			}
		}

		// If no ECU supports the next DID support DID, end requests
		if ( bIDSupport == FALSE &&
		     EcuIdx >= gNumOfECUs )
		{
			// if no bad response, nak, etc, the next DID-Support DID really is unsupported
			if ( bDataError == FALSE )
			{
				break;  // leave support ID for loop
			}
			// indicate that the next DID-Support DID is not supported possibly because of data error
			bIDSupport = FALSE;
		}
	}


	// Verify support information
	VerifyIDSupportData ( IdType );


	// Request next unsupported DID-Supported DID
	if ( IdIdx < 0xE0 &&
	          (IdType != INFREQUEST ||
	           geTestPhase != eTestNoDTC || gTestSubsection != 17) )  // IF not Test 5.17
	{
		IdIdx += 0x20;

		stReqMsg.SID      = 0x22;
		stReqMsg.NumIds   = 1;
		stReqMsg.u.DID[0] = DidMsb + IdIdx;
		if ( (gbPhysicalAddressing == FALSE &&
		      (RetCode = RequestSID ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE )) != FAIL ) ||
		     (gbPhysicalAddressing == TRUE  &&
		      (RetCode = RequestSID_PhysicallyAddressed_All ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE )) != FAIL) )
		{
			for ( EcuIdx = 0;
			      EcuIdx < gNumOfECUs;
			      EcuIdx++ )
			{
				if ( IdType == PIDREQUEST )
				{
					SupSize = gstResponse[EcuIdx].PIDSize;
				}
				else if ( IdType == PF5REQUEST )
				{
					SupSize = gstResponse[EcuIdx].PIDSize;
				}
				else if ( IdType == MIDREQUEST )
				{
					SupSize = gstResponse[EcuIdx].MIDSize;
				}
				else
				{
					SupSize = gstResponse[EcuIdx].INFSize;
				}

				// if received a nak
				if ( gstResponse[EcuIdx].bNAKReceived == TRUE )
				{
					// nak $31 (ID not supported)
					if ( gstResponse[EcuIdx].NAKCode == NAK_ID_NOT_SUPPORTED &&
					     gbPhysicalAddressing == FALSE )
					{
						Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
						      "ECU %X  DID $%04X  NAK $31 (Unsupported DID request) for functionally addressed request!\n",
						      GetEcuId ( EcuIdx ),
						      DidMsb + IdIdx );
					}
					// other nak codes handles in RequestSID.Process___Msg
				}
				// if a response with data was received
				else if ( SupSize != 0 )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  Unexpected response to unsupported SID $22 DID $%04X!\n",
					      GetEcuId ( EcuIdx ),
					      DidMsb + IdIdx );
					bTestFailed = TRUE;
				}
				else if ( SupSize == 0 &&
				          gstResponse[EcuIdx].bResponseReceived == TRUE )
				{
					Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
					      "ECU %X  No data in response to unsupported SID $22 DID $%04X!\n",
					      GetEcuId ( EcuIdx ),
					      DidMsb + IdIdx );
					bTestFailed = TRUE;
				}
			}
		}
	}

	// Verify ECU did not drop out
	if ( bVerifyECUCommunication &&
	     VerifyECUCommunication ( ) != PASS )
	{
		bTestFailed = TRUE;
	}

	if ( bTestFailed == TRUE )
	{
		return FAIL;
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  RequestGroupPIDSupportData
**
**  Purpose:   Verify ID group support
**
*******************************************************************************/
STATUS RequestGroupPIDSupportData ( IDTYPE IdType )
{
	REQ_MSG       stReqMsg;
	BYTE          EcuIdx;

	unsigned int  NumIds;
	unsigned int  IdIdx;
	unsigned int  NextSupportOffset = 2;
	BOOL          bReqNextGroup;  // Binary logic for request for next group!
	BOOL          bTestFailed = FALSE;


	// Save the original response data for comparison
	CopyIDSupport ( IdType );


	// Set starting DID Support DID
	if ( IdType == PIDREQUEST )
	{
		IdIdx = 0xF400;
	}
	else if ( IdType == PF5REQUEST )
	{
		IdIdx = 0xF500;
	}
	else if ( IdType == MIDREQUEST )
	{
		IdIdx = 0xF600;
	}
	else
	{
		IdIdx = 0xF800;
	}


	do
	{
		bReqNextGroup = FALSE;
		// highest group request is         $F4C0 $F4E0
		// But     lower group requests are $F460 $F480 $F4A0
		// and                              $F400 $F420 $F440
		if ( (IdIdx & 0x00FF) < 0xC0 )
		{
			NumIds = 3;
		}
		else
		{
			NumIds = 2;
		}
	
		// Request DID support data as a group
		stReqMsg.SID      = 0x22;
		stReqMsg.NumIds   = NumIds;
		stReqMsg.u.DID[0] = IdIdx;
		stReqMsg.u.DID[1] = IdIdx + 0x20;
		if ( NumIds == 3 )
		{
			stReqMsg.u.DID[2] = IdIdx + 0x40;
		}
		if ( RequestSID ( &stReqMsg, REQ_MSG_NORMAL|REQ_MSG_ALLOW_NO_RESPONSE ) == FAIL )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "SID $22 group $%04X-$%04X support request failed\n",
			      IdIdx,
			      IdIdx + (0x20 * (NumIds-1)) );
			bTestFailed = TRUE;
		}

		// Set next group select to false
		bReqNextGroup = FALSE;

		// Determine if next group support request is required
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( (IdType == PIDREQUEST && (gstResponse[EcuIdx].PIDSupport[NextSupportOffset].SupBits[3] & 0x01) ) ||
			     (IdType == PF5REQUEST && (gstResponse[EcuIdx].PF5Support[NextSupportOffset].SupBits[3] & 0x01) ) ||
			     (IdType == MIDREQUEST && (gstResponse[EcuIdx].MIDSupport[NextSupportOffset].SupBits[3] & 0x01) ) ||
			     (IdType == INFREQUEST && (gstResponse[EcuIdx].INFSupport[NextSupportOffset].SupBits[3] & 0x01) ) )
			{
				bReqNextGroup = TRUE;
				IdIdx += 0x60;
				NextSupportOffset += 3;
				break;  // leave ECU for loop
			}
		}
	}
	while ( bReqNextGroup == TRUE );

	// Verify that all INF group/individual support data compares
	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( CompareIDSupport ( EcuIdx,
		                        IdType,
		                        TRUE ) == FAIL )
		{
			bTestFailed = TRUE;
		}
	}

	if ( bTestFailed == TRUE )
	{
		// Something was wrong, restore the original response data
		RestoreIDSupport ( IdType );

		return FAIL;
	}

	return PASS;
}


/*******************************************************************************
**
**  Function:  CopyIDSupport
**
**  Purpose:   Copy PID Support responses for SID $22 DID $F4xx, $F6xx, $F8xx
**
*******************************************************************************/
void CopyIDSupport ( IDTYPE IdType )
{
	BYTE        EcuIdx;
	ID_SUPPORT *pSup;

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( IdType == PIDREQUEST )
		{
			gstResponse[EcuIdx].CompareSupportSize = gstResponse[EcuIdx].PIDSupportSize;
			pSup = &gstResponse[EcuIdx].PIDSupport[0];
		}
		else if ( IdType == PF5REQUEST )
		{
			gstResponse[EcuIdx].CompareSupportSize = gstResponse[EcuIdx].PF5SupportSize;
			pSup = &gstResponse[EcuIdx].PF5Support[0];
		}
		else if ( IdType == MIDREQUEST )
		{
			gstResponse[EcuIdx].CompareSupportSize = gstResponse[EcuIdx].MIDSupportSize;
			pSup = &gstResponse[EcuIdx].MIDSupport[0];
		}
		else
		{
			gstResponse[EcuIdx].CompareSupportSize = gstResponse[EcuIdx].INFSupportSize;
			pSup = &gstResponse[EcuIdx].INFSupport[0];
		}

		memcpy ( &gstResponse[EcuIdx].CompareSupport[0],
		         pSup,
		         sizeof ( ID_SUPPORT ) * MAX_NUM_SUP_BYTES );
	}
}


/*******************************************************************************
**
**  Function:  RestoreIDSupport
**
**  Purpose:   Restore PID Support responses for SID $22 DID $F4xx, $F6xx, $F8xx
**
*******************************************************************************/
void RestoreIDSupport ( IDTYPE IdType )
{
	BYTE        EcuIdx;
	ID_SUPPORT *pSup;

	for ( EcuIdx = 0;
	      EcuIdx < gNumOfECUs;
	      EcuIdx++ )
	{
		if ( IdType == PIDREQUEST )
		{
			gstResponse[EcuIdx].PIDSupportSize = gstResponse[EcuIdx].CompareSupportSize;
			pSup = &gstResponse[EcuIdx].PIDSupport[0];
		}
		else if ( IdType == PF5REQUEST )
		{
			gstResponse[EcuIdx].PF5SupportSize = gstResponse[EcuIdx].CompareSupportSize;
			pSup = &gstResponse[EcuIdx].PF5Support[0];
		}
		else if ( IdType == MIDREQUEST )
		{
			gstResponse[EcuIdx].MIDSupportSize = gstResponse[EcuIdx].CompareSupportSize;
			pSup = &gstResponse[EcuIdx].MIDSupport[0];
		}
		else
		{
			gstResponse[EcuIdx].INFSupportSize = gstResponse[EcuIdx].CompareSupportSize;
			pSup = &gstResponse[EcuIdx].INFSupport[0];
		}

		memcpy ( pSup,
		         &gstResponse[EcuIdx].CompareSupport[0],
		         sizeof ( ID_SUPPORT ) );
	}
}


/*******************************************************************************
**
**  Function:  CompareIDSupport
**
**  Purpose:   Compare normal/single and reverse/group
**             PID Support responses for SID $22 DID $F4xx, 5, $F6xx, 8, $F8xx
**
*******************************************************************************/
STATUS CompareIDSupport ( BYTE        EcuIdx,   // The index of the current ECU
                          IDTYPE      IdType,   // Type of DID support array
                          BOOL        bGroup )  // Flag indicating if this is a group DID Support response
{
	BYTE          ResponseSize;  // Size of group/reverse DID Support response
	ID_SUPPORT   *pResponseData;  // Data of group/reverse DID Support response
	unsigned int  IdOffset;
	unsigned int  IdIdx;
	unsigned int  IdBitIndex;
	unsigned int  BitIndex;
	BYTE          BitMask;
	BOOL          bTestFailed = FALSE;


	if ( IdType == PIDREQUEST )
	{
		IdOffset = 0xF400;
		ResponseSize = gstResponse[EcuIdx].PIDSupportSize;
		pResponseData = &gstResponse[EcuIdx].PIDSupport[0];
	}
	else if ( IdType == PF5REQUEST )
	{
		IdOffset = 0xF500;
		ResponseSize = gstResponse[EcuIdx].PF5SupportSize;
		pResponseData = &gstResponse[EcuIdx].PF5Support[0];
	}
	else if ( IdType == MIDREQUEST )
	{
		IdOffset = 0xF600;
		ResponseSize = gstResponse[EcuIdx].MIDSupportSize;
		pResponseData = &gstResponse[EcuIdx].MIDSupport[0];
	}
	else
	{
		IdOffset = 0xF800;
		ResponseSize = gstResponse[EcuIdx].INFSupportSize;
		pResponseData = &gstResponse[EcuIdx].INFSupport[0];
	}


	if ( ResponseSize != gstResponse[EcuIdx].CompareSupportSize )
	{
		Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
		      "ECU %X  SID $22 %s Supported %s %s size mismatch\n"
		      "                          %s response size = %d\n"
		      "                          %s response size = %d\n",
		      GetEcuId ( EcuIdx ),
		      IDTypeString[IdType],
		      IDTypeString[IdType],
		      bGroup ? "group/single" : "reverse/normal",
		      bGroup ? "Group " : "Reverse",
		      ResponseSize,
		      bGroup ? "Single" : "Normal ",
		      gstResponse[EcuIdx].CompareSupportSize );
		bTestFailed = TRUE;
	}

	// Verify that all SID $22 DID support data compares
	for ( IdIdx = 0;
	      IdIdx < 8;
	      IdIdx++ )
	{
		if ( gstResponse[EcuIdx].CompareSupport[IdIdx].ID != pResponseData[IdIdx].ID )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  SID $22 %s $%04X %s support response Support ID mismatch\n",
			      GetEcuId ( EcuIdx ),
			      IDTypeString[IdType],
			      IdOffset + (IdIdx * 0x20),
			      bGroup ? "group/single" : "reverse/normal",
			      gstResponse[EcuIdx].CompareSupport[IdIdx].ID );
			bTestFailed = TRUE;

			if ( gstResponse[EcuIdx].CompareSupport[IdIdx].ID != IdIdx * 0x20 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $22 %s $%04X invalid %s support response Support ID ($%02X)\n",
				      GetEcuId ( EcuIdx ),
				      IDTypeString[IdType],
				      IdOffset + (IdIdx * 0x20),
				      bGroup ? "single" : "normal",
				      gstResponse[EcuIdx].CompareSupport[IdIdx].ID );
			}

			if ( pResponseData[IdIdx].ID != IdIdx * 0x20 )
			{
				Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
				      "ECU %X  SID $22 %s $%04X invalid %s support response Support ID ($%02X)\n",
				      GetEcuId ( EcuIdx ),
				      IDTypeString[IdType],
				      IdOffset + (IdIdx * 0x20),
				      bGroup ? "group" : "reverse",
				      pResponseData[IdIdx].ID );
			}
		}

		if ( pResponseData[IdIdx].SupBits[0] != gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[0] ||
		     pResponseData[IdIdx].SupBits[1] != gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[1] ||
		     pResponseData[IdIdx].SupBits[2] != gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[2] ||
		     pResponseData[IdIdx].SupBits[3] != gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[3] )
		{
			Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
			      "ECU %X  SID $22 %s $%04X %s support response data mismatch\n"
			      "                         %7s response = %02X  %02X  %02X  %02X\n"
			      "                         %7s response = %02X  %02X  %02X  %02X\n",
			      GetEcuId ( EcuIdx ),
			      IDTypeString[IdType],
			      IdOffset + (IdIdx * 0x20),
			      bGroup ? "group/single" : "reverse/normal",
			      bGroup ? "Group " : "Reverse",
			      pResponseData[IdIdx].SupBits[0],
			      pResponseData[IdIdx].SupBits[1],
			      pResponseData[IdIdx].SupBits[2],
			      pResponseData[IdIdx].SupBits[3],
			      bGroup ? "Single" : "Normal ",
			      gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[0],
			      gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[1],
			      gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[2],
			      gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[3] );
			bTestFailed = TRUE;

			for ( IdBitIndex = 0;
			      IdBitIndex < 4;
			      IdBitIndex++ )
			{
				if ( (BitMask = (pResponseData[IdIdx].SupBits[IdBitIndex] ^ gstResponse[EcuIdx].CompareSupport[IdIdx].SupBits[IdBitIndex])) != 0 )
				{
					for ( BitIndex = 8;
					      BitIndex != 0;
					      BitIndex--)
					{
						if ( (BitMask & 0x80) != 0 )
						{
							Log ( FAILURE, SCREENOUTPUTON, LOGOUTPUTON, NO_PROMPT,
							      "ECU %X  SID $22 %s $%04X Byte %d:  %s $%04X support response mismatch\n",
							      GetEcuId ( EcuIdx ),
							      IDTypeString[IdType],
							      IdOffset + (IdIdx * 0x20),
							      IdBitIndex,
							      IDTypeString[IdType],
							      IdOffset + (IdIdx * 0x20) + (IdBitIndex * 8) + (8 - (BitIndex - 1)) );
						}
						BitMask <<= 1;
					}
				}
			}
		}
	}

	if ( bTestFailed == TRUE )
	{
		return FAIL;
	}
	else
	{
		return PASS;
	}
}


/*******************************************************************************
**
**  Function:  IsIDSupported
**
**  Purpose:   Determine if SID $22 DID $xx is supported on specific ECU.
**             Need to have called RequestIDSupportData( ) previously.
**             If EcuIdx < 0 then check all ECUs.
**
*******************************************************************************/
BOOL IsIDSupported ( BYTE     EcuIdx,
                     IDTYPE  IdType,
                     WORD    IdIdx )
{
	unsigned int index1;
	unsigned int index2;
	unsigned int mask;

	WORD         IDBase;
	ID_SUPPORT  *pSupport;


	if ( IdType == PIDREQUEST )
	{
		IDBase = 0xF400;
		pSupport = &gstResponse[EcuIdx].PIDSupport[0];
	}
	else if ( IdType == PF5REQUEST )
	{
		IDBase = 0xF500;
		pSupport = &gstResponse[EcuIdx].PF5Support[0];
	}
	else if ( IdType == MIDREQUEST )
	{
		IDBase = 0xF600;
		pSupport = &gstResponse[EcuIdx].MIDSupport[0];
	}
	else
	{
		IDBase = 0xF800;
		pSupport = &gstResponse[EcuIdx].INFSupport[0];
	}

	//  if the ID is $Fx00,
	if ( (IdIdx & 0x00FF) == 0 )
	{
		// if checking for support of next range (ie if $F4E0 supports $F500),
		if ( (IdIdx - IDBase) == 0x100 )
		{
			// if DID Support DID $E0 byte 3 bit 0 is set. it supports it
			if ( (pSupport[7].SupBits[3] & 0x01) != 0x00 )
			{
				return TRUE;
			}
			else
			{
				return FALSE;
			}
		}
		// otherwise, all modules must support PID/MID/INF $00
		return TRUE;
	}
	IdIdx &= 0x00FF;

	IdIdx--;
	index1 = IdIdx >> 5;
	index2 = (IdIdx >> 3) & 0x03;
	mask   = 0x80 >> (IdIdx & 0x07);

	if ( EcuIdx == ALLECUS )
	{
		for ( EcuIdx = 0;
		      EcuIdx < gNumOfECUs;
		      EcuIdx++ )
		{
			if ( (IdType == PIDREQUEST && (gstResponse[EcuIdx].PIDSupport[index1].SupBits[index2] & mask) != 0x00) ||
			     (IdType == PF5REQUEST && (gstResponse[EcuIdx].PF5Support[index1].SupBits[index2] & mask) != 0x00) ||
			     (IdType == MIDREQUEST && (gstResponse[EcuIdx].MIDSupport[index1].SupBits[index2] & mask) != 0x00) ||
			     (IdType == INFREQUEST && (gstResponse[EcuIdx].INFSupport[index1].SupBits[index2] & mask) != 0x00) )
			{
				return TRUE;
			}
		}
	}
	else
	{
		if ( (IdType == PIDREQUEST && (gstResponse[EcuIdx].PIDSupport[index1].SupBits[index2] & mask) != 0x00) ||
		     (IdType == PF5REQUEST && (gstResponse[EcuIdx].PF5Support[index1].SupBits[index2] & mask) != 0x00) ||
		     (IdType == MIDREQUEST && (gstResponse[EcuIdx].MIDSupport[index1].SupBits[index2] & mask) != 0x00) ||
		     (IdType == INFREQUEST && (gstResponse[EcuIdx].INFSupport[index1].SupBits[index2] & mask) != 0x00) )
		{
			return TRUE;
		}
	}

	return FALSE;
}
