diff --git a/CMakeLists.txt b/CMakeLists.txt index da512d4..ed303a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -236,9 +236,12 @@ set(SRC_FILES communication/message/clientidmessage.cpp communication/message/mfgconfigmessage.cpp communication/message/transmitmessage.cpp + + communication/message/spiportkeymessage.cpp communication/message/genericapidatamessage.cpp communication/message/genericapistatusmessage.cpp + communication/message/iso15765message.cpp communication/packet/flexraypacket.cpp communication/packet/canpacket.cpp communication/packet/a2bpacket.cpp diff --git a/api/icsneolegacy/icsneolegacy.cpp b/api/icsneolegacy/icsneolegacy.cpp index 3084742..89a6719 100644 --- a/api/icsneolegacy/icsneolegacy.cpp +++ b/api/icsneolegacy/icsneolegacy.cpp @@ -545,15 +545,13 @@ int LegacyDLLExport icsneoGetTimeStampForMsg(void* hObject, icsSpyMessage* pMsg, void LegacyDLLExport icsneoGetISO15765Status(void* hObject, int lNetwork, int lClearTxStatus, int lClearRxStatus, int* lTxStatus, int* lRxStatus) { - // TODO Implement - return; + // Not supported } void LegacyDLLExport icsneoSetISO15765RxParameters(void* hObject, int lNetwork, int lEnable, spyFilterLong* pFF_CFMsgFilter, icsSpyMessage* pTxMsg, int lCFTimeOutMs, int lFlowCBlockSize, int lUsesExtendedAddressing, int lUseHardwareIfPresent) { - // TODO Implement - return; + // Not supported } int LegacyDLLExport icsneoGetRTC(void* hObject, icsSpyTime* time) @@ -835,26 +833,25 @@ int LegacyDLLExport icsneoGetErrorInfo(int lErrorNumber, char* szErrorDescriptio //ISO15765-2 Functions int LegacyDLLExport icsneoISO15765_EnableNetworks(void* hObject, unsigned long ulNetworks) { - // TODO Implement + // Not supported return false; } int LegacyDLLExport icsneoISO15765_DisableNetworks(void* hObject) { - // TODO Implement + // Not supported return false; } -int LegacyDLLExport icsneoISO15765_TransmitMessage(void* hObject, unsigned long ulNetworkID, stCM_ISO157652_TxMessage* pMsg, - unsigned long ulBlockingTimeout) +int LegacyDLLExport icsneoISO15765_TransmitMessage(void* hObject, unsigned long ulNetworkID, stCM_ISO157652_TxMessage* pMsg, unsigned long ulBlockingTimeout) { - // TODO Implement + // Not supported return false; } int LegacyDLLExport icsneoISO15765_ReceiveMessage(void* hObject, int ulNetworkID, stCM_ISO157652_RxMessage* pMsg) { - // TODO Implement + // Not supported return false; } @@ -1285,10 +1282,11 @@ int LegacyDLLExport icsneoJ2534Cmd(void* hObject, unsigned char* CmdBuf, short L return false; neodevice_t* device = reinterpret_cast(hObject); + const auto& cmd = static_cast(*CmdBuf); - switch (*CmdBuf) + switch (cmd) { - case J2534NVCMD_SetNetworkBaudRate: + case icsneo::J2534Command::SetNetworkBaudRate: pTmp = (uint64_t *)&CmdBuf[1]; NetworkID = (uint16_t)*pTmp; @@ -1298,7 +1296,7 @@ int LegacyDLLExport icsneoJ2534Cmd(void* hObject, unsigned char* CmdBuf, short L iRetVal = 0; break; - case J2534NVCMD_GetNetworkBaudRate: + case icsneo::J2534Command::GetNetworkBaudRate: { pTmp = (uint64_t *)&CmdBuf[1]; NetworkID = (uint16_t)*pTmp; @@ -1311,7 +1309,7 @@ int LegacyDLLExport icsneoJ2534Cmd(void* hObject, unsigned char* CmdBuf, short L *pTmp = static_cast(ret); break; } - case J2534NVCMD_SetCANFDRate: + case icsneo::J2534Command::SetCANFDRate: pTmp = (uint64_t *)&CmdBuf[1]; NetworkID = (uint16_t)*pTmp; @@ -1322,7 +1320,7 @@ int LegacyDLLExport icsneoJ2534Cmd(void* hObject, unsigned char* CmdBuf, short L iRetVal = 0; break; - case J2534NVCMD_GetCANFDRate: + case icsneo::J2534Command::GetCANFDRate: pTmp = (uint64_t *)&CmdBuf[1]; NetworkID = (uint16_t)*pTmp; @@ -1332,7 +1330,7 @@ int LegacyDLLExport icsneoJ2534Cmd(void* hObject, unsigned char* CmdBuf, short L *pTmp = icsneo_getFDBaudrate(device, NetworkID); break; - case J2534NVCMD_GetCANFDTermination: + case icsneo::J2534Command::GetCANFDTermination: pTmp = (uint64_t *)&CmdBuf[1]; NetworkID = (uint16_t)*pTmp; @@ -1368,7 +1366,7 @@ int LegacyDLLExport icsneoJ2534Cmd(void* hObject, unsigned char* CmdBuf, short L } break; - case J2534NVCMD_SetCANFDTermination: + case icsneo::J2534Command::SetCANFDTermination: pTmp = (uint64_t *)&CmdBuf[1]; NetworkID = (uint16_t)*pTmp; @@ -1422,11 +1420,9 @@ int LegacyDLLExport icsneoEnableBusVoltageMonitor(void* hObject, unsigned int en return false; } -int LegacyDLLExport icsneoISO15765_TransmitMessageEx(void* hObject, - unsigned long ulNetworkID, - ISO15765_2015_TxMessage* pMsg, - unsigned long ulBlockingTimeout) +int LegacyDLLExport icsneoISO15765_TransmitMessageEx(void* hObject, unsigned long ulNetworkID, ISO15765_2015_TxMessage* pMsg, unsigned long ulBlockingTimeout) { + // Not supported return false; } diff --git a/communication/decoder.cpp b/communication/decoder.cpp index f086ab7..402f019 100644 --- a/communication/decoder.cpp +++ b/communication/decoder.cpp @@ -44,6 +44,7 @@ #include "icsneo/communication/packet/i2cpacket.h" #include "icsneo/communication/packet/scriptstatuspacket.h" #include "icsneo/communication/packet/linpacket.h" +#include "icsneo/communication/message/iso15765message.h" #include "icsneo/communication/packet/componentversionpacket.h" #include "icsneo/communication/packet/supportedfeaturespacket.h" #include "icsneo/communication/packet/mdiopacket.h" @@ -479,6 +480,14 @@ bool Decoder::decode(std::shared_ptr& result, const std::shared_ptrdata); + if(!result) { + report(APIEvent::Type::PacketDecodingError, APIEvent::Severity::Error); + return false; + } + return true; + } default: auto msg = std::make_shared(); msg->command = Command(packet->data[0]); diff --git a/communication/message/iso15765message.cpp b/communication/message/iso15765message.cpp new file mode 100644 index 0000000..fff62c4 --- /dev/null +++ b/communication/message/iso15765message.cpp @@ -0,0 +1,337 @@ +#include "icsneo/communication/message/iso15765message.h" + +#include + +using namespace icsneo; + +std::shared_ptr J2534CommandMessage::decodeToMessage(const std::vector& data) { + if(data.size() < 2) + return nullptr; + + auto msg = std::make_shared((J2534Command)data[1]); + if(data.size() > 2) + msg->setArgumentData(std::vector(data.begin() + 2, data.end())); + + return msg; +} + +uint16_t J2534CommandMessage::read16LE(size_t o) const { + return (uint16_t)((uint16_t)argumentData[o] | ((uint16_t)argumentData[o + 1] << 8)); +} + +void J2534CommandMessage::write16LE(size_t o, uint16_t v) { + argumentData[o] = (uint8_t)(v & 0xFF); + argumentData[o + 1] = (uint8_t)((v >> 8) & 0xFF); +} + +uint16_t J2534CommandMessage::read16BE(size_t o) const { + return (uint16_t)(((uint16_t)argumentData[o] << 8) | argumentData[o + 1]); +} + +void J2534CommandMessage::write16BE(size_t o, uint16_t v) { + argumentData[o] = (uint8_t)((v >> 8) & 0xFF); + argumentData[o + 1] = (uint8_t)(v & 0xFF); +} + +void J2534CommandMessage::writeBits16LE(size_t o, unsigned bitPos, unsigned bitCount, uint32_t value) { + const uint16_t mask = (uint16_t)(((1u << bitCount) - 1u) << bitPos); + uint16_t word = read16LE(o); + word = (uint16_t)((word & ~mask) | (((value << bitPos) & mask))); + write16LE(o, word); +} + +uint32_t J2534CommandMessage::read32LE(size_t o) const { + return (uint32_t)argumentData[o] + | ((uint32_t)argumentData[o + 1] << 8) + | ((uint32_t)argumentData[o + 2] << 16) + | ((uint32_t)argumentData[o + 3] << 24); +} + +void J2534CommandMessage::write32LE(size_t o, uint32_t v) { + argumentData[o] = (uint8_t)(v & 0xFF); + argumentData[o + 1] = (uint8_t)((v >> 8) & 0xFF); + argumentData[o + 2] = (uint8_t)((v >> 16) & 0xFF); + argumentData[o + 3] = (uint8_t)((v >> 24) & 0xFF); +} + +uint32_t J2534CommandMessage::read32BE(size_t o) const { + return ((uint32_t)argumentData[o] << 24) | ((uint32_t)argumentData[o + 1] << 16) + | ((uint32_t)argumentData[o + 2] << 8) | (uint32_t)argumentData[o + 3]; +} + +void J2534CommandMessage::write32BE(size_t o, uint32_t v) { + argumentData[o] = (uint8_t)((v >> 24) & 0xFF); + argumentData[o + 1] = (uint8_t)((v >> 16) & 0xFF); + argumentData[o + 2] = (uint8_t)((v >> 8) & 0xFF); + argumentData[o + 3] = (uint8_t)(v & 0xFF); +} + +J2534_RxCanFilter J2534SetupCanRxFilteringMessage::getRxCanFilter() const { + constexpr size_t filterSize = sizeof(J2534_RxCanFilter::fb); + J2534_RxCanFilter filter = {}; + if(argumentData.size() >= (10 + filterSize)) + { + filter.idx = (argumentData[2] << 8) | argumentData[3]; + filter.enabled = (argumentData[4] << 8) | argumentData[5]; + filter.filterCount = (argumentData[6] << 8) | argumentData[7]; + filter.fb = *((MessageFilterBytes*)(argumentData.data() + 8)); + filter.networkId = (argumentData[8 + filterSize] << 8) | argumentData[9 + filterSize]; + } + return filter; +} + +void J2534SetupCanRxFilteringMessage::setRxCanFilter(const J2534_RxCanFilter& filter) { + argumentData.resize(10 + sizeof(filter.fb)); + argumentData[2] = (filter.idx >> 8) & 0xFF; + argumentData[3] = filter.idx & 0xFF; + argumentData[4] = (filter.enabled >> 8) & 0xFF; + argumentData[5] = filter.enabled & 0xFF; + argumentData[6] = (filter.filterCount >> 8) & 0xFF; + argumentData[7] = filter.filterCount & 0xFF; + *((MessageFilterBytes*)(argumentData.data() + 8)) = filter.fb; + argumentData[8 + sizeof(filter.fb)] = (filter.networkId >> 8) & 0xFF; + argumentData[9 + sizeof(filter.fb)] = filter.networkId & 0xFF; +} + + +J2534SetupIso15765FlowControlMessage::J2534SetupIso15765FlowControlMessage() + : J2534CommandMessage(J2534Command::SetupIso15765RxFilter) { + argumentData.resize(MessageSize); + // Bytes [0..1] were populated by the base constructor. Everything else starts at zero + // (matches the previous behavior which zeroed the flt region and left the rest of the + // caller-supplied struct as-is; typical callers value-initialized their struct too). + for(size_t i = 2; i < MessageSize; ++i) + argumentData[i] = 0; +} + +void J2534SetupIso15765FlowControlMessage::rebuildFilterBytes(void) { + // Zero the 28-byte flt region. + for(size_t i = 0; i < FltSize; ++i) + argumentData[FltOffset + i] = 0; + + const uint32_t id = read32LE(IdOffset); + const uint32_t idMask = read32LE(IdMaskOffset); + const bool is29Bit = getFlagBit(FlagId29BitEnable); + const bool extAddr = getFlagBit(FlagExtAddressEnable); + const uint8_t extAddrByte = argumentData[ExtAddrOffset]; + + // CoreMiniMessageFilterBytes has an interleaved layout: + // uiFilterMask0 at flt+0, uiFilterID0 at flt+2 + // uiFilterMask1 at flt+4, uiFilterID1 at flt+6 + // uiFilterMask2 at flt+8, uiFilterID2 at flt+10 + // datalink.uiFilterMask3 at flt+12, datalink.uiFilterID3 at flt+14 + // + // CoreMiniMsgBitsCAN bit layout within each 16-bit unit: + // unit0: IDE(bit0), SRR(bit1), SID(bits2-12), NETWORKINDEX(bits13-15) + // unit1: EID(bits0-11), TXMSG(bit12), ... + // unit2: DLC(bits0-3), ..., RTR(bit9), EID2(bits10-15) + const size_t MaskUnit0 = FltOffset + 0; + const size_t MaskUnit1 = FltOffset + 4; + const size_t MaskUnit2 = FltOffset + 8; + const size_t ValueUnit0 = FltOffset + 2; + const size_t ValueUnit1 = FltOffset + 6; + const size_t ValueUnit2 = FltOffset + 10; + + // filter arbid: pMask->IDE = 1 + writeBits16LE(MaskUnit0, /*bit*/0, /*width*/1, 1); + + if(is29Bit) { + // pValue->IDE = 1; pValue->SID = (id >> 18) & 0x7FF + writeBits16LE(ValueUnit0, 0, 1, 1); + writeBits16LE(ValueUnit0, 2, 11, (id >> 18) & 0x7FF); + // pValue->EID = (id >> 6) & 0xFFF + writeBits16LE(ValueUnit1, 0, 12, (id >> 6) & 0xFFF); + // pValue->EID2 = id & 0x3F + writeBits16LE(ValueUnit2, 10, 6, id & 0x3F); + // pMask->SID = (id_mask >> 18) & 0x7FF + writeBits16LE(MaskUnit0, 2, 11, (idMask >> 18) & 0x7FF); + // pMask->EID = (id_mask >> 6) & 0xFFF (overlaps pValue->{IDE,SID} storage) + writeBits16LE(MaskUnit1, 0, 12, (idMask >> 6) & 0xFFF); + // pMask->EID2 = id_mask & 0x3F (overlaps pValue->EID storage) + writeBits16LE(MaskUnit2, 10, 6, idMask & 0x3F); + } else { + // pValue->IDE = 0 (already zero); pValue->SID = id + writeBits16LE(ValueUnit0, 2, 11, id & 0x7FF); + // pMask->SID = id_mask + writeBits16LE(MaskUnit0, 2, 11, idMask & 0x7FF); + } + + // Extended-address filter: writes into the datalink.uiFilterID3 / uiFilterMask3 bytes. + // uiFilterMask3 = flt[12..13] = wire[37..38] + // uiFilterID3 = flt[14..15] = wire[39..40] + if(extAddr) { + const size_t kValueBytes = FltOffset + 14; // uiFilterID3 + const size_t kMaskBytes = FltOffset + 12; // uiFilterMask3 + argumentData[kValueBytes + 0] = extAddrByte; + argumentData[kMaskBytes + 0] = 0xFF; + argumentData[kValueBytes + 1] = 0; + argumentData[kMaskBytes + 1] = 0; + } +} + +uint16_t J2534SetupIso15765FlowControlMessage::getIdx(void) const { return read16LE(IdxOffset); } +void J2534SetupIso15765FlowControlMessage::setIdx(uint16_t idx) { write16LE(IdxOffset, idx); } + +uint16_t J2534SetupIso15765FlowControlMessage::getCoreMiniId(void) const { return read16LE(CoreMiniOffset); } +void J2534SetupIso15765FlowControlMessage::setCoreMiniId(uint16_t coreMiniId) { write16LE(CoreMiniOffset, coreMiniId); } + +uint8_t J2534SetupIso15765FlowControlMessage::getPadding(void) const { return argumentData[PaddingOffset]; } +void J2534SetupIso15765FlowControlMessage::setPadding(uint8_t padding) { argumentData[PaddingOffset] = padding; } + +uint32_t J2534SetupIso15765FlowControlMessage::getId(void) const { return read32LE(IdOffset); } +void J2534SetupIso15765FlowControlMessage::setId(uint32_t id) { write32LE(IdOffset, id); rebuildFilterBytes(); } + +uint32_t J2534SetupIso15765FlowControlMessage::getIdMask(void) const { return read32LE(IdMaskOffset); } +void J2534SetupIso15765FlowControlMessage::setIdMask(uint32_t idMask) { write32LE(IdMaskOffset, idMask); rebuildFilterBytes(); } + +uint32_t J2534SetupIso15765FlowControlMessage::getFcId(void) const { return read32LE(FcIdOffset); } +void J2534SetupIso15765FlowControlMessage::setFcId(uint32_t fcId) { write32LE(FcIdOffset, fcId); } + +uint8_t J2534SetupIso15765FlowControlMessage::getFlowControlExtendedAddress(void) const { return argumentData[FcExtAddrOffset]; } +void J2534SetupIso15765FlowControlMessage::setFlowControlExtendedAddress(uint8_t addr) { argumentData[FcExtAddrOffset] = addr; } + +uint8_t J2534SetupIso15765FlowControlMessage::getExtendedAddress(void) const { return argumentData[ExtAddrOffset]; } +void J2534SetupIso15765FlowControlMessage::setExtendedAddress(uint8_t addr) { argumentData[ExtAddrOffset] = addr; rebuildFilterBytes(); } + +uint8_t J2534SetupIso15765FlowControlMessage::getBlockSize(void) const { return argumentData[BlockSizeOffset]; } +void J2534SetupIso15765FlowControlMessage::setBlockSize(uint8_t blockSize) { argumentData[BlockSizeOffset] = blockSize; } + +uint8_t J2534SetupIso15765FlowControlMessage::getStMin(void) const { return argumentData[StMinOffset]; } +void J2534SetupIso15765FlowControlMessage::setStMin(uint8_t stMin) { argumentData[StMinOffset] = stMin; } + +uint16_t J2534SetupIso15765FlowControlMessage::getCfTimeout(void) const { return read16LE(CfTimeoutOffset); } +void J2534SetupIso15765FlowControlMessage::setCfTimeout(uint16_t cfTimeout) { write16LE(CfTimeoutOffset, cfTimeout); } + +uint32_t J2534SetupIso15765FlowControlMessage::getFlags(void) const { return read32LE(FlagsOffset); } +void J2534SetupIso15765FlowControlMessage::setFlags(uint32_t flags) { write32LE(FlagsOffset, flags); rebuildFilterBytes(); } + +bool J2534SetupIso15765FlowControlMessage::getEnable(void) const { return getFlagBit(FlagEnable); } +void J2534SetupIso15765FlowControlMessage::setEnable(bool enable) { setFlagBit(FlagEnable, enable); } + +bool J2534SetupIso15765FlowControlMessage::getIs29BitEnabled(void) const { return getFlagBit(FlagId29BitEnable); } +void J2534SetupIso15765FlowControlMessage::setIs29BitEnabled(bool enable) { setFlagBit(FlagId29BitEnable, enable); rebuildFilterBytes(); } + +bool J2534SetupIso15765FlowControlMessage::getIsFc29BitEnabled(void) const { return getFlagBit(FlagFcId29BitEnable); } +void J2534SetupIso15765FlowControlMessage::setIsFc29BitEnabled(bool enable) { setFlagBit(FlagFcId29BitEnable, enable); } + +bool J2534SetupIso15765FlowControlMessage::getExtAddressEnabled(void) const { return getFlagBit(FlagExtAddressEnable); } +void J2534SetupIso15765FlowControlMessage::setExtAddressEnabled(bool enable) { setFlagBit(FlagExtAddressEnable, enable); rebuildFilterBytes(); } + +bool J2534SetupIso15765FlowControlMessage::getFcExtAddressEnabled(void) const { return getFlagBit(FlagFcExtAddressEnable); } +void J2534SetupIso15765FlowControlMessage::setFcExtAddressEnabled(bool enable) { setFlagBit(FlagFcExtAddressEnable, enable); } + +bool J2534SetupIso15765FlowControlMessage::getFlowControlTransmissionEnabled(void) const { return getFlagBit(FlagEnableFlowControlTransmit); } +void J2534SetupIso15765FlowControlMessage::setFlowControlTransmissionEnabled(bool enable) { setFlagBit(FlagEnableFlowControlTransmit, enable); } + +bool J2534SetupIso15765FlowControlMessage::getPaddingEnabled(void) const { return getFlagBit(FlagPaddingEnable); } +void J2534SetupIso15765FlowControlMessage::setPaddingEnabled(bool enable) { setFlagBit(FlagPaddingEnable, enable); } + +bool J2534SetupIso15765FlowControlMessage::getIsCanFd(void) const { return getFlagBit(FlagIsCanFd); } +void J2534SetupIso15765FlowControlMessage::setIsCanFd(bool enable) { setFlagBit(FlagIsCanFd, enable); } + +bool J2534SetupIso15765FlowControlMessage::getIsBrsEnabled(void) const { return getFlagBit(FlagIsBrsEnabled); } +void J2534SetupIso15765FlowControlMessage::setIsBrsEnabled(bool enable) { setFlagBit(FlagIsBrsEnabled, enable); } + +void J2534SetupIso15765FlowControlMessage::setFlagBit(uint32_t mask, bool enable) { + uint32_t f = read32LE(FlagsOffset); + if(enable) f |= mask; + else f &= ~mask; + write32LE(FlagsOffset, f); +} + +bool J2534SetupIso15765FlowControlMessage::getFlagBit(uint32_t mask) const { + return (read32LE(FlagsOffset) & mask) != 0; +} + +uint16_t Iso15765TxSetupMessage::getIdx(void) const { return read16LE(SetupIdxOffset); } +void Iso15765TxSetupMessage::setIdx(uint16_t idx) { write16LE(SetupIdxOffset, idx); } + +uint16_t Iso15765TxSetupMessage::getCoreMiniId(void) const { return read16LE(SetupCoreMiniOffset); } +void Iso15765TxSetupMessage::setCoreMiniId(uint16_t coreMiniId) { write16LE(SetupCoreMiniOffset, coreMiniId); } + +uint32_t Iso15765TxSetupMessage::getMessageLength(void) const { return read32LE(SetupMsgLenOffset); } +void Iso15765TxSetupMessage::setMessageLength(uint32_t messageLength) { write32LE(SetupMsgLenOffset, messageLength); } + +uint8_t Iso15765TxSetupMessage::getPadding(void) const { return argumentData[SetupPaddingOffset]; } +void Iso15765TxSetupMessage::setPadding(uint8_t padding) { argumentData[SetupPaddingOffset] = padding; } + +uint8_t Iso15765TxSetupMessage::getTxDl(void) const { return argumentData[SetupTxDlOffset]; } +void Iso15765TxSetupMessage::setTxDl(uint8_t txDl) { argumentData[SetupTxDlOffset] = txDl; } + +uint32_t Iso15765TxSetupMessage::getId(void) const { return read32LE(SetupIdOffset); } +void Iso15765TxSetupMessage::setId(uint32_t id) { write32LE(SetupIdOffset, id); } + +uint32_t Iso15765TxSetupMessage::getFcId(void) const { return read32LE(SetupFcIdOffset); } +void Iso15765TxSetupMessage::setFcId(uint32_t fcId) { write32LE(SetupFcIdOffset, fcId); } + +uint32_t Iso15765TxSetupMessage::getFcIdMask(void) const { return read32LE(SetupFcIdMaskOffset); } +void Iso15765TxSetupMessage::setFcIdMask(uint32_t fcIdMask) { write32LE(SetupFcIdMaskOffset, fcIdMask); } + +uint8_t Iso15765TxSetupMessage::getFlowControlExtendedAddress(void) const { return argumentData[SetupFcExtAddrOffset]; } +void Iso15765TxSetupMessage::setFlowControlExtendedAddress(uint8_t addr) { argumentData[SetupFcExtAddrOffset] = addr; } + +uint8_t Iso15765TxSetupMessage::getExtendedAddress(void) const { return argumentData[SetupExtAddrOffset]; } +void Iso15765TxSetupMessage::setExtendedAddress(uint8_t addr) { argumentData[SetupExtAddrOffset] = addr; } + +uint16_t Iso15765TxSetupMessage::getFsTimeout(void) const { return read16LE(SetupFsTimeoutOffset); } +void Iso15765TxSetupMessage::setFsTimeout(uint16_t timeout) { write16LE(SetupFsTimeoutOffset, timeout); } + +uint16_t Iso15765TxSetupMessage::getFsWait(void) const { return read16LE(SetupFsWaitOffset); } +void Iso15765TxSetupMessage::setFsWait(uint16_t wait) { write16LE(SetupFsWaitOffset, wait); } + +uint32_t Iso15765TxSetupMessage::getFlags(void) const { return read32LE(SetupFlagsOffset); } +void Iso15765TxSetupMessage::setFlags(uint32_t flags) { write32LE(SetupFlagsOffset, flags); } + +uint8_t Iso15765TxSetupMessage::getStMin(void) const { return argumentData[SetupStMinOffset]; } +void Iso15765TxSetupMessage::setStMin(uint8_t stMin) { argumentData[SetupStMinOffset] = stMin; } + +uint8_t Iso15765TxSetupMessage::getBlockSize(void) const { return argumentData[SetupBlockSizeOffset]; } +void Iso15765TxSetupMessage::setBlockSize(uint8_t blockSize) { argumentData[SetupBlockSizeOffset] = blockSize; } + +bool Iso15765TxSetupMessage::getIs29BitEnabled(void) const { return getFlagBit(FlagId29BitEnable); } +void Iso15765TxSetupMessage::setIs29BitEnabled(bool enable) { setFlagBit(FlagId29BitEnable, enable); } +bool Iso15765TxSetupMessage::getIsFc29BitEnabled(void) const { return getFlagBit(FlagFcId29BitEnable); } +void Iso15765TxSetupMessage::setIsFc29BitEnabled(bool enable) { setFlagBit(FlagFcId29BitEnable, enable); } +bool Iso15765TxSetupMessage::getExtAddressEnabled(void) const { return getFlagBit(FlagExtAddressEnable); } +void Iso15765TxSetupMessage::setExtAddressEnabled(bool enable) { setFlagBit(FlagExtAddressEnable, enable); } +bool Iso15765TxSetupMessage::getFcExtAddressEnabled(void) const { return getFlagBit(FlagFcExtAddressEnable); } +void Iso15765TxSetupMessage::setFcExtAddressEnabled(bool enable) { setFlagBit(FlagFcExtAddressEnable, enable); } +bool Iso15765TxSetupMessage::getOverrideStMin(void) const { return getFlagBit(FlagOverrideStMin); } +void Iso15765TxSetupMessage::setOverrideStMin(bool enable) { setFlagBit(FlagOverrideStMin, enable); } +bool Iso15765TxSetupMessage::getOverrideBlockSize(void) const { return getFlagBit(FlagOverrideBlockSize); } +void Iso15765TxSetupMessage::setOverrideBlockSize(bool enable) { setFlagBit(FlagOverrideBlockSize, enable); } +bool Iso15765TxSetupMessage::getPaddingEnabled(void) const { return getFlagBit(FlagPaddingEnable); } +void Iso15765TxSetupMessage::setPaddingEnabled(bool enable) { setFlagBit(FlagPaddingEnable, enable); } +bool Iso15765TxSetupMessage::getIsCanFd(void) const { return getFlagBit(FlagIsCanFd); } +void Iso15765TxSetupMessage::setIsCanFd(bool enable) { setFlagBit(FlagIsCanFd, enable); } +bool Iso15765TxSetupMessage::getIsBrsEnabled(void) const { return getFlagBit(FlagIsBrsEnabled); } +void Iso15765TxSetupMessage::setIsBrsEnabled(bool enable) { setFlagBit(FlagIsBrsEnabled, enable); } + +bool Iso15765TxSetupMessage::getFlagBit(uint32_t mask) const { + return (getFlags() & mask) != 0; +} + +void Iso15765TxSetupMessage::setFlagBit(uint32_t mask, bool enable) { + uint32_t f = getFlags(); + if(enable) f |= mask; + else f &= ~mask; + setFlags(f); +} + +uint16_t Iso15765TxDataMessage::getIdx(void) const { return read16LE(DataIdxOffset); } +void Iso15765TxDataMessage::setIdx(uint16_t idx) { write16LE(DataIdxOffset, idx); } + +uint32_t Iso15765TxDataMessage::getOffset(void) const { return read32LE(DataOffsetOffset); } +void Iso15765TxDataMessage::setOffset(uint32_t offset) { write32LE(DataOffsetOffset, offset); } + +uint16_t Iso15765TxDataMessage::getLen(void) const { return read16LE(DataLenOffset); } + +const uint8_t* Iso15765TxDataMessage::getData(void) const { return argumentData.data() + DataPayloadOffset; } + +void Iso15765TxDataMessage::setData(const uint8_t* sourceData, uint16_t len) { + len = std::min(len, (uint16_t)MaxDataLength); + argumentData.resize(DataPayloadOffset + len); + write16LE(DataLenOffset, len); + if(len > 0 && sourceData != nullptr) + std::copy(sourceData, sourceData + len, argumentData.begin() + DataPayloadOffset); +} \ No newline at end of file diff --git a/device/device.cpp b/device/device.cpp index 767d0bb..949659a 100644 --- a/device/device.cpp +++ b/device/device.cpp @@ -239,12 +239,12 @@ std::vector Device::getChipVersions(bool refreshComponents) { auto& version = chipVersions.back(); auto disectedVersion = disectVersion(component.dotVersion); - version.id = chipInfo.id; - version.name = chipInfo.name; - version.major = disectedVersion[0]; - version.minor = disectedVersion[1]; + version.id = chipInfo.id; + version.name = chipInfo.name; + version.major = disectedVersion[0]; + version.minor = disectedVersion[1]; version.maintenance = disectedVersion[2]; - version.build = disectedVersion[3]; + version.build = disectedVersion[3]; } } } @@ -264,12 +264,12 @@ std::vector Device::getChipVersions(bool refreshComponents) { chipVersions.emplace_back(); auto& version = chipVersions.back(); - version.id = chipInfo.id; - version.name = chipInfo.name; - version.major = appVer->major; - version.minor = appVer->minor; + version.id = chipInfo.id; + version.name = chipInfo.name; + version.major = appVer->major; + version.minor = appVer->minor; version.maintenance = 0; - version.build = 0; + version.build = 0; } } return chipVersions; @@ -4127,3 +4127,256 @@ void Device::restartHeartbeat() { stopHeartbeat(); startHeartbeat(); } + +bool Device::iso15765Enable(const Network& network) { + return J2534_EnableIso15765(network, true); +} + +bool Device::iso15765DisableAll(void) { + bool ok = true; + for(const auto& slot : iso15765FirmwareEnabled) + { + if (slot.second) + { + if (!J2534_EnableIso15765(slot.first, false)) + ok = false; + } + } + return ok; +} + +bool Device::iso15765TransmitMessage(const Network& network, const Iso15765MessageArgs& msg, const std::chrono::milliseconds& timeout) { + if (!isOnline()) + return false; + + if(iso15765FirmwareEnabled.find(network) == iso15765FirmwareEnabled.end() || !iso15765FirmwareEnabled[network]) + return false; + + bool result; + Iso15765TxSetupMessage setupMsg; + + //Set the network ID to use + if(const auto& coreMiniId = network.getCoreMini()) + setupMsg.setCoreMiniId((uint16_t)*coreMiniId); + + const uint8_t txIndex = msg.getTxIndex(); + setupMsg.setIdx(txIndex); + + setupMsg.setId(msg.getArbId().Id); + setupMsg.setFcId(msg.getFlowControlArbId().Id); + setupMsg.setFcIdMask(msg.getFlowControlArbIdMask()); + + setupMsg.setFsTimeout(msg.getFsTimeout()); + setupMsg.setFsWait(msg.getFsWaitTimeout()); + + const uint32_t messageLength = (uint32_t)msg.getData().size(); + setupMsg.setMessageLength(messageLength); + setupMsg.setIs29BitEnabled(msg.getArbId().Is29Bit); + setupMsg.setIsFc29BitEnabled(msg.getFlowControlArbId().Is29Bit); + if(const auto& extAddress = msg.getExtendedAddress()) + { + setupMsg.setExtAddressEnabled(true); + setupMsg.setExtendedAddress(*extAddress); + } + if(const auto& extFcAddress = msg.getFlowControlExtendedAddress()) + { + setupMsg.setFcExtAddressEnabled(true); + setupMsg.setFlowControlExtendedAddress(*extFcAddress); + } + if(const auto& stMin = msg.getStMin()) + { + setupMsg.setOverrideStMin(true); + setupMsg.setStMin(*stMin); + } + if(const auto& blockSize = msg.getBlockSize()) + { + setupMsg.setOverrideBlockSize(true); + setupMsg.setBlockSize(*blockSize); + } + if(const auto& padding = msg.getPaddingValue()) + { + setupMsg.setPaddingEnabled(true); + setupMsg.setPadding(*padding); + } + setupMsg.setIsBrsEnabled(msg.getIsBrsEnabled()); + setupMsg.setIsCanFd(msg.getIsCanFd()); + setupMsg.setTxDl(msg.getTxDl()); + + { + uint32_t octetsToSend = messageLength; + const uint8_t* payload = msg.getData().data(); + uint32_t offset = 0; + + result = J2534_Transaction(network, std::move(setupMsg), timeout); + while(result && octetsToSend) + { + const uint16_t chunkSize = (uint16_t)std::min((uint32_t)Iso15765TxDataMessage::MaxDataLength, octetsToSend); + + Iso15765TxDataMessage dataMsg; + dataMsg.setIdx(txIndex); + dataMsg.setOffset(offset); + dataMsg.setData(&payload[offset], chunkSize); + + result = J2534_Transaction(network, std::move(dataMsg), timeout); + + offset += chunkSize; + octetsToSend -= chunkSize; + } + } + + return result; +} + +bool Device::iso15765SetupRxFlowControl(const Network& network, const Iso15765MessageArgs& msg) { + if (!isOnline()) + return false; + + if(iso15765FirmwareEnabled.find(network) == iso15765FirmwareEnabled.end() || !iso15765FirmwareEnabled[network]) + return false; + + J2534SetupIso15765FlowControlMessage rxFlowControl; + + rxFlowControl.setIsBrsEnabled(msg.getIsBrsEnabled()); + rxFlowControl.setIsCanFd(msg.getIsCanFd()); + rxFlowControl.setIdx(msg.getTxIndex()); + if(const auto& coreMiniId = network.getCoreMini()) + rxFlowControl.setCoreMiniId((uint16_t)*coreMiniId); + rxFlowControl.setEnable(true); + if(const auto& blockSize = msg.getBlockSize()) + { + rxFlowControl.setBlockSize(*blockSize); + } + rxFlowControl.setCfTimeout(msg.getCfTimeout()); + rxFlowControl.setFlowControlTransmissionEnabled(msg.getIsFlowControlEnabled()); + if(const auto& extAddress = msg.getExtendedAddress()) + { + rxFlowControl.setExtendedAddress(*extAddress); + rxFlowControl.setExtAddressEnabled(true); + } + if(const auto& fcExtAddress = msg.getFlowControlExtendedAddress()) + { + rxFlowControl.setFlowControlExtendedAddress(*fcExtAddress); + rxFlowControl.setFcExtAddressEnabled(true); + } + rxFlowControl.setFcId(msg.getFlowControlArbId().Id); + rxFlowControl.setIsFc29BitEnabled(msg.getFlowControlArbId().Is29Bit); + rxFlowControl.setIs29BitEnabled(msg.getArbId().Is29Bit); + rxFlowControl.setId(msg.getArbId().Id); + rxFlowControl.setIdMask(msg.getFlowControlArbIdMask()); + if(const auto& padding = msg.getPaddingValue()) + { + rxFlowControl.setPadding(*padding); + rxFlowControl.setPaddingEnabled(true); + } + if(const auto& stMin = msg.getStMin()) + { + rxFlowControl.setStMin(*stMin); + } + + return J2534_Transaction(network, std::move(rxFlowControl), std::chrono::milliseconds(0)); +} + +bool Device::J2534_Transaction(const Network& network, J2534CommandMessage&& msg, const std::chrono::milliseconds& timeout) { + msg.network = network; + if (timeout.count()) + { + static std::shared_ptr filter = std::make_shared(Command::J2534Command); + const auto& response = com->waitForMessageSync([&](void) + { + return com->sendCommand(msg.command, msg.getArgumentData()); + }, filter, timeout); + + if (!response) + { + report(APIEvent::Type::NoDeviceResponse, APIEvent::Severity::Error); + return false; + } + + auto responseMsg = std::dynamic_pointer_cast(response); + if (!responseMsg || responseMsg->command != Command::J2534Command) + { + report(APIEvent::Type::MessageFormattingError, APIEvent::Severity::Error); + return false; + } + } + else + { + return com->sendCommand(msg.command, msg.getArgumentData()); + } + + // NOTE: responseMsg->data.front() + + return true; +} + + +bool Device::J2534_ClearRxFilters(const Network& network) +{ + return J2534_Transaction(network, J2534CommandMessage(J2534CommandMessage::J2534Command::SetupClearCanRxFilters, { (uint8_t)0 /* is this necessary? */}), std::chrono::milliseconds(0)); +} + +bool Device::J2534_SetupCanRxFilter(const Network& network, const J2534_RxCanFilter& rxCanFilter) +{ + return J2534_Transaction(network, J2534SetupCanRxFilteringMessage(rxCanFilter), std::chrono::milliseconds(0)); +} + +bool Device::J2534_EnableFiltering(const Network& network, const bool enable) +{ + return J2534_Transaction(network, J2534EnableFilteringMessage(enable), std::chrono::milliseconds(0)); +} + + + +bool Device::J2534_EnableIso15765(const Network& network, const bool enable) +{ + if (iso15765FirmwareEnabled[network] == enable) + return true; + + if (!J2534_Transaction(network, J2534EnableMessage(enable), std::chrono::milliseconds(0))) + return false; + + iso15765FirmwareEnabled[network] = enable; + + if (enable) + { + // Turn OFF filtering of CAN messages in the firmware + return J2534_EnableFirmwareUsbPassFilters(network, false); + } + return true; +} + +bool Device::J2534_ClearRxFilter(const Network& network, unsigned int iIndex) +{ + if (const auto& slot = iso15765FirmwareEnabled.find(network); slot != iso15765FirmwareEnabled.end() && slot->second) + { + J2534_RxCanFilter rxFilter; + memset(&rxFilter, 0, sizeof(rxFilter)); + rxFilter.idx = (uint16_t)iIndex; + return J2534_SetupCanRxFilter(network, rxFilter); + } + return false; +} + +bool Device::J2534_EnableFirmwareUsbPassFilters(const Network& network, const bool enable) +{ + const auto& slot = iso15765FirmwareEnabled.find(network); + if (slot == iso15765FirmwareEnabled.end()) + return false; + //check for disconnect here! + if (!slot->second) + { + // Tried to shut-off Rx table message filtering but FW ISO15765 was not enabled + return true; //just ignore it, no filtering can occur anyway + } + + if (enable) //turning ON filtering in the Firmware based on the rx table + { + //Rx table message filtering is on by default in the firmware when m_bISO15765_FW_Enabled is true + //need to check here to see if we set index 0 to a PASS all filter (i.e. turned off USB filtering) + if (!J2534_ClearRxFilters(network)) + return false; + } + + // New "filtering enable" mechanism - old firmware ignores this frame + return J2534_EnableFiltering(network, enable); +} diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index f57c476..3577e9f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -36,6 +36,7 @@ option(LIBICSNEO_BUILD_CPP_ANALOG_OUT_EXAMPLE "Build the analog output example." option(LIBICSNEO_BUILD_CPP_DISKFORMAT_EXAMPLE "Build the disk format example." ON) option(LIBICSNEO_BUILD_CPP_T1S_EXAMPLE "Build the T1S example." ON) option(LIBICSNEO_BUILD_CPP_GPTP_EXAMPLE "Build the C++ gPTP settings example." ON) +option(LIBICSNEO_BUILD_CPP_ISO15765_LOOPBACK_EXAMPLE "Build the C++ ISO15765 two-device CAN1 loopback example." ON) add_compile_options(${LIBICSNEO_COMPILER_WARNINGS}) @@ -190,3 +191,7 @@ endif() if(LIBICSNEO_BUILD_CPP_GPTP_EXAMPLE) add_subdirectory(cpp/gptp) endif() + +if(LIBICSNEO_BUILD_CPP_ISO15765_LOOPBACK_EXAMPLE) + add_subdirectory(cpp/iso15765_loopback) +endif() diff --git a/examples/cpp/iso15765_loopback/CMakeLists.txt b/examples/cpp/iso15765_loopback/CMakeLists.txt new file mode 100644 index 0000000..e66b5d9 --- /dev/null +++ b/examples/cpp/iso15765_loopback/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(libicsneocpp-iso15765-loopback src/ISO15765Loopback.cpp) +target_link_libraries(libicsneocpp-iso15765-loopback icsneocpp) diff --git a/examples/cpp/iso15765_loopback/src/ISO15765Loopback.cpp b/examples/cpp/iso15765_loopback/src/ISO15765Loopback.cpp new file mode 100644 index 0000000..55031a3 --- /dev/null +++ b/examples/cpp/iso15765_loopback/src/ISO15765Loopback.cpp @@ -0,0 +1,450 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icsneo/icsneocpp.h" + +namespace { + +using namespace std::chrono_literals; + +enum class Mode { + None, + Raw, + SingleFrame, + Both, +}; + +struct Options { + Mode mode = Mode::None; + std::string testerSerial; + std::string ecuSerial; + int64_t baud = 500000; + bool keepBaud = false; + uint32_t txId = 0x7E0; + uint32_t fcId = 0x7E8; + std::optional payloadBytes; + int listenMs = 2000; + uint8_t padding = 0x00; + uint16_t fsTimeoutMs = 75; + uint16_t fsWaitMs = 150; + uint16_t cfTimeoutMs = 150; +}; + +std::mutex gLogMutex; + +void logLine(const std::string& line) { + std::lock_guard lk(gLogMutex); + std::cout << line << std::endl; +} + +bool iequals(const std::string& a, const std::string& b) { + if(a.size() != b.size()) + return false; + for(size_t i = 0; i < a.size(); i++) { + if(std::tolower(static_cast(a[i])) != std::tolower(static_cast(b[i]))) + return false; + } + return true; +} + +std::string hexBytes(const std::vector& data) { + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for(size_t i = 0; i < data.size(); i++) { + if(i) + oss << ' '; + oss << std::setw(2) << static_cast(data[i]); + } + return oss.str(); +} + +std::string pciDescribe(const std::vector& data) { + if(data.empty()) + return {}; + const uint8_t pciType = static_cast(data[0] >> 4); + const uint8_t pciLow = static_cast(data[0] & 0x0F); + std::ostringstream oss; + switch(pciType) { + case 0x0: + if(pciLow > 7 || data.size() < static_cast(1 + pciLow)) + return {}; + oss << "SF len=" << static_cast(pciLow); + break; + case 0x1: { + if(data.size() < 8) + return {}; + const uint16_t len = static_cast((pciLow << 8) | data[1]); + if(len < 8) + return {}; + oss << "FF len=" << len; + break; + } + case 0x2: + if(data.size() < 2) + return {}; + oss << "CF SN=" << static_cast(pciLow); + break; + case 0x3: { + if(data.size() < 3 || pciLow > 2) + return {}; + static const char* fsName[] = { "CTS", "WAIT", "OVFLW" }; + oss << "FC FS=" << fsName[pciLow]; + oss << " BS=" << static_cast(data[1]); + oss << " STmin=0x" << std::hex << std::setw(2) << std::setfill('0') + << static_cast(data[2]); + break; + } + default: + return {}; + } + return oss.str(); +} + +void printUsage(const char* argv0) { + std::cerr + << "Usage: " << argv0 << " --raw|--sf|--both [options]\n\n" + << "Two Intrepid devices with CAN1 wired together (CANH/CANL, terminated).\n" + << "Prints CAN1 frames with ISO-TP PCI decode (SF/FF/CF/FC).\n\n" + << "Modes:\n" + << " --raw Raw CAN ping-pong (cable check)\n" + << " --sf Firmware ISO-TP single-frame TX from tester\n" + << " --both Firmware ISO-TP TX on tester, firmware flow-control on ECU\n\n" + << "Devices:\n" + << " --tester SERIAL Tester (sender) serial\n" + << " --ecu SERIAL ECU / peer serial\n" + << " Optional if exactly two devices are found.\n\n" + << "Optional:\n" + << " --baud N CAN1 baud (default 500000, applied temporarily)\n" + << " --keep-baud Do not change device baud\n" + << " --tx-id HEX Tester ISO-TP CAN ID (default 0x7E0)\n" + << " --fc-id HEX Flow-control CAN ID (default 0x7E8)\n" + << " --bytes N ISO-TP payload size (sf default 5, both default 20)\n" + << " --listen-ms N Time to listen after the action (default 2000)\n"; +} + +uint32_t parseU32(const std::string& s) { + return static_cast(std::stoul(s, nullptr, 0)); +} + +bool parseArgs(int argc, char** argv, Options& opt) { + for(int i = 1; i < argc; i++) { + const std::string a = argv[i]; + auto need = [&](const char* name) -> std::string { + if(i + 1 >= argc) + throw std::runtime_error(std::string("missing value for ") + name); + return argv[++i]; + }; + if(a == "--raw") + opt.mode = Mode::Raw; + else if(a == "--sf") + opt.mode = Mode::SingleFrame; + else if(a == "--both") + opt.mode = Mode::Both; + else if(a == "--tester") + opt.testerSerial = need("--tester"); + else if(a == "--ecu") + opt.ecuSerial = need("--ecu"); + else if(a == "--baud") + opt.baud = static_cast(std::stoll(need("--baud"))); + else if(a == "--keep-baud") + opt.keepBaud = true; + else if(a == "--tx-id") + opt.txId = parseU32(need("--tx-id")); + else if(a == "--fc-id") + opt.fcId = parseU32(need("--fc-id")); + else if(a == "--bytes") + opt.payloadBytes = static_cast(std::stoul(need("--bytes"))); + else if(a == "--listen-ms") + opt.listenMs = std::stoi(need("--listen-ms")); + else if(a == "-h" || a == "--help") { + printUsage(argv[0]); + return false; + } else { + std::cerr << "Unknown argument: " << a << "\n"; + printUsage(argv[0]); + return false; + } + } + if(opt.mode == Mode::None) { + printUsage(argv[0]); + return false; + } + return true; +} + +size_t defaultPayloadSize(Mode mode) { + return mode == Mode::SingleFrame ? 5 : 20; +} + +std::vector makePayload(size_t n) { + std::vector data(n); + for(size_t i = 0; i < n; i++) + data[i] = static_cast(i & 0xFF); + return data; +} + +bool isExtended(uint32_t id) { + return id > 0x7FFu; +} + +void printApiResult(const char* what, bool ok) { + std::ostringstream oss; + oss << " " << what << ": " << (ok ? "OK" : "FAIL"); + if(!ok) { + const auto err = icsneo::GetLastError(); + if(err.getType() != icsneo::APIEvent::Type::NoErrorFound) + oss << " [" << err << "]"; + } + logLine(oss.str()); +} + +bool transmitCan(icsneo::Device& device, uint32_t arbId, std::vector data) { + auto frame = std::make_shared(); + frame->network = icsneo::Network::NetID::DWCAN_01; + frame->arbid = arbId; + frame->data = std::move(data); + frame->isExtended = isExtended(arbId); + frame->isCANFD = false; + const bool ok = device.transmit(frame); + if(!ok) + printApiResult("transmit CAN", ok); + return ok; +} + +void fillIso15765(icsneo::Iso15765MessageArgs& msg, const Options& opt, const std::vector& payload, bool rx) { + msg.setTxIndex(0); + msg.setArbId(icsneo::Iso15765MessageArgs::ArbId(opt.txId, isExtended(opt.txId))); + msg.setFlowControlArbId(icsneo::Iso15765MessageArgs::ArbId(opt.fcId, isExtended(opt.fcId))); + msg.setFlowControlArbIdMask(isExtended(opt.txId) ? 0x1FFFFFFFu : 0x7FFu); + msg.setIsCanFd(false); + msg.setIsBrsEnabled(false); + msg.setTxDl(8); + msg.setPaddingValue(opt.padding); + msg.setFsTimeout(opt.fsTimeoutMs); + msg.setFsWaitTimeout(opt.fsWaitMs); + msg.setCfTimeout(opt.cfTimeoutMs); + msg.setIsFlowControlEnabled(rx); + if(!payload.empty()) { + auto copy = payload; + msg.setData(std::move(copy)); + } +} + +struct Sniffer { + const char* tag = ""; + std::optional t0; + std::mutex t0Mutex; + + void onCan(const std::shared_ptr& can) { + { + std::lock_guard lk(t0Mutex); + if(!t0) + t0 = can->timestamp; + } + + double relMs = 0.0; + { + std::lock_guard lk(t0Mutex); + if(t0 && can->timestamp >= *t0) + relMs = static_cast(can->timestamp - *t0) / 1e6; + } + + std::ostringstream oss; + oss << " [" << tag << (can->transmitted ? " TX" : " RX") << "] " + << std::fixed << std::setprecision(3) << relMs << " ms " + << can->network << " 0x" << std::hex << std::uppercase + << std::setw(isExtended(can->arbid) ? 8 : 3) << std::setfill('0') << can->arbid + << std::dec << " [" << can->data.size() << "] " << hexBytes(can->data); + const auto pci = pciDescribe(can->data); + if(!pci.empty()) + oss << " " << pci; + logLine(oss.str()); + } +}; + +void addCanSniffer(icsneo::Device& device, Sniffer& sniffer) { + auto filter = std::make_shared(icsneo::Network::NetID::DWCAN_01); + device.addMessageCallback(std::make_shared(filter, [&sniffer](std::shared_ptr message) { + if(message->type != icsneo::Message::Type::Frame) + return; + auto frame = std::static_pointer_cast(message); + if(frame->network.getType() != icsneo::Network::Type::CAN) + return; + sniffer.onCan(std::static_pointer_cast(message)); + })); +} + +bool openAndOnline(std::shared_ptr& device, const Options& opt) { + logLine(std::string("Opening ") + device->describe() + " ..."); + if(!device->open()) { + printApiResult("open", false); + return false; + } + if(!opt.keepBaud && device->settings) { + if(!device->settings->setBaudrateFor(icsneo::Network::NetID::DWCAN_01, opt.baud)) + printApiResult("setBaudrateFor", false); + else if(!device->settings->apply(true)) + printApiResult("settings->apply(temporary)", false); + } + if(!device->goOnline()) { + printApiResult("goOnline", false); + device->close(); + return false; + } + return true; +} + +void closeDevice(const std::shared_ptr& device) { + if(!device) + return; + device->iso15765DisableAll(); + if(device->isOnline()) + device->goOffline(); + if(device->isOpen()) + device->close(); +} + +bool enableIso(icsneo::Device& device, const char* who) { + const bool ok = device.iso15765Enable(icsneo::Network(icsneo::Network::NetID::DWCAN_01)); + printApiResult((std::string(who) + " ISO15765_Enable").c_str(), ok); + return ok; +} + +bool receiveIso(icsneo::Device& device, const Options& opt) { + icsneo::Iso15765MessageArgs msg; + fillIso15765(msg, opt, {}, true); + const bool ok = device.iso15765SetupRxFlowControl(icsneo::Network(icsneo::Network::NetID::DWCAN_01), msg); + printApiResult("ECU ISO15765_ReceiveMessage", ok); + return ok; +} + +bool transmitIso(icsneo::Device& device, const Options& opt, const std::vector& payload) { + icsneo::Iso15765MessageArgs msg; + fillIso15765(msg, opt, payload, false); + const bool ok = device.iso15765TransmitMessage( + icsneo::Network(icsneo::Network::NetID::DWCAN_01), msg, std::chrono::milliseconds(0)); + printApiResult("tester ISO15765_TransmitMessage", ok); + return ok; +} + +std::shared_ptr findBySerial(const std::vector>& devices, const std::string& serial) { + for(const auto& d : devices) { + if(iequals(d->getSerial(), serial)) + return d; + } + return nullptr; +} + +} // namespace + +int main(int argc, char** argv) { + Options opt; + try { + if(!parseArgs(argc, argv, opt)) + return 1; + } catch(const std::exception& ex) { + std::cerr << ex.what() << std::endl; + return 1; + } + + std::cout << "libicsneo " << icsneo::GetVersion() << std::endl; + + auto devices = icsneo::FindAllDevices(); + std::cout << "Found " << devices.size() << " device(s)\n"; + for(const auto& d : devices) + std::cout << " " << d->describe() << " serial=" << d->getSerial() << "\n"; + + if(devices.size() < 2) { + std::cerr << "Need two devices connected.\n"; + return 1; + } + + std::shared_ptr tester; + std::shared_ptr ecu; + if(opt.testerSerial.empty() && opt.ecuSerial.empty()) { + if(devices.size() != 2) { + std::cerr << "More than two devices found; pass --tester and --ecu serials.\n"; + return 1; + } + tester = devices[0]; + ecu = devices[1]; + std::cout << "No serials given; using first as tester, second as ECU.\n"; + } else { + if(opt.testerSerial.empty() || opt.ecuSerial.empty()) { + std::cerr << "Pass both --tester and --ecu, or neither.\n"; + return 1; + } + tester = findBySerial(devices, opt.testerSerial); + ecu = findBySerial(devices, opt.ecuSerial); + if(!tester || !ecu) { + std::cerr << "Could not match tester/ECU serials.\n"; + return 1; + } + } + + const size_t payloadSize = opt.payloadBytes.value_or(defaultPayloadSize(opt.mode)); + const auto payload = makePayload(payloadSize); + + std::cout << "Tester: " << tester->describe() << "\n" + << "ECU: " << ecu->describe() << "\n"; + if(opt.mode != Mode::Raw) { + std::ostringstream hdr; + hdr << "CAN1 TX 0x" << std::hex << std::uppercase << opt.txId + << " FC 0x" << opt.fcId << std::dec + << " payload " << payloadSize << " bytes"; + std::cout << hdr.str() << "\n"; + } + std::cout << std::endl; + + if(!openAndOnline(tester, opt) || !openAndOnline(ecu, opt)) + return 1; + + Sniffer testerSniff; + testerSniff.tag = "TESTER"; + Sniffer ecuSniff; + ecuSniff.tag = "ECU "; + addCanSniffer(*tester, testerSniff); + addCanSniffer(*ecu, ecuSniff); + + switch(opt.mode) { + case Mode::Raw: + logLine("Mode --raw: tester 0x123, then ECU 0x456"); + transmitCan(*tester, 0x123, { 0x11, 0x22, 0x33, 0x44 }); + std::this_thread::sleep_for(200ms); + transmitCan(*ecu, 0x456, { 0x55, 0x66, 0x77, 0x88 }); + break; + case Mode::SingleFrame: + logLine("Mode --sf: firmware ISO-TP single frame from tester"); + if(payload.size() > 7) + logLine(" warning: payload > 7 bytes will be a First Frame (no flow control in this mode)"); + enableIso(*tester, "tester"); + transmitIso(*tester, opt, payload); + break; + case Mode::Both: + logLine("Mode --both: firmware TX on tester, firmware flow-control on ECU"); + enableIso(*tester, "tester"); + enableIso(*ecu, "ECU"); + receiveIso(*ecu, opt); + std::this_thread::sleep_for(100ms); + transmitIso(*tester, opt, payload); + break; + case Mode::None: + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(opt.listenMs)); + + closeDevice(tester); + closeDevice(ecu); + return 0; +} diff --git a/include/icsneo/J2534.h b/include/icsneo/J2534.h index e225dfe..1724dd4 100644 --- a/include/icsneo/J2534.h +++ b/include/icsneo/J2534.h @@ -1,33 +1,133 @@ #ifndef _J2534_H #define _J2534_H +#include + +namespace icsneo { + //J2534 Commands #define J1534_NUM_PERIOD_TX_MSGS 128 #define J1534_NUM_PERIOD_RX_MSGS 128 -#define J2534NVCMD_SetISJ2534 0 -#define J2534NVCMD_SetISO5Baud 1 -#define J2534NVCMD_SetISOFastInit 2 -#define J2534NVCMD_SetISOCheckSum 3 -#define J2534NVCMD_SetISO9141Parms 4 -#define J2534NVCMD_GetISO9141Parms 5 -#define J2534NVCMD_ISO9141APIChkSum 6 -#define J2534NVCMD_SetNetworkBaudRate 7 -#define J2534NVCMD_GetNetworkBaudRate 8 -#define J2534NVCMD_EnableTransmitEvent 9 -#define J2534NVCMD_SetTransmitEvent 10 -#define J2534NVCMD_BlueEnableStopFilters 11 -#define J2534NVCMD_Blue15765HWSupport 12 -#define J2534NVCMD_GetTXBufferInfo 13 -#define J2534NVCMD_GetEncryptionKey 14 -#define J2534NVCMD_SetMiscIOForVBATT 15 -#define J2534NVCMD_EnableISO_KW_Network 16 -#define J2534NVCMD_SetJ1708CheckSum 17 -#define J2534NVCMD_GetTimestamp 18 -#define J2534NVCMD_GetCANFDRate 19 -#define J2534NVCMD_SetCANFDRate 20 -#define J2534NVCMD_GetCANFDTermination 21 -#define J2534NVCMD_SetCANFDTermination 22 -#define J2534NVCMD_GetCANFDFormat 23 -#define J2534NVCMD_SetCANFDFormat 24 + +enum class J2534Command : uint8_t { + SetISJ2534 = 0, + SetISO5Baud = 1, + SetISOFastInit = 2, + SetISOCheckSum = 3, + SetISO9141Parms = 4, + GetISO9141Parms = 5, + ISO9141APIChkSum = 6, + SetNetworkBaudRate = 7, + GetNetworkBaudRate = 8, + EnableTransmitEvent = 9, + SetTransmitEvent = 10, + BlueEnableStopFilters = 11, + Blue15765HWSupport = 12, + GetTXBufferInfo = 13, + GetEncryptionKey = 14, + SetMiscIOForVBATT = 15, + EnableISO_KW_Network = 16, + SetJ1708CheckSum = 17, + GetTimestamp = 18, + GetCANFDRate = 19, + SetCANFDRate = 20, + GetCANFDTermination = 21, + SetCANFDTermination = 22, + GetCANFDFormat = 23, + SetCANFDFormat = 24 +}; + +#pragma pack(push) +#pragma pack(1) + +/** + * SubFrame Commands + */ +enum class J2534SubCommand : uint8_t { + Enable = 0, + SetupIso15765RxFilter = 1, + SetupIso15765TxMessageProperties = 2, + SetupIso15765TxMessageDataBytes = 3, + SetupCanRxFilter = 4, + SetupClearCanRxFilters = 5, + EnableFiltering = 6 +}; + +typedef struct +{ + uint16_t uiFilterMask0; + uint16_t uiFilterID0; + uint16_t uiFilterMask1; + uint16_t uiFilterID1; + uint16_t uiFilterMask2; + uint16_t uiFilterID2; + union { + /** Mask and value words if rx filter is datalink filter*/ + struct + { + uint16_t uiFilterMask3; + uint16_t uiFilterID3; + uint16_t uiFilterMask4; + uint16_t uiFilterID4; + uint16_t uiFilterMask5; + uint16_t uiFilterID5; + uint16_t uiFilterMask6; + uint16_t uiFilterID6; + } datalink; + /** Mask and value bytes if rx filter is datalink filter*/ + struct + { + uint8_t uiFilterMask_DB0; + uint8_t uiFilterMask_DB1; + uint8_t uiFilterID_DB0; + uint8_t uiFilterID_DB1; + uint8_t uiFilterMask_DB2; + uint8_t uiFilterMask_DB3; + uint8_t uiFilterID_DB2; + uint8_t uiFilterID_DB3; + uint8_t uiFilterMask_DB4; + uint8_t uiFilterMask_DB5; + uint8_t uiFilterID_DB4; + uint8_t uiFilterID_DB5; + uint8_t uiFilterMask_DB6; + uint8_t uiFilterMask_DB7; + uint8_t uiFilterID_DB6; + uint8_t uiFilterID_DB7; + } datalinkBytes; + /** Mask and value bytes if rx filter is Iso15765-2*/ + struct + { + uint8_t uiFilterMask_DB0; + uint8_t uiFilterID_DB0; + uint8_t uiFilterMask_DB1; + uint8_t uiFilterID_DB1; + uint8_t uiFilterMask_DB2; + uint8_t uiFilterID_DB2; + uint8_t uiFilterMask_DB3; + uint8_t uiFilterID_DB3; + uint8_t uiFilterMask_DB4; + uint8_t uiFilterID_DB4; + uint8_t uiFilterMask_DB5; + uint8_t uiFilterID_DB5; + uint8_t uiFilterMask_DB6; + uint8_t uiFilterID_DB6; + uint8_t uiFilterMask_DB7; + uint8_t uiFilterID_DB7; + } iso15Bytes; + } args; +} MessageFilterBytes; + +typedef struct +{ + uint16_t idx; + uint16_t enabled; + uint16_t filterCount; + MessageFilterBytes fb; + uint16_t networkId; +} J2534_RxCanFilter; + +#pragma pack(pop) + +} // namespace icsneo #endif diff --git a/include/icsneo/communication/command.h b/include/icsneo/communication/command.h index f3b7818..f551b20 100644 --- a/include/icsneo/communication/command.h +++ b/include/icsneo/communication/command.h @@ -43,6 +43,7 @@ enum class Command : uint8_t { GetLogicalDiskInfo = 0xBB, // Previously known as RED_CMD_GET_SDCARD_INFO RequestStatusUpdate = 0xBC, ReadSettings = 0xC7, // Previously known as 3G_READ_SETTINGS_EX + J2534Command = 0xD7, // Previously known as RED_CMD_J2534_EXTENSION SetVBattMonitor = 0xDB, // Previously known as RED_CMD_CM_VBATT_MONITOR RequestBitSmash = 0xDC, // Previously known as RED_CMD_CM_BITSMASH WiVICommand = 0xDD, // Previously known as RED_CMD_WIVI_COMM diff --git a/include/icsneo/communication/message/iso15765message.h b/include/icsneo/communication/message/iso15765message.h new file mode 100644 index 0000000..10d2856 --- /dev/null +++ b/include/icsneo/communication/message/iso15765message.h @@ -0,0 +1,443 @@ +#ifndef __ISO_15765MESSAGE_H_ +#define __ISO_15765MESSAGE_H_ + +#ifdef __cplusplus + +#include "icsneo/communication/message/main51message.h" +#include "icsneo/communication/command.h" +#include "icsneo/J2534.h" + +#include +#include +#include + +namespace icsneo { + +class J2534CommandMessage : public Main51Message +{ +public: + enum class J2534Command : uint8_t + { + Enable = 0, //RED_CMD_J2534_EXTENSION_ENABLE, + SetupIso15765RxFilter = 1, //RED_CMD_J2534_EXTENSION_SETUP_ISO15_RX_FILTER, + SetupIso15765TxMessageProperties = 2, //RED_CMD_J2534_EXTENSION_SETUP_ISO15_TX_MESSAGE_PROPERTIES, + SetupIso15765TxMessageDataBytes = 3, //RED_CMD_J2534_EXTENSION_SETUP_ISO15_TX_MESSAGE_DATABYTES, + SetupCanRxFilter = 4, //RED_CMD_J2534_EXTENSION_SETUP_CAN_RX_FILTER, + SetupClearCanRxFilters = 5, //RED_CMD_J2534_EXTENSION_SETUP_CLEAR_CAN_RX_FILTERS, + EnableFiltering = 6, //RED_CMD_J2534_EXTENSION_ENABLE_FILTERING, + }; + + J2534CommandMessage(const J2534Command j2534Command, const std::vector& arguments = {}) + : Main51Message() { + command = Command::J2534Command; + argumentData.push_back((uint8_t)j2534Command); + argumentData.push_back(0); + setArgumentData(arguments); + } + virtual ~J2534CommandMessage(void) = default; + + J2534Command getJ2534Command(void) const { return (J2534Command)argumentData[0]; } + void setJ2534Command(const J2534Command& j2534Command) { argumentData[0] = (uint8_t)j2534Command; } + + const std::vector& getArgumentData(void) const { return argumentData; } + void setArgumentData(const std::vector& arguments) { argumentData.insert(argumentData.end(), arguments.begin(), arguments.end()); } + + static std::shared_ptr decodeToMessage(const std::vector& data); + +protected: + std::vector argumentData; + + uint16_t read16LE(size_t o) const; + void write16LE(size_t o, uint16_t v); + uint16_t read16BE(size_t o) const; + void write16BE(size_t o, uint16_t v); + void writeBits16LE(size_t o, unsigned bitPos, unsigned bitCount, uint32_t value); + uint32_t read32LE(size_t o) const; + void write32LE(size_t o, uint32_t v); + uint32_t read32BE(size_t o) const; + void write32BE(size_t o, uint32_t v); +}; + +class J2534EnableMessage : public J2534CommandMessage { +public: + J2534EnableMessage(const bool enable) + : J2534CommandMessage(J2534Command::Enable, { (uint8_t)enable }) { + } + bool getEnable(void) const { return (bool)argumentData[2]; } + void setEnable(bool enable) { argumentData[2] = (uint8_t)enable; } +}; + +class J2534EnableFilteringMessage : public J2534CommandMessage { +public: + J2534EnableFilteringMessage(const bool enable) + : J2534CommandMessage(J2534Command::EnableFiltering, { (uint8_t)enable }) { + } + bool getEnable(void) const { return (bool)argumentData[2]; } + void setEnable(bool enable) { argumentData[2] = (uint8_t)enable; } +}; + +class J2534SetupCanRxFilteringMessage : public J2534CommandMessage { +public: + J2534SetupCanRxFilteringMessage(const J2534_RxCanFilter& filter) + : J2534CommandMessage(J2534Command::SetupCanRxFilter) { + setRxCanFilter(filter); + } + + J2534_RxCanFilter getRxCanFilter(void) const; + + void setRxCanFilter(const J2534_RxCanFilter& filter); +}; + +class J2534SetupIso15765FlowControlMessage : public J2534CommandMessage +{ +public: + J2534SetupIso15765FlowControlMessage(); + + uint16_t getIdx(void) const; + void setIdx(uint16_t idx); + uint16_t getCoreMiniId(void) const; + void setCoreMiniId(uint16_t coreMiniId); + uint8_t getPadding(void) const; + void setPadding(uint8_t padding); + uint32_t getId(void) const; + void setId(uint32_t id); + uint32_t getIdMask(void) const; + void setIdMask(uint32_t idMask); + uint32_t getFcId(void) const; + void setFcId(uint32_t fcId); + uint8_t getFlowControlExtendedAddress(void) const; + void setFlowControlExtendedAddress(uint8_t addr); + uint8_t getExtendedAddress(void) const; + void setExtendedAddress(uint8_t addr); + uint8_t getBlockSize(void) const; + void setBlockSize(uint8_t blockSize); + uint8_t getStMin(void) const; + void setStMin(uint8_t stMin); + uint16_t getCfTimeout(void) const; + void setCfTimeout(uint16_t cfTimeout); + uint32_t getFlags(void) const; + void setFlags(uint32_t flags); + + bool getEnable(void) const; + void setEnable(bool enable); + bool getIs29BitEnabled(void) const; + void setIs29BitEnabled(bool enable); + bool getIsFc29BitEnabled(void) const; + void setIsFc29BitEnabled(bool enable); + bool getExtAddressEnabled(void) const; + void setExtAddressEnabled(bool enable); + bool getFcExtAddressEnabled(void) const; + void setFcExtAddressEnabled(bool enable); + bool getFlowControlTransmissionEnabled(void) const; + void setFlowControlTransmissionEnabled(bool enable); + bool getPaddingEnabled(void) const; + void setPaddingEnabled(bool enable); + bool getIsCanFd(void) const; + void setIsCanFd(bool enable); + bool getIsBrsEnabled(void) const; + void setIsBrsEnabled(bool enable); + +private: + // The legacy wire format was the raw memory of the (now-removed) J2534_RxFlowControlRequest + // struct copied into mArgumentData starting at offset 1 (overwriting the header pad byte). + // The struct was compiled with `#pragma pack(2)` on MSVC little-endian. All multi-byte + // scalars are therefore stored little-endian; the "flt" region reproduces the exact + // byte pattern produced by the old MessageBitsCAN bitfield writes. + // + // Byte layout (mArgumentData indices): + // [0] J2534 command header byte (set by base class) + // [1] padding + // [2..3] idx (u16 LE) + // [4..5] iCoreMiniID (u16 LE) + // [6] padding (u8) + // [7] structure pad byte (unused) + // [8..11] id (u32 LE) + // [12..15] id_mask (u32 LE) + // [16..19] fc_id (u32 LE) + // [20] flowControlExtendedAddress (u8) + // [21] extendedAddress (u8) + // [22] blockSize (u8) + // [23] stMin (u8) + // [24..25] cf_timeout (u16 LE) + // [26..53] MessageFilterBytes flt (28 bytes) -- see RebuildFilterBytes for details + // [54..57] flags (u32 LE) -- see kFcFlag* below + // + // Total wire size: 58 bytes. + static const size_t MessageSize = 58; + static const size_t IdxOffset = 2; + static const size_t CoreMiniOffset = 4; + static const size_t PaddingOffset = 6; + static const size_t IdOffset = 8; + static const size_t IdMaskOffset = 12; + static const size_t FcIdOffset = 16; + static const size_t FcExtAddrOffset = 20; + static const size_t ExtAddrOffset = 21; + static const size_t BlockSizeOffset = 22; + static const size_t StMinOffset = 23; + static const size_t CfTimeoutOffset = 24; + static const size_t FltOffset = 26; // 28 bytes + static const size_t FltSize = 28; + static const size_t FlagsOffset = 54; + + // Bit positions inside the u32 LE flags word (matches the legacy bitfield layout). + static const uint32_t FlagEnable = 1u << 0; + static const uint32_t FlagId29BitEnable = 1u << 1; + static const uint32_t FlagFcId29BitEnable = 1u << 2; + static const uint32_t FlagExtAddressEnable = 1u << 3; + static const uint32_t FlagFcExtAddressEnable = 1u << 4; + static const uint32_t FlagEnableFlowControlTransmit = 1u << 5; + static const uint32_t FlagPaddingEnable = 1u << 6; + static const uint32_t FlagIsCanFd = 1u << 7; + static const uint32_t FlagIsBrsEnabled = 1u << 8; + + // Rebuilds the filter bytes region of mArgumentData based on the current id / id_mask / + // id_29_bit_enable / ext_address_enable / extendedAddress state. Preserves the legacy + // (pre-existing) wire format bit-for-bit. + void rebuildFilterBytes(void); + + void setFlagBit(uint32_t mask, bool enable); + + bool getFlagBit(uint32_t mask) const; +}; + +class Iso15765TxSetupMessage : public J2534CommandMessage { +public: + Iso15765TxSetupMessage() + : J2534CommandMessage(J2534Command::SetupIso15765TxMessageProperties) { + argumentData.resize(36); + } + + uint16_t getIdx(void) const; + void setIdx(uint16_t idx); + uint16_t getCoreMiniId(void) const; + void setCoreMiniId(uint16_t coreMiniId); + uint32_t getMessageLength(void) const; + void setMessageLength(uint32_t messageLength); + uint8_t getPadding(void) const; + void setPadding(uint8_t padding); + uint8_t getTxDl(void) const; + void setTxDl(uint8_t txDl); + uint32_t getId(void) const; + void setId(uint32_t id); + uint32_t getFcId(void) const; + void setFcId(uint32_t fcId); + uint32_t getFcIdMask(void) const; + void setFcIdMask(uint32_t fcIdMask); + uint8_t getFlowControlExtendedAddress(void) const; + void setFlowControlExtendedAddress(uint8_t addr); + uint8_t getExtendedAddress(void) const; + void setExtendedAddress(uint8_t addr); + uint16_t getFsTimeout(void) const; + void setFsTimeout(uint16_t timeout); + uint16_t getFsWait(void) const; + void setFsWait(uint16_t wait); + uint32_t getFlags(void) const; + void setFlags(uint32_t flags); + uint8_t getStMin(void) const; + void setStMin(uint8_t stMin); + uint8_t getBlockSize(void) const; + void setBlockSize(uint8_t blockSize); + + bool getIs29BitEnabled(void) const; + void setIs29BitEnabled(bool enable); + bool getIsFc29BitEnabled(void) const; + void setIsFc29BitEnabled(bool enable); + bool getExtAddressEnabled(void) const; + void setExtAddressEnabled(bool enable); + bool getFcExtAddressEnabled(void) const; + void setFcExtAddressEnabled(bool enable); + bool getOverrideStMin(void) const; + void setOverrideStMin(bool enable); + bool getOverrideBlockSize(void) const; + void setOverrideBlockSize(bool enable); + bool getPaddingEnabled(void) const; + void setPaddingEnabled(bool enable); + bool getIsCanFd(void) const; + void setIsCanFd(bool enable); + bool getIsBrsEnabled(void) const; + void setIsBrsEnabled(bool enable); + +private: + // Matches J2534_Iso15TxRequest (#pragma pack(2), little-endian) copied after the + // 2-byte J2534 command header, as sent by vspy3 J2534_Transaction. + // [0..1] J2534 command header (managed by base class) + // [2..3] idx (u16 LE) + // [4..5] iCoreMiniID (u16 LE) + // [6..9] messageLength (u32 LE) + // [10] padding + // [11] tx_dl + // [12..15] id (u32 LE) + // [16..19] fc_id (u32 LE) + // [20..23] fc_id_mask (u32 LE) + // [24] flowControlExtendedAddress + // [25] extendedAddress + // [26..27] fs_timeout (u16 LE) + // [28..29] fs_wait (u16 LE) + // [30..33] flags (u32 LE) + // [34] stMin + // [35] blockSize + + static const size_t SetupIdxOffset = 2; + static const size_t SetupCoreMiniOffset = 4; + static const size_t SetupMsgLenOffset = 6; + static const size_t SetupPaddingOffset = 10; + static const size_t SetupTxDlOffset = 11; + static const size_t SetupIdOffset = 12; + static const size_t SetupFcIdOffset = 16; + static const size_t SetupFcIdMaskOffset = 20; + static const size_t SetupFcExtAddrOffset = 24; + static const size_t SetupExtAddrOffset = 25; + static const size_t SetupFsTimeoutOffset = 26; + static const size_t SetupFsWaitOffset = 28; + static const size_t SetupFlagsOffset = 30; + static const size_t SetupStMinOffset = 34; + static const size_t SetupBlockSizeOffset = 35; + + // Flag bit positions within the 32-bit flags word. + static const uint32_t FlagId29BitEnable = 1u << 0; + static const uint32_t FlagFcId29BitEnable = 1u << 1; + static const uint32_t FlagExtAddressEnable = 1u << 2; + static const uint32_t FlagFcExtAddressEnable = 1u << 3; + static const uint32_t FlagOverrideStMin = 1u << 4; + static const uint32_t FlagOverrideBlockSize = 1u << 5; + static const uint32_t FlagPaddingEnable = 1u << 6; + static const uint32_t FlagIsCanFd = 1u << 7; + static const uint32_t FlagIsBrsEnabled = 1u << 8; + + bool getFlagBit(uint32_t mask) const; + void setFlagBit(uint32_t mask, bool enable); +}; + +class Iso15765TxDataMessage : public J2534CommandMessage { +public: + static constexpr uint16_t MaxDataLength = 500; + + Iso15765TxDataMessage() + : J2534CommandMessage(J2534Command::SetupIso15765TxMessageDataBytes) { + argumentData.resize(10); + } + + uint16_t getIdx(void) const; + void setIdx(uint16_t idx); + uint32_t getOffset(void) const; + void setOffset(uint32_t offset); + uint16_t getLen(void) const; + const uint8_t* getData(void) const; + void setData(const uint8_t* sourceData, uint16_t len); + +private: + // Matches J2534_Iso15TxDataRequest (#pragma pack(2), little-endian) after the + // 2-byte J2534 command header. + // [0..1] J2534 command header (managed by base class) + // [2..3] idx (u16 LE) + // [4..7] offset (u32 LE) + // [8..9] len (u16 LE) + // [10..] data + static const size_t DataIdxOffset = 2; + static const size_t DataOffsetOffset = 4; + static const size_t DataLenOffset = 8; + static const size_t DataPayloadOffset = 10; +}; + +class Iso15765MessageArgs { +public: + struct ArbId { + bool Is29Bit; + uint32_t Id; + + ArbId(uint32_t id = 0, bool is29Bit = false) + : Is29Bit(is29Bit) + , Id(id) { + } + }; + + Iso15765MessageArgs(void) + : networkId(0) + , txIndex(0) + , arbId({ false, 0 }) + , flowControlArbId({ false, 0 }) + , flowControlArbIdMask(0) + , isCanFd(false) + , isBrsEnabled(false) + , tx_DL(8) + , fs_Timeout(0) + , fs_WaitTimeout(0) + , cf_Timeout(0) + , isFlowControlEnabled(false) { + } + + uint16_t getNetworkId(void) const { return networkId; } + void setNetworkId(uint16_t netId) { networkId = netId; } + uint8_t getTxIndex(void) const { return txIndex; } + void setTxIndex(uint8_t idx) { txIndex = idx; } + + const ArbId& getArbId(void) const { return arbId; } + void setArbId(const ArbId& id) { arbId = id; } + const ArbId& getFlowControlArbId(void) const { return flowControlArbId; } + void setFlowControlArbId(const ArbId& id) { flowControlArbId = id; } + uint32_t getFlowControlArbIdMask(void) const { return flowControlArbIdMask; } + void setFlowControlArbIdMask(uint32_t mask) { flowControlArbIdMask = mask; } + bool getIsCanFd(void) const { return isCanFd; } + void setIsCanFd(bool value) { isCanFd = value; } + bool getIsBrsEnabled(void) const { return isBrsEnabled; } + void setIsBrsEnabled(bool value) { isBrsEnabled = value; } + + uint8_t getTxDl(void) const { return tx_DL; } + void setTxDl(uint8_t txDl) { tx_DL = txDl; } + std::optional getStMin(void) const { return stMin; } + void setStMin(std::optional v) { stMin = v; } + std::optional getBlockSize(void) const { return blockSize; } + void setBlockSize(std::optional b) { blockSize = b; } + std::optional getFlowControlExtendedAddress(void) const { return flowControlExtendedAddress; } + void setFlowControlExtendedAddress(std::optional fcExtAddr) { flowControlExtendedAddress = fcExtAddr; } + std::optional getExtendedAddress(void) const { return extendedAddress; } + void setExtendedAddress(std::optional extAddr) { extendedAddress = extAddr; } + std::optional getPaddingValue(void) const { return paddingValue; } + void setPaddingValue(std::optional v) { paddingValue = v; } + + uint16_t getFsTimeout(void) const { return fs_Timeout; } + void setFsTimeout(uint16_t timeout) { fs_Timeout = timeout; } + uint16_t getFsWaitTimeout(void) const { return fs_WaitTimeout; } + void setFsWaitTimeout(uint16_t timeout) { fs_WaitTimeout = timeout; } + uint16_t getCfTimeout(void) const { return cf_Timeout; } + void setCfTimeout(uint16_t timeout) { cf_Timeout = timeout; } + + const std::vector& getData(void) const { return data; } + void setData(std::vector&& d) { data = std::move(d); } + + bool getIsFlowControlEnabled(void) const { return isFlowControlEnabled; } + void setIsFlowControlEnabled(bool enabled) { isFlowControlEnabled = enabled; } + +protected: + + uint16_t networkId; // The netid of the message (determines which network to transmit on) + uint8_t txIndex; // identifier for this transmit message + + ArbId arbId; // arbId of transmitted frames (CAN id to transmit to) + ArbId flowControlArbId; // flow control arb id filter value (response id from receiver) + uint32_t flowControlArbIdMask; // The flow control arb id filter mask (response id from receiver) + bool isCanFd; + bool isBrsEnabled; + + uint8_t tx_DL; // Maximum CAN(FD) protocol length for transmitted frames (Valid values are 8, 12, 16, 20, 24, 32, 48, 64) + std::optional stMin; // Overrides the stMin that the receiver reports (Set to J2534's STMIN_TX if <= 0xFF) + std::optional blockSize; // Overrides the block size that the receiver reports (Set to J2534's BS_TX if <= 0xFF) + std::optional flowControlExtendedAddress; // Expected Extended Address byte of response from receiver + std::optional extendedAddress; // Extended Address byte of transmitter + std::optional paddingValue; // The padding byte to use to fill the unused portion of transmitted CAN frames (single frame, first frame, consecutive frame) + + uint16_t fs_Timeout; // max timeout (ms) for waiting on flow control response (Set this to N_BS_MAX's value if J2534) + uint16_t fs_WaitTimeout; // max timeout (ms) for waiting on flow control response after receiving flow control with flow status set to WAIT (Set this to N_BS_MAX's value if J2534) + uint16_t cf_Timeout; // max timeout (ms) for waiting on consecutive frame (Set this to N_CR_MAX's value in J2534) + + std::vector data; + + bool isFlowControlEnabled; // Enables Flow Control frame transmission (Rx message; from neoVI to ECU) +}; + + +} + +#endif // __cplusplus + +#endif \ No newline at end of file diff --git a/include/icsneo/device/device.h b/include/icsneo/device/device.h index cf0fa40..fadafef 100644 --- a/include/icsneo/device/device.h +++ b/include/icsneo/device/device.h @@ -64,6 +64,8 @@ #include "icsneo/communication/message/networkmutexmessage.h" #include "icsneo/communication/message/allmacaddressesmessage.h" #include "icsneo/communication/message/mfgconfigmessage.h" +#include "icsneo/communication/message/iso15765message.h" +#include "icsneo/J2534.h" #define ICSNEO_FINDABLE_DEVICE_BASE(className, type) \ static constexpr DeviceType::Enum DEVICE_TYPE = type; \ @@ -909,6 +911,11 @@ public: void stopHeartbeat(); void restartHeartbeat(); + bool iso15765Enable(const Network& network); + bool iso15765DisableAll(void); + bool iso15765TransmitMessage(const Network& network, const Iso15765MessageArgs& msg, const std::chrono::milliseconds& timeout); + bool iso15765SetupRxFlowControl(const Network& network, const Iso15765MessageArgs& msg); + protected: bool online = false; int messagePollingCallbackID = 0; @@ -1177,6 +1184,23 @@ private: bool enableNetworkCommunication(bool enable, uint32_t timeout = 0); + bool J2534_Transaction(const Network& network, J2534CommandMessage&& msg, const std::chrono::milliseconds& timeout); + bool J2534_ClearRxFilters(const Network& network); + bool J2534_SetupCanRxFilter(const Network& network, const J2534_RxCanFilter& rxCanFilter); + bool J2534_EnableFiltering(const Network& network, const bool enable); + + bool J2534_EnableIso15765(const Network& network, const bool enable); + bool J2534_ClearRxFilter(const Network& network, unsigned int iIndex); + bool J2534_EnableFirmwareUsbPassFilters(const Network& network, const bool enable); + + struct NetworkHash { + size_t operator()(const Network& x) const { + return (size_t)x.getNetID(); + } + }; + std::unordered_map iso15765FirmwareEnabled; + + // Keeponline (keepalive for online) std::unique_ptr keeponline; std::unique_ptr heartbeat;