//
// Copyright (c) 2015-2016 DashLogic, Inc.
// All Rights Reserved.
//
// http://www.dashlogic.com
// sales@dashlogic.com
//
// Redistribution and use in source and binary forms, with or without
// modification, including use for commercial purposes, are permitted
// provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in
//    the documentation and/or other materials provided with the
//    distribution.
//
// 3. Neither the name of the copyright holder nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
// 4. Redistributions of any form whatsoever must retain the following
//    acknowledgment: 'This product includes software developed by 
//    "DashLogic, Inc." (http://www.dashlogic.com/).'
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//


//
// Formatting:
//  Indents:	Use tabs only (1 tab per indent)
//	Tab Size:	4 spaces
//
// File Revision:
// $Rev: 5220 $
// $Date: 2016-03-16 08:31:37 -0600 (Wed, 16 Mar 2016) $
//


#include "passthruhelper0404.h"
#include "J2534_v0404.h"

#include <string.h>
#include <stdio.h>

#if defined(_WIN32) || defined(_WIN64)
	#define WIN32_LEAN_AND_MEAN
	#include <windows.h>
#endif

#ifndef NULL
  #define NULL		0
#endif

#ifndef TRUE
  #define TRUE		1
#endif

#ifndef FALSE
  #define FALSE		0
#endif





#define BEGIN_J2534_API_CALL()		{ m_bHasLocalError = FALSE; if (m_pApiFuncs == NULL) { return ReturnLocalError(ERR_FAILED, "J2534 Pass-Thru library not loaded."); } }





////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PassThruHelper0404ApiFuncs Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PassThruHelper0404ApiFuncs::PassThruHelper0404ApiFuncs()
{
	m_pPassThruOpenFunc = NULL;
	m_pPassThruCloseFunc = NULL;
	m_pPassThruConnectFunc = NULL;
	m_pPassThruDisconnectFunc = NULL;
	m_pPassThruReadMsgsFunc = NULL;
	m_pPassThruWriteMsgsFunc = NULL;
	m_pPassThruStartPeriodicMsgFunc = NULL;
	m_pPassThruStopPeriodicMsgFunc = NULL;
	m_pPassThruStartMsgFilterFunc = NULL;
	m_pPassThruStopMsgFilterFunc = NULL;
	m_pPassThruSetProgrammingVoltageFunc = NULL;
	m_pPassThruReadVersionFunc = NULL;
	m_pPassThruGetLastErrorFunc = NULL;
	m_pPassThruIoctlFunc = NULL;
}

PassThruHelper0404ApiFuncs::~PassThruHelper0404ApiFuncs()
{

}

long PassThruHelper0404ApiFuncs::PassThruOpen(void *pName, unsigned long *pDeviceID)
{
	return ((PTOPEN)m_pPassThruOpenFunc)(pName, pDeviceID);
}

long PassThruHelper0404ApiFuncs::PassThruClose(unsigned long DeviceID)
{
	return ((PTCLOSE)m_pPassThruCloseFunc)(DeviceID);
}

long PassThruHelper0404ApiFuncs::PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID)
{
	return ((PTCONNECT)m_pPassThruConnectFunc)(DeviceID, ProtocolID, Flags, BaudRate, pChannelID);
}

long PassThruHelper0404ApiFuncs::PassThruDisconnect(unsigned long ChannelID)
{
	return ((PTDISCONNECT)m_pPassThruDisconnectFunc)(ChannelID);
}

long PassThruHelper0404ApiFuncs::PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
{
	return ((PTREADMSGS)m_pPassThruReadMsgsFunc)(ChannelID, pMsg, pNumMsgs, Timeout);
}

long PassThruHelper0404ApiFuncs::PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
{
	return ((PTWRITEMSGS)m_pPassThruWriteMsgsFunc)(ChannelID, pMsg, pNumMsgs, Timeout);
}

long PassThruHelper0404ApiFuncs::PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval)
{
	return ((PTSTARTPERIODICMSG)m_pPassThruStartPeriodicMsgFunc)(ChannelID, pMsg, pMsgID, TimeInterval);
}

long PassThruHelper0404ApiFuncs::PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID)
{
	return ((PTSTOPPERIODICMSG)m_pPassThruStopPeriodicMsgFunc)(ChannelID, MsgID);
}

long PassThruHelper0404ApiFuncs::PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID)
{
	return ((PTSTARTMSGFILTER)m_pPassThruStartMsgFilterFunc)(ChannelID, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID);
}

long PassThruHelper0404ApiFuncs::PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID)
{
	return ((PTSTOPMSGFILTER)m_pPassThruStopMsgFilterFunc)(ChannelID, FilterID);
}

long PassThruHelper0404ApiFuncs::PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage)
{
	return ((PTSETPROGRAMMINGVOLTAGE)m_pPassThruSetProgrammingVoltageFunc)(DeviceID, PinNumber, Voltage);
}

long PassThruHelper0404ApiFuncs::PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion)
{
	return ((PTREADVERSION)m_pPassThruReadVersionFunc)(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion);
}

long PassThruHelper0404ApiFuncs::PassThruGetLastError(char *pErrorDescription)
{
	return ((PTGETLASTERROR)m_pPassThruGetLastErrorFunc)(pErrorDescription);
}

long PassThruHelper0404ApiFuncs::PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput)
{
	return ((PTIOCTL)m_pPassThruIoctlFunc)(ChannelID, IoctlID, pInput, pOutput);
}







////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PassThruHelper0404 Implementation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
PassThruHelper0404::PassThruHelper0404()
{
	unsigned long	Index;

	m_pApiFuncs = NULL;
	m_bUserProvidedApiFuncs = FALSE;

	m_PassThruLibraryHandle = NULL;
	m_bPassThruLibraryHandleValid = FALSE;

	for (Index=0; Index<PT0404HELPER_MAX_DEVICES; Index++)
	{
		m_DeviceList[Index].m_bDeviceOpen = FALSE;
		m_DeviceList[Index].m_DeviceId = (unsigned long)-1;
	}

	for (Index=0; Index<PT0404HELPER_MAX_CHANNELS; Index++)
	{
		m_ChannelList[Index].m_bChannelOpen = FALSE;
		m_ChannelList[Index].m_DeviceId = (unsigned long)-1;
		m_ChannelList[Index].m_ChannelId = (unsigned long)-1;
	}

	for (Index=0; Index<PT0404HELPER_MAX_FILTERS; Index++)
	{
		m_FilterList[Index].m_bFilterInUse = FALSE;
		m_FilterList[Index].m_ChannelId = (unsigned long)-1;
		m_FilterList[Index].m_FilterId = (unsigned long)-1;
	}

	for (Index=0; Index<PT0404HELPER_MAX_PERIODICS; Index++)
	{
		m_PeriodicList[Index].m_bPeriodicInUse = FALSE;
		m_PeriodicList[Index].m_ChannelId = (unsigned long)-1;
		m_PeriodicList[Index].m_PeriodicId = (unsigned long)-1;
	}
}

PassThruHelper0404::~PassThruHelper0404()
{
	CloseAllAndUnloadPassThruLibrary();
}

bool PassThruHelper0404::EnumeratePassThruDevices(ENUMERATE_PASSTHRU_DEVICES_CALLBACK_PROTOTYPE pCallback)
{
	HKEY			hPassThruSupportKey;
	char			strVendorDeviceKeyName[256];
	char			strVendorName[256];
	char			strDeviceName[256];
	HKEY			hVendorDeviceKey;
	DWORD			KeyType;
	DWORD			KeySize;
	DWORD			KeyIndex;
	char			LibraryPath[MAX_PATH];
	LONG			EnumStatus;
	bool			bSuccess = TRUE;
	char			strErrorDescription[100];

	// Open all PassThruSupport (v04.04) devices
	if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\PassThruSupport.04.04", 0, KEY_READ, &hPassThruSupportKey) == ERROR_SUCCESS)
	{
		KeySize = 256;
		KeyIndex = 0;
		EnumStatus = RegEnumKeyExA(hPassThruSupportKey, KeyIndex, strVendorDeviceKeyName, &KeySize, NULL, NULL, NULL, NULL);

		// Find all available interfaces in the registry
		while (EnumStatus == ERROR_SUCCESS)
		{
			// Open this J2534 device's registry key
			if (RegOpenKeyExA(hPassThruSupportKey, strVendorDeviceKeyName, 0, KEY_READ, &hVendorDeviceKey) == ERROR_SUCCESS)
			{
				// Get the device function library
				KeySize = sizeof(LibraryPath);
				if (RegQueryValueExA(hVendorDeviceKey, "FunctionLibrary", 0, &KeyType, (BYTE*)LibraryPath,  &KeySize) == ERROR_SUCCESS)
				{
					// Get the device name
					KeySize = sizeof(strDeviceName);
					if (RegQueryValueExA(hVendorDeviceKey, "Name", 0, &KeyType, (BYTE*)strDeviceName,  &KeySize) == ERROR_SUCCESS)
					{
						// Get the vendor name
						KeySize = sizeof(strVendorName);
						if (RegQueryValueExA(hVendorDeviceKey, "Vendor", 0, &KeyType, (BYTE*)strVendorName,  &KeySize) == ERROR_SUCCESS)
						{
							pCallback(strVendorName, strDeviceName, LibraryPath);
						}
						else
						{
							_snprintf(strErrorDescription, sizeof(strErrorDescription)-1, "Cannot query %s\\Vendor key. Error %u.", strVendorDeviceKeyName, GetLastError());
							bSuccess = FALSE;
						}
					}
					else
					{
						_snprintf(strErrorDescription, sizeof(strErrorDescription)-1, "Cannot query %s\\Name key. Error %u.", strVendorDeviceKeyName, GetLastError());
						bSuccess = FALSE;
					}
				}
				else
				{
					_snprintf(strErrorDescription, sizeof(strErrorDescription)-1, "Cannot query %s\\FunctionLibrary key. Error %u.", strVendorDeviceKeyName, GetLastError());
					bSuccess = FALSE;
				}

				RegCloseKey(hVendorDeviceKey);
			}
			else
			{
				_snprintf(strErrorDescription, sizeof(strErrorDescription)-1, "Cannot query %s key. Error %u.", strVendorDeviceKeyName, GetLastError());
				bSuccess = FALSE;
			}

			KeySize = 256;
			KeyIndex++;
			EnumStatus = RegEnumKeyExA(hPassThruSupportKey, KeyIndex, strVendorDeviceKeyName, &KeySize, NULL, NULL, NULL, NULL);
		}

		RegCloseKey(hPassThruSupportKey);
	}
	else
	{
		_snprintf(strErrorDescription, sizeof(strErrorDescription)-1, "Cannot open 'HKLM\\Software\\PassThruSupport.04.04' key. Error %u.", GetLastError());
		bSuccess = FALSE;
	}

	if (!bSuccess)
	{
		ReturnLocalError(ERR_FAILED, strErrorDescription);
	}

	return bSuccess;
}

bool PassThruHelper0404::LoadPassThruLibrary(const char* strLibraryPath)
{
	bool						bSuccess = TRUE;
	char						ErrorDescription[100];

	CloseAllAndUnloadPassThruLibrary();

#if defined(_WIN32) || defined(_WIN64)
	m_PassThruLibraryHandle = LoadLibraryA(strLibraryPath);

	if (m_PassThruLibraryHandle == NULL)
	{
		return FALSE;
	}

	m_pApiFuncs = new PassThruHelper0404ApiFuncs;

	if (bSuccess && (m_pApiFuncs->m_pPassThruOpenFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruOpen")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruOpen()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruCloseFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruClose")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruClose()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruConnectFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruConnect")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruConnect()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruDisconnectFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruDisconnect")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruDisconnect()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruReadMsgsFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruReadMsgs")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruReadMsgs()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruWriteMsgsFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruWriteMsgs")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruWriteMsgs()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruStartPeriodicMsgFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruStartPeriodicMsg")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruStartPeriodicMsg()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruStopPeriodicMsgFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruStopPeriodicMsg")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruStopPeriodicMsg()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruStartMsgFilterFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruStartMsgFilter")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruStartMsgFilter()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruStopMsgFilterFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruStopMsgFilter")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruStopMsgFilter()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruSetProgrammingVoltageFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruSetProgrammingVoltage")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruSetProgrammingVoltage()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruReadVersionFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruReadVersion")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruReadVersion()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruGetLastErrorFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruGetLastError")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruGetLastError()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}
	if (bSuccess && (m_pApiFuncs->m_pPassThruIoctlFunc = GetProcAddress((HMODULE)m_PassThruLibraryHandle, "PassThruIoctl")) == NULL)
	{
		_snprintf(ErrorDescription, sizeof(ErrorDescription)-1, "Failed loading PassThruIoctl()! Error code %u", GetLastError());
		bSuccess = FALSE;
	}

	if (bSuccess)
	{
		m_bPassThruLibraryHandleValid = TRUE;
	}
	else
	{
		if (m_PassThruLibraryHandle != NULL)
		{
			FreeLibrary((HMODULE)m_PassThruLibraryHandle);
			m_PassThruLibraryHandle = NULL;
		}

		delete m_pApiFuncs;
		m_pApiFuncs = NULL;

		ReturnLocalError(ERR_FAILED, ErrorDescription);

		return FALSE;
	}
#else
	#error "PassThruHelper0404 not implemented for this platform!"
#endif

	return TRUE;
}

void PassThruHelper0404::CloseAllAndUnloadPassThruLibrary()
{
	unsigned long	Index;

	for (Index=0; Index<PT0404HELPER_MAX_DEVICES; Index++)
	{
		if (m_DeviceList[Index].m_bDeviceOpen)
		{
			PassThruClose(m_DeviceList[Index].m_DeviceId);
		}

		m_DeviceList[Index].m_bDeviceOpen = FALSE;
		m_DeviceList[Index].m_DeviceId = (unsigned long)-1;
	}

#if defined(_WIN32) || defined(_WIN64)
	if (m_bPassThruLibraryHandleValid)
	{
		FreeLibrary((HMODULE)m_PassThruLibraryHandle);
	}
	m_PassThruLibraryHandle = NULL;
	m_bPassThruLibraryHandleValid = FALSE;
#else
	#error "PassThruHelper0404 not implemented for this platform!"
#endif

	if (m_bUserProvidedApiFuncs)
	{
		// The user did provide the API funcs object, so
		// they are responsible for freeing it.
		m_bUserProvidedApiFuncs = FALSE;
		m_pApiFuncs = NULL;
	}
	else
	{
		// The user did not provide our API funcs object, so
		// we must clean it up ourselves.
		if (m_pApiFuncs != NULL)
		{
			delete m_pApiFuncs;
			m_pApiFuncs = NULL;
		}
	}

	for (Index=0; Index<PT0404HELPER_MAX_DEVICES; Index++)
	{
		m_DeviceList[Index].m_bDeviceOpen = FALSE;
		m_DeviceList[Index].m_DeviceId = (unsigned long)-1;
	}

	for (Index=0; Index<PT0404HELPER_MAX_CHANNELS; Index++)
	{
		m_ChannelList[Index].m_bChannelOpen = FALSE;
		m_ChannelList[Index].m_DeviceId = (unsigned long)-1;
		m_ChannelList[Index].m_ChannelId = (unsigned long)-1;
	}

	for (Index=0; Index<PT0404HELPER_MAX_FILTERS; Index++)
	{
		m_FilterList[Index].m_bFilterInUse = FALSE;
		m_FilterList[Index].m_ChannelId = (unsigned long)-1;
		m_FilterList[Index].m_FilterId = (unsigned long)-1;
	}

	for (Index=0; Index<PT0404HELPER_MAX_PERIODICS; Index++)
	{
		m_PeriodicList[Index].m_bPeriodicInUse = FALSE;
		m_PeriodicList[Index].m_ChannelId = (unsigned long)-1;
		m_PeriodicList[Index].m_PeriodicId = (unsigned long)-1;
	}

	m_bHasLocalError = FALSE;
	memset(m_strLocalErrorDescription, 0, sizeof(m_strLocalErrorDescription));
}

long PassThruHelper0404::PassThruOpen(void *pName, unsigned long *pDeviceID)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;
	bool			bFoundFreeSlot = FALSE;

	for (Index=0; Index<PT0404HELPER_MAX_DEVICES; Index++)
	{
		if (!m_DeviceList[Index].m_bDeviceOpen)
		{
			bFoundFreeSlot = TRUE;
			break;
		}
	}

	if (!bFoundFreeSlot)
	{
		return ReturnLocalError(ERR_FAILED, "Max devices reached.");
	}

	Status = m_pApiFuncs->PassThruOpen(pName, pDeviceID);

	if (Status == STATUS_NOERROR)
	{
		m_DeviceList[Index].m_DeviceId = *pDeviceID;
		m_DeviceList[Index].m_bDeviceOpen = TRUE;
	}

	return Status;
}

long PassThruHelper0404::PassThruClose(unsigned long DeviceID)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	DeviceIndex;
	unsigned long	ChannelIndex;
	unsigned long	FilterIndex;
	unsigned long	PeriodicIndex;

	Status = m_pApiFuncs->PassThruClose(DeviceID);

	// Clear out our stored data
	for (DeviceIndex=0; DeviceIndex<PT0404HELPER_MAX_DEVICES; DeviceIndex++)
	{
		if (m_DeviceList[DeviceIndex].m_bDeviceOpen &&
			(m_DeviceList[DeviceIndex].m_DeviceId == DeviceID))
		{
			// Clear all channels for this device
			for (ChannelIndex=0; ChannelIndex<PT0404HELPER_MAX_CHANNELS; ChannelIndex++)
			{
				if (m_ChannelList[ChannelIndex].m_bChannelOpen &&
					(m_ChannelList[ChannelIndex].m_DeviceId == DeviceID))
				{
					// Clear all filters for this channel
					for (FilterIndex=0; FilterIndex<PT0404HELPER_MAX_FILTERS; FilterIndex++)
					{
						if (m_FilterList[FilterIndex].m_bFilterInUse &&
							(m_FilterList[FilterIndex].m_ChannelId == m_ChannelList[ChannelIndex].m_ChannelId))
						{
							m_FilterList[FilterIndex].m_bFilterInUse = FALSE;
							m_FilterList[FilterIndex].m_ChannelId = (unsigned long)-1;
							m_FilterList[FilterIndex].m_FilterId = (unsigned long)-1;
						}
					}

					// Clear all periodics for this channel
					for (PeriodicIndex=0; PeriodicIndex<PT0404HELPER_MAX_PERIODICS; PeriodicIndex++)
					{
						if (m_PeriodicList[PeriodicIndex].m_bPeriodicInUse &&
							(m_PeriodicList[PeriodicIndex].m_ChannelId == m_ChannelList[ChannelIndex].m_ChannelId))
						{
							m_PeriodicList[PeriodicIndex].m_bPeriodicInUse = FALSE;
							m_PeriodicList[PeriodicIndex].m_ChannelId = (unsigned long)-1;
							m_PeriodicList[PeriodicIndex].m_PeriodicId = (unsigned long)-1;
						}
					}

					m_ChannelList[ChannelIndex].m_bChannelOpen = FALSE;
					m_ChannelList[ChannelIndex].m_DeviceId = (unsigned long)-1;
					m_ChannelList[ChannelIndex].m_ChannelId = (unsigned long)-1;
				}
			}

			m_DeviceList[DeviceIndex].m_DeviceId = (unsigned long)-1;
			m_DeviceList[DeviceIndex].m_bDeviceOpen = FALSE;
		}
	}

	return Status;
}

long PassThruHelper0404::PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;
	bool			bFoundFreeSlot = FALSE;

	for (Index=0; Index<PT0404HELPER_MAX_CHANNELS; Index++)
	{
		if (!m_ChannelList[Index].m_bChannelOpen)
		{
			bFoundFreeSlot = TRUE;
			break;
		}
	}

	if (!bFoundFreeSlot)
	{
		return ReturnLocalError(ERR_FAILED, "Max channels reached.");
	}

	Status = m_pApiFuncs->PassThruConnect(DeviceID, ProtocolID, Flags, BaudRate, pChannelID);

	if (Status == STATUS_NOERROR)
	{
		m_ChannelList[Index].m_DeviceId = DeviceID;
		m_ChannelList[Index].m_ChannelId = *pChannelID;
		m_ChannelList[Index].m_bChannelOpen = TRUE;
	}

	return Status;
}

long PassThruHelper0404::PassThruDisconnect(unsigned long ChannelID)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;

	Status = m_pApiFuncs->PassThruDisconnect(ChannelID);

	// Clear out our stored data
	for (Index=0; Index<PT0404HELPER_MAX_FILTERS; Index++)
	{
		if (m_FilterList[Index].m_bFilterInUse &&
			(m_FilterList[Index].m_ChannelId == ChannelID))
		{
			m_FilterList[Index].m_bFilterInUse = FALSE;
			m_FilterList[Index].m_ChannelId = (unsigned long)-1;
			m_FilterList[Index].m_FilterId = (unsigned long)-1;
		}
	}

	for (Index=0; Index<PT0404HELPER_MAX_PERIODICS; Index++)
	{
		if (m_PeriodicList[Index].m_bPeriodicInUse &&
			(m_PeriodicList[Index].m_ChannelId == ChannelID))
		{
			m_PeriodicList[Index].m_bPeriodicInUse = FALSE;
			m_PeriodicList[Index].m_ChannelId = (unsigned long)-1;
			m_PeriodicList[Index].m_PeriodicId = (unsigned long)-1;
		}
	}

	for (Index=0; Index<PT0404HELPER_MAX_CHANNELS; Index++)
	{
		if (m_ChannelList[Index].m_bChannelOpen &&
			(m_ChannelList[Index].m_ChannelId == ChannelID))
		{
			m_ChannelList[Index].m_bChannelOpen = FALSE;
			m_ChannelList[Index].m_DeviceId = (unsigned long)-1;
			m_ChannelList[Index].m_ChannelId = (unsigned long)-1;
		}
	}

	return Status;
}

long PassThruHelper0404::PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
{
	BEGIN_J2534_API_CALL();

	return m_pApiFuncs->PassThruReadMsgs(ChannelID, pMsg, pNumMsgs, Timeout);
}

long PassThruHelper0404::PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout)
{
	BEGIN_J2534_API_CALL();

	return m_pApiFuncs->PassThruWriteMsgs(ChannelID, pMsg, pNumMsgs, Timeout);
}

long PassThruHelper0404::PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;
	bool			bFoundFreeSlot = FALSE;

	for (Index=0; Index<PT0404HELPER_MAX_PERIODICS; Index++)
	{
		if (!m_PeriodicList[Index].m_bPeriodicInUse)
		{
			bFoundFreeSlot = TRUE;
			break;
		}
	}

	if (!bFoundFreeSlot)
	{
		return ReturnLocalError(ERR_FAILED, "Max periodics reached.");
	}

	Status = m_pApiFuncs->PassThruStartPeriodicMsg(ChannelID, pMsg, pMsgID, TimeInterval);

	if (Status == STATUS_NOERROR)
	{
		m_PeriodicList[Index].m_ChannelId = ChannelID;
		m_PeriodicList[Index].m_PeriodicId = *pMsgID;
		m_PeriodicList[Index].m_bPeriodicInUse = TRUE;
	}

	return Status;
}

long PassThruHelper0404::PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;

	Status = m_pApiFuncs->PassThruStopPeriodicMsg(ChannelID, MsgID);

	// Clear out our stored data
	for (Index=0; Index<PT0404HELPER_MAX_PERIODICS; Index++)
	{
		if (m_PeriodicList[Index].m_bPeriodicInUse &&
			(m_PeriodicList[Index].m_ChannelId == ChannelID) &&
			(m_PeriodicList[Index].m_PeriodicId == MsgID))
		{
			m_PeriodicList[Index].m_bPeriodicInUse = FALSE;
			m_PeriodicList[Index].m_ChannelId = (unsigned long)-1;
			m_PeriodicList[Index].m_PeriodicId = (unsigned long)-1;
		}
	}

	return Status;
}

long PassThruHelper0404::PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;
	bool			bFoundFreeSlot = FALSE;

	for (Index=0; Index<PT0404HELPER_MAX_FILTERS; Index++)
	{
		if (!m_FilterList[Index].m_bFilterInUse)
		{
			bFoundFreeSlot = TRUE;
			break;
		}
	}

	if (!bFoundFreeSlot)
	{
		return ReturnLocalError(ERR_FAILED, "Max filters reached.");
	}

	Status = m_pApiFuncs->PassThruStartMsgFilter(ChannelID, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID);

	if (Status == STATUS_NOERROR)
	{
		m_FilterList[Index].m_ChannelId = ChannelID;
		m_FilterList[Index].m_FilterId = *pFilterID;
		m_FilterList[Index].m_bFilterInUse = TRUE;
	}

	return Status;
}

long PassThruHelper0404::PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;

	Status = m_pApiFuncs->PassThruStopMsgFilter(ChannelID, FilterID);

	// Clear out our stored data
	for (Index=0; Index<PT0404HELPER_MAX_FILTERS; Index++)
	{
		if (m_FilterList[Index].m_bFilterInUse &&
			(m_FilterList[Index].m_ChannelId == ChannelID) &&
			(m_FilterList[Index].m_FilterId == FilterID))
		{
			m_FilterList[Index].m_bFilterInUse = FALSE;
			m_FilterList[Index].m_ChannelId = (unsigned long)-1;
			m_FilterList[Index].m_FilterId = (unsigned long)-1;
		}
	}

	return Status;
}

long PassThruHelper0404::PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage)
{
	BEGIN_J2534_API_CALL();

	return m_pApiFuncs->PassThruSetProgrammingVoltage(DeviceID, PinNumber, Voltage);
}

long PassThruHelper0404::PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion)
{
	BEGIN_J2534_API_CALL();

	return m_pApiFuncs->PassThruReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion);
}

long PassThruHelper0404::PassThruGetLastError(char *pErrorDescription)
{
	if (m_bHasLocalError)
	{
		strcpy(pErrorDescription, m_strLocalErrorDescription);
		return STATUS_NOERROR;
	}
	else
	{
		BEGIN_J2534_API_CALL();

		return m_pApiFuncs->PassThruGetLastError(pErrorDescription);
	}
}

long PassThruHelper0404::PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput)
{
	BEGIN_J2534_API_CALL();

	long			Status;
	unsigned long	Index;

	Status = m_pApiFuncs->PassThruIoctl(ChannelID, IoctlID, pInput, pOutput);

	switch (IoctlID)
	{
	default:
		break;

	case CLEAR_PERIODIC_MSGS:
		// Clear out our stored data
		for (Index=0; Index<PT0404HELPER_MAX_PERIODICS; Index++)
		{
			if (m_PeriodicList[Index].m_bPeriodicInUse &&
				(m_PeriodicList[Index].m_ChannelId == ChannelID))
			{
				m_PeriodicList[Index].m_bPeriodicInUse = FALSE;
				m_PeriodicList[Index].m_ChannelId = (unsigned long)-1;
				m_PeriodicList[Index].m_PeriodicId = (unsigned long)-1;
			}
		}
		break;

	case CLEAR_MSG_FILTERS:
		// Clear out our stored data
		for (Index=0; Index<PT0404HELPER_MAX_FILTERS; Index++)
		{
			if (m_FilterList[Index].m_bFilterInUse &&
				(m_FilterList[Index].m_ChannelId == ChannelID))
			{
				m_FilterList[Index].m_bFilterInUse = FALSE;
				m_FilterList[Index].m_ChannelId = (unsigned long)-1;
				m_FilterList[Index].m_FilterId = (unsigned long)-1;
			}
		}
		break;
	}

	return Status;
}

long PassThruHelper0404::ReturnLocalError(unsigned long ErrorCode, const char* strErrorDescription)
{
	m_bHasLocalError = TRUE;

	memset(m_strLocalErrorDescription, 0, sizeof(m_strLocalErrorDescription));
	strncpy(m_strLocalErrorDescription, strErrorDescription, sizeof(m_strLocalErrorDescription)-1);

	return ErrorCode;
}
