panda/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp

434 lines
17 KiB
C++

// pandaJ2534DLL.cpp : Defines the exported functions for the DLL application.
// Protocol derived from the following sites (which shall be referred to as The Protocol Reference #).
// https://web.archive.org/web/20130805013326/https://tunertools.com/prodimages/DrewTech/Manuals/PassThru_API-1.pdf
// http://web.archive.org/web/20170910063536/http://www.tiecar.net/downloads/SAE_J2534_2002.pdf
#include "stdafx.h"
#include "J2534_v0404.h"
#include "panda/panda.h"
#include "J2534Connection.h"
#include "J2534Connection_CAN.h"
#include "J2534Connection_ISO15765.h"
#include "PandaJ2534Device.h"
#include "dllmain.h"
// A quick way to avoid the name mangling that __stdcall liked to do
#define EXPORT comment(linker, "/EXPORT:" __FUNCTION__ "=" __FUNCDNAME__)
std::vector<std::shared_ptr<PandaJ2534Device>> pandas;
int J25334LastError = 0;
std::string GetProductAndVersion(TCHAR* szFilename)//std::string & strProductName, std::string & strProductVersion)
{
// allocate a block of memory for the version info
DWORD dummy;
DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy);
if (dwSize == 0) {
return "error";
}
std::vector<BYTE> data(dwSize);
// load the version info
if (!GetFileVersionInfo(szFilename, NULL, dwSize, &data[0])) {
return "error";
}
// get the name and version strings
LPVOID pvProductName = NULL;
unsigned int iProductNameLen = 0;
LPVOID pvProductVersion = NULL;
unsigned int iProductVersionLen = 0;
// 040904b0 is a language id.
if (!VerQueryValueA(&data[0], "\\StringFileInfo\\040904b0\\ProductName", &pvProductName, &iProductNameLen) ||
!VerQueryValueA(&data[0], "\\StringFileInfo\\040904b0\\ProductVersion", &pvProductVersion, &iProductVersionLen)) {
return "error";
}
std::string ver_str = std::string((char*)pvProductVersion, iProductVersionLen-1);
std::string prod_str = std::string((char*)pvProductName, iProductNameLen-1);
std::string full_ver = prod_str + std::string(": ") + ver_str;
return full_ver;
}
long ret_code(long code) {
J25334LastError = code;
return code;
}
#define EXTRACT_DID(CID) (CID & 0xFFFF)
#define EXTRACT_CID(CID) ((CID >> 16) & 0xFFFF)
long check_valid_DeviceID(unsigned long DeviceID) {
uint16_t dev_id = EXTRACT_DID(DeviceID);
if (pandas.size() <= dev_id || pandas[dev_id] == nullptr)
return ret_code(ERR_INVALID_DEVICE_ID);
return ret_code(STATUS_NOERROR);
}
long check_valid_ChannelID(unsigned long ChannelID) {
uint16_t dev_id = EXTRACT_DID(ChannelID);;
uint16_t con_id = EXTRACT_CID(ChannelID);
if (pandas.size() <= dev_id || pandas[dev_id] == nullptr)
return ret_code(ERR_INVALID_CHANNEL_ID);
if (pandas[dev_id]->connections.size() <= con_id) return ret_code(ERR_INVALID_CHANNEL_ID);
if (pandas[dev_id]->connections[con_id] == nullptr) return ret_code(ERR_DEVICE_NOT_CONNECTED);
return ret_code(STATUS_NOERROR);
}
//Do not call without checking if the device/channel id exists first.
#define get_device(DeviceID) (pandas[EXTRACT_DID(DeviceID)])
#define get_channel(ChannelID) (get_device(ChannelID)->connections[EXTRACT_CID(ChannelID)])
PANDAJ2534DLL_API long PTAPI PassThruOpen(void *pName, unsigned long *pDeviceID) {
#pragma EXPORT
if (pDeviceID == NULL) return ret_code(ERR_NULL_PARAMETER);
std::string sn = (pName == NULL) ? "" : std::string((char*)pName);
if (sn == "J2534-2:")
sn = "";
auto new_panda = PandaJ2534Device::openByName(sn);
if (new_panda == nullptr) {
if(sn == "" && pandas.size() == 1)
return ret_code(ERR_DEVICE_IN_USE);
for (auto& pn : pandas) {
if (pn->panda->get_usb_sn() == sn)
return ret_code(ERR_DEVICE_IN_USE);
}
return ret_code(ERR_DEVICE_NOT_CONNECTED);
}
int panda_index = -1;
for (unsigned int i = 0; i < pandas.size(); i++)
if (pandas[i] == nullptr) {
panda_index = i;
pandas[panda_index] = std::move(new_panda);
break;
}
if (panda_index == -1) {
if(pandas.size() == 0xFFFF) //device id will be 16 bit to fit channel next to it.
return ret_code(ERR_FAILED); //Too many pandas. Off the endangered species list.
pandas.push_back(std::move(new_panda));
panda_index = pandas.size()-1;
}
*pDeviceID = panda_index;
return ret_code(STATUS_NOERROR);
}
PANDAJ2534DLL_API long PTAPI PassThruClose(unsigned long DeviceID) {
#pragma EXPORT
if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError;
get_device(DeviceID) = nullptr;
return ret_code(STATUS_NOERROR);
}
PANDAJ2534DLL_API long PTAPI PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID,
unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID) {
#pragma EXPORT
if (pChannelID == NULL) return ret_code(ERR_NULL_PARAMETER);
if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError;
auto& panda = get_device(DeviceID);
std::shared_ptr<J2534Connection> conn;
//TODO check if channel can be made
try {
switch (ProtocolID) {
//SW seems to refer to Single Wire. https://www.nxp.com/files-static/training_pdf/20451_BUS_COMM_WBT.pdf
//SW_ protocols may be touched on here: https://www.iso.org/obp/ui/#iso:std:iso:22900:-2:ed-1:v1:en
case J1850VPW: //These protocols are outdated and will not be supported. HDS wants them to not fail to open.
case J1850PWM:
case J1850VPW_PS:
case J1850PWM_PS:
case ISO9141: //This protocol could be implemented if 5 BAUD init support is added to the panda.
case ISO9141_PS:
conn = std::make_shared<J2534Connection>(panda, ProtocolID, Flags, BaudRate);
break;
case ISO14230: //Only supporting Fast init until panda adds support for 5 BAUD init.
case ISO14230_PS:
conn = std::make_shared<J2534Connection>(panda, ProtocolID, Flags, BaudRate);
break;
case CAN:
case CAN_PS:
//case SW_CAN_PS:
conn = std::make_shared<J2534Connection_CAN>(panda, ProtocolID, Flags, BaudRate);
break;
case ISO15765:
case ISO15765_PS:
conn = std::make_shared<J2534Connection_ISO15765>(panda, ProtocolID, Flags, BaudRate);
break;
//case SW_ISO15765_PS: // SW = Single Wire. GMLAN is a SW CAN protocol
//case GM_UART_PS: // PS = Pin Select. Handles different ports.
//Looks like SCI based protocols may not be compatible with the panda:
//http://mdhmotors.com/can-communications-vehicle-network-protocols/3/
//case SCI_A_ENGINE:
//case SCI_A_TRANS:
//case SCI_B_ENGINE:
//case SCI_B_TRANS:
//case J2610_PS:
default:
return ret_code(ERR_INVALID_PROTOCOL_ID);
}
} catch (int e) {
return ret_code(e);
}
unsigned long channel_index;
unsigned long err = panda->addChannel(conn, &channel_index);
if (err == STATUS_NOERROR)
*pChannelID = (channel_index << 16) | DeviceID;
return ret_code(err);
}
PANDAJ2534DLL_API long PTAPI PassThruDisconnect(unsigned long ChannelID) {
#pragma EXPORT
unsigned long res = check_valid_DeviceID(ChannelID);
if (res == ERR_INVALID_DEVICE_ID) return ret_code(ERR_INVALID_CHANNEL_ID);
if (res != STATUS_NOERROR) return J25334LastError;
return ret_code(get_device(ChannelID)->closeChannel(EXTRACT_CID(ChannelID)));
}
PANDAJ2534DLL_API long PTAPI PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg,
unsigned long *pNumMsgs, unsigned long Timeout) {
#pragma EXPORT
if (pMsg == NULL || pNumMsgs == NULL) return ret_code(ERR_NULL_PARAMETER);
if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError;
return ret_code(get_channel(ChannelID)->PassThruReadMsgs(pMsg, pNumMsgs, Timeout));
}
PANDAJ2534DLL_API long PTAPI PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) {
#pragma EXPORT
if (pMsg == NULL || pNumMsgs == NULL) return ret_code(ERR_NULL_PARAMETER);
if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError;
return ret_code(get_channel(ChannelID)->PassThruWriteMsgs(pMsg, pNumMsgs, Timeout));
}
PANDAJ2534DLL_API long PTAPI PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval) {
#pragma EXPORT
if (pMsg == NULL || pMsgID == NULL) return ret_code(ERR_NULL_PARAMETER);
if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError;
return ret_code(get_channel(ChannelID)->PassThruStartPeriodicMsg(pMsg, pMsgID, TimeInterval));
}
PANDAJ2534DLL_API long PTAPI PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID) {
#pragma EXPORT
if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError;
return ret_code(get_channel(ChannelID)->PassThruStopPeriodicMsg(MsgID));
}
PANDAJ2534DLL_API long PTAPI PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg,
PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) {
#pragma EXPORT
if (FilterType != PASS_FILTER && FilterType != BLOCK_FILTER && FilterType != FLOW_CONTROL_FILTER) return ret_code(ERR_NULL_PARAMETER);
if (!pFilterID || (!pMaskMsg && !pPatternMsg && !pFlowControlMsg)) return ret_code(ERR_NULL_PARAMETER);
if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError;
return ret_code(get_channel(ChannelID)->PassThruStartMsgFilter(FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID));
}
PANDAJ2534DLL_API long PTAPI PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID) {
#pragma EXPORT
if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError;
return ret_code(get_channel(ChannelID)->PassThruStopMsgFilter(FilterID));
}
PANDAJ2534DLL_API long PTAPI PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage) {
#pragma EXPORT
//Unused
if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError;
auto& panda = get_device(DeviceID);
switch (Voltage) {
case SHORT_TO_GROUND:
break;
case VOLTAGE_OFF:
break;
default:
if (!(5000 <= Voltage && Voltage <= 20000))
return ret_code(ERR_NOT_SUPPORTED);
break;
}
return ret_code(STATUS_NOERROR);
}
PANDAJ2534DLL_API long PTAPI PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion) {
#pragma EXPORT
if (!pFirmwareVersion || !pDllVersion || !pApiVersion) return ret_code(ERR_NULL_PARAMETER);
if (check_valid_DeviceID(DeviceID) != STATUS_NOERROR) return J25334LastError;
auto& panda = get_device(DeviceID);
auto fw_version = panda->panda->get_version();
strcpy_s(pFirmwareVersion, 80, fw_version.c_str());
std::string j2534dll_ver;
TCHAR pandalib_filename[MAX_PATH + 1] = { 0 };
if (GetModuleFileName(thisdll, pandalib_filename, MAX_PATH) == 0) {
j2534dll_ver = "error";
} else {
j2534dll_ver = GetProductAndVersion(pandalib_filename);
}
std::string pandalib_ver = GetProductAndVersion(_T("panda.dll"));
std::string fullver = "(" + j2534dll_ver + "; " + pandalib_ver + ")";
strcpy_s(pDllVersion, 80, fullver.c_str());
strcpy_s(pApiVersion, 80, J2534_APIVER_NOVEMBER_2004);
return ret_code(STATUS_NOERROR);
}
PANDAJ2534DLL_API long PTAPI PassThruGetLastError(char *pErrorDescription) {
#pragma EXPORT
if (pErrorDescription == NULL) return ret_code(ERR_NULL_PARAMETER);
switch (J25334LastError) {
case STATUS_NOERROR:
strcpy_s(pErrorDescription, 80, "Function call successful.");
break;
case ERR_NOT_SUPPORTED:
strcpy_s(pErrorDescription, 80, "Device cannot support requested functionality mandated in J2534.");
break;
case ERR_INVALID_CHANNEL_ID:
strcpy_s(pErrorDescription, 80, "Invalid ChannelID value.");
break;
case ERR_INVALID_PROTOCOL_ID:
strcpy_s(pErrorDescription, 80, "Invalid or unsupported ProtocolID, or resource conflict.");
break;
case ERR_NULL_PARAMETER:
strcpy_s(pErrorDescription, 80, "NULL pointer supplied where a valid pointer is required.");
break;
case ERR_INVALID_IOCTL_VALUE:
strcpy_s(pErrorDescription, 80, "Invalid value for Ioctl parameter.");
break;
case ERR_INVALID_FLAGS:
strcpy_s(pErrorDescription, 80, "Invalid flag values.");
break;
case ERR_FAILED:
strcpy_s(pErrorDescription, 80, "Undefined error.");
break;
case ERR_DEVICE_NOT_CONNECTED:
strcpy_s(pErrorDescription, 80, "Unable to communicate with device.");
break;
case ERR_TIMEOUT:
strcpy_s(pErrorDescription, 80, "Read or write timeout:");
// PassThruReadMsgs() - No message available to read or could not read the specified number of messages. The actual number of messages read is placed in <NumMsgs>.
// PassThruWriteMsgs() - Device could not write the specified number of messages. The actual number of messages sent on the vehicle network is placed in <NumMsgs>.
break;
case ERR_INVALID_MSG:
strcpy_s(pErrorDescription, 80, "Invalid message structure pointed to by pMsg.");
break;
case ERR_INVALID_TIME_INTERVAL:
strcpy_s(pErrorDescription, 80, "Invalid TimeInterval value.");
break;
case ERR_EXCEEDED_LIMIT:
strcpy_s(pErrorDescription, 80, "Exceeded maximum number of message IDs or allocated space.");
break;
case ERR_INVALID_MSG_ID:
strcpy_s(pErrorDescription, 80, "Invalid MsgID value.");
break;
case ERR_DEVICE_IN_USE:
strcpy_s(pErrorDescription, 80, "Device is currently open.");
break;
case ERR_INVALID_IOCTL_ID:
strcpy_s(pErrorDescription, 80, "Invalid IoctlID value.");
break;
case ERR_BUFFER_EMPTY:
strcpy_s(pErrorDescription, 80, "Protocol message buffer empty.");
break;
case ERR_BUFFER_FULL:
strcpy_s(pErrorDescription, 80, "Protocol message buffer full. Messages may have been lost.");
break;
case ERR_BUFFER_OVERFLOW:
strcpy_s(pErrorDescription, 80, "A buffer overflow occurred and messages were lost.");
break;
case ERR_PIN_INVALID:
strcpy_s(pErrorDescription, 80, "Invalid pin number, or pin number already in use.");
break;
case ERR_CHANNEL_IN_USE:
strcpy_s(pErrorDescription, 80, "Channel number is currently connected.");
break;
case ERR_MSG_PROTOCOL_ID:
strcpy_s(pErrorDescription, 80, "The Message's Protocol does not match the Channel's protocol.");
break;
case ERR_INVALID_FILTER_ID:
strcpy_s(pErrorDescription, 80, "Invalid Filter ID value.");
break;
case ERR_NO_FLOW_CONTROL:
strcpy_s(pErrorDescription, 80, "No flow control filter set or matched.");
break;
case ERR_NOT_UNIQUE:
strcpy_s(pErrorDescription, 80, "This filter already exists.");
break;
case ERR_INVALID_BAUDRATE:
strcpy_s(pErrorDescription, 80, "The desired baud rate cannot be achieved within SAE tolerance.");
break;
case ERR_INVALID_DEVICE_ID:
strcpy_s(pErrorDescription, 80, "Device ID invalid.");
break;
}
return ret_code(STATUS_NOERROR);
}
PANDAJ2534DLL_API long PTAPI PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID,
void *pInput, void *pOutput) {
#pragma EXPORT
if (check_valid_ChannelID(ChannelID) != STATUS_NOERROR) return J25334LastError;
auto& dev_entry = get_device(ChannelID);
//get_channel(ChannelID)
switch (IoctlID) {
case GET_CONFIG:
{
SCONFIG_LIST *inconfig = (SCONFIG_LIST*)pInput;
if (inconfig == NULL)
return ret_code(ERR_NULL_PARAMETER);
for (unsigned int i = 0; i < inconfig->NumOfParams; i++) {
try {
inconfig->ConfigPtr[i].Value = get_channel(ChannelID)->processIOCTLGetConfig(inconfig->ConfigPtr[i].Parameter);
} catch (int e) {
return ret_code(e);
}
}
break;
}
case SET_CONFIG:
{
SCONFIG_LIST *inconfig = (SCONFIG_LIST*)pInput;
if (inconfig == NULL)
return ret_code(ERR_NULL_PARAMETER);
for (unsigned int i = 0; i < inconfig->NumOfParams; i++) {
try {
get_channel(ChannelID)->processIOCTLSetConfig(inconfig->ConfigPtr[i].Parameter, inconfig->ConfigPtr[i].Value);
} catch (int e) {
return ret_code(e);
}
}
break;
}
case READ_VBATT:
panda::PANDA_HEALTH health = dev_entry->panda->get_health();
*(unsigned long*)pOutput = health.voltage;
break;
case FIVE_BAUD_INIT:
if (!pInput || !pOutput) return ret_code(ERR_NULL_PARAMETER);
return ret_code(get_channel(ChannelID)->init5b((SBYTE_ARRAY*)pInput, (SBYTE_ARRAY*)pOutput));
case FAST_INIT:
if (!pInput || !pOutput) return ret_code(ERR_NULL_PARAMETER);
return ret_code(get_channel(ChannelID)->initFast((PASSTHRU_MSG*)pInput, (PASSTHRU_MSG*)pOutput));
case CLEAR_TX_BUFFER:
return ret_code(get_channel(ChannelID)->clearTXBuff());
case CLEAR_RX_BUFFER:
return ret_code(get_channel(ChannelID)->clearRXBuff());
case CLEAR_PERIODIC_MSGS:
return ret_code(get_channel(ChannelID)->clearPeriodicMsgs());
case CLEAR_MSG_FILTERS:
return ret_code(get_channel(ChannelID)->clearMsgFilters());
case CLEAR_FUNCT_MSG_LOOKUP_TABLE: // LOOKUP TABLE IS RELATED TO J1850 PWM. Unsupported.
if (!pInput) return ret_code(ERR_NULL_PARAMETER);
return ret_code(STATUS_NOERROR);
case ADD_TO_FUNCT_MSG_LOOKUP_TABLE: // LOOKUP TABLE IS RELATED TO J1850 PWM. Unsupported.
if (!pInput) return ret_code(ERR_NULL_PARAMETER);
return ret_code(STATUS_NOERROR);
case DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE: // LOOKUP TABLE IS RELATED TO J1850 PWM. Unsupported.
return ret_code(STATUS_NOERROR);
case READ_PROG_VOLTAGE:
*(unsigned long*)pOutput = 0;
break;
default:
printf("Got unknown IIOCTL %X\n", IoctlID);
}
return ret_code(STATUS_NOERROR);
}