diff --git a/drivers/windows/.gitignore b/drivers/windows/.gitignore new file mode 100644 index 0000000..dbe7ad5 --- /dev/null +++ b/drivers/windows/.gitignore @@ -0,0 +1,306 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +Debug_x86/ +Debug_x64/ +Release_x86/ +Release_x64/ +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ +**/Properties/launchSettings.json + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Typescript v1 declaration files +typings/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# CodeRush +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# installer +*.exe diff --git a/drivers/windows/ECUsim CLI/ECUsim CLI.cpp b/drivers/windows/ECUsim CLI/ECUsim CLI.cpp new file mode 100644 index 0000000..6c8a469 --- /dev/null +++ b/drivers/windows/ECUsim CLI/ECUsim CLI.cpp @@ -0,0 +1,38 @@ +// ECUsim CLI.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include "ECUsim DLL\ECUsim.h" + +std::unique_ptr sim; + +BOOL CtrlHandler(DWORD fdwCtrlType) +{ + if (fdwCtrlType != CTRL_C_EVENT) return FALSE; + + sim->stop(); + sim->join(); + + return(TRUE); +} + +int main(int argc, // Number of strings in array argv + char *argv[], // Array of command-line argument strings + char *envp[]) // Array of environment variable strings +{ + + int count; + + // Display each command-line argument. + std::cout << "\nCommand-line arguments:\n"; + for (count = 0; count < argc; count++) + std::cout << " argv[" << count << "] " << argv[count] << "\n"; + + SetConsoleCtrlHandler((PHANDLER_ROUTINE)CtrlHandler, TRUE); + + sim.reset(new ECUsim("", 500000)); + sim->join(); + + return 0; +} + diff --git a/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj new file mode 100644 index 0000000..9ac10e8 --- /dev/null +++ b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj @@ -0,0 +1,178 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {D99E2FCD-21A4-4065-949A-31E34E0E69D1} + Win32Proj + ECUsimCLI + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + Use + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)ecusim.lib + + + + + + + + + + + Create + Create + Create + Create + + + + + {96e0e646-ee76-444d-9a77-a0cd7f781deb} + + + + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters new file mode 100644 index 0000000..ea223e3 --- /dev/null +++ b/drivers/windows/ECUsim CLI/ECUsim CLI.vcxproj.filters @@ -0,0 +1,36 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim CLI/stdafx.cpp b/drivers/windows/ECUsim CLI/stdafx.cpp new file mode 100644 index 0000000..d4a23c3 --- /dev/null +++ b/drivers/windows/ECUsim CLI/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ECUsim CLI.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/ECUsim CLI/stdafx.h b/drivers/windows/ECUsim CLI/stdafx.h new file mode 100644 index 0000000..b005a83 --- /dev/null +++ b/drivers/windows/ECUsim CLI/stdafx.h @@ -0,0 +1,15 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#include +#include + + + +// TODO: reference additional headers your program requires here diff --git a/drivers/windows/ECUsim CLI/targetver.h b/drivers/windows/ECUsim CLI/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/drivers/windows/ECUsim CLI/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj new file mode 100644 index 0000000..d50a0a2 --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj @@ -0,0 +1,197 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {96E0E646-EE76-444D-9A77-A0CD7F781DEB} + Win32Proj + ECUsimDLL + 8.1 + + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + ecusim + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Use + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;ECUSIMDLL_EXPORTS;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + + + + + + + false + + + false + + + false + + + false + + + + + + Create + Create + Create + Create + + + + + {5528aefb-638d-49af-b9d4-965154e7d531} + + + + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters new file mode 100644 index 0000000..299d654 --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim DLL.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/ECUsim DLL/ECUsim.cpp b/drivers/windows/ECUsim DLL/ECUsim.cpp new file mode 100644 index 0000000..19f2bf3 --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim.cpp @@ -0,0 +1,261 @@ +#include "stdafx.h" +#include "ECUsim.h" + +ECUsim::ECUsim(std::string sn, unsigned long can_baud, bool ext_addr) : + doloop(TRUE), verbose(TRUE), can11b_enabled(TRUE), can29b_enabled(TRUE), ext_addr(ext_addr){ + this->panda = panda::Panda::openPanda(sn); + this->panda->set_can_speed_cbps(panda::PANDA_CAN1, can_baud / 100); //Don't pass in baud where baud%100 != 0 + this->panda->set_safety_mode(panda::SAFETY_ALLOUTPUT); + this->panda->set_can_loopback(FALSE); + this->panda->can_clear(panda::PANDA_CAN_RX); + + DWORD threadid; + this->thread_can = CreateThread(NULL, 0, _canthreadBootstrap, (LPVOID)this, 0, &threadid); +} + +ECUsim::~ECUsim() { + this->stop(); + this->join(); +} + +void ECUsim::stop() { + this->doloop = FALSE; +} + +void ECUsim::join() { + WaitForSingleObject(this->thread_can, INFINITE); +} + +DWORD WINAPI ECUsim::_canthreadBootstrap(LPVOID This) { + return ((ECUsim*)This)->can_recv_thread_function(); +} + +DWORD ECUsim::can_recv_thread_function() { + while (this->doloop) { + auto msgs = this->panda->can_recv(); + for (auto& msg : msgs) { + if (msg.is_receipt) continue; + if (msg.bus == 0 && !msg.is_receipt /*&& msg.len == 8*/ && msg.dat[0] >= 2) { + if (this->verbose) { + printf("Processing message (bus: %d; addr: %X; 29b: %d):\n ", msg.bus, msg.addr, msg.addr_29b); + for (int i = 0; i < msg.len; i++) printf("%02X ", msg.dat[i]); + printf("\n"); + } + this->_CAN_process_msg(msg); + } else { + if (this->verbose) { + printf("Rejecting message (bus: %d; addr: %X; 29b: %d):\n ", msg.bus, msg.addr, msg.addr_29b); + for (int i = 0; i < msg.len; i++) printf("%02X ", msg.dat[i]); + printf("\n"); + } + } + } + } + + return 0; +} + +BOOL ECUsim::_can_addr_matches(panda::PANDA_CAN_MSG& msg) { + if (this->can11b_enabled && !msg.addr_29b && (msg.addr == 0x7DF || (msg.addr & 0x7F8) == 0x7E0)) { + if (!this->ext_addr) { + return TRUE; + } else { + return msg.len >= 1 && msg.dat[0] == 0x13;//13 is an arbitrary address picked to test ext addresses + } + } + if (this->can29b_enabled && msg.addr_29b && ((msg.addr & 0x1FFF00FF) == 0x18DB00F1 || (msg.addr & 0x1FFF00FF) == 0x18da00f1)) { + if (!this->ext_addr) { + return TRUE; + } else { + return msg.len >= 1 && msg.dat[0] == 0x13;//13 is an arbitrary address picked to test ext addresses + } + } + return FALSE; +} + +void ECUsim::_CAN_process_msg(panda::PANDA_CAN_MSG& msg) { + std::string outmsg; + uint32_t outaddr; + uint8_t formatted_msg_buff[8]; + bool doreply = FALSE; + + if (this->_can_addr_matches(msg)) {// && msg.len == 8) { + uint8_t *dat = (this->ext_addr) ? &msg.dat[1] : &msg.dat[0]; + if ((dat[0] & 0xF0) == 0x10) { + printf("Got a multiframe write request\n"); + outaddr = (msg.addr_29b) ? 0x18DAF1EF : 0x7E8; + this->panda->can_send(outaddr, msg.addr_29b, (const uint8_t*)"\x30\x00\x00", 3, panda::PANDA_CAN1); + return; + } + + /////////// Check if Flow Control Msg + if ((dat[0] & 0xF0) == 0x30 && msg.len >= 3 && this->can_multipart_data.size() > 0) { + if (this->verbose) printf("More data requested\n"); + uint8_t block_size = dat[1], sep_time_min = dat[2]; + outaddr = (msg.addr == 0x7DF || msg.addr == 0x7E0) ? 0x7E8 : 0x18DAF1EF; //ext addr 5th byte is just always 0x13 for simplicity + + unsigned int msgnum = 1; + while (this->can_multipart_data.size()) { + unsigned int datalen = this->ext_addr ? + min(6, this->can_multipart_data.size()): //EXT ADDR VALUE + min(7, this->can_multipart_data.size()); //NORMAL ADDR VALUE + + unsigned int idx = 0; + if (this->ext_addr) + formatted_msg_buff[idx++] = 0x13; //EXT ADDR + formatted_msg_buff[idx++] = 0x20 | msgnum; + for (int i = 0; i < datalen; i++) { + formatted_msg_buff[i + idx] = this->can_multipart_data.front(); + this->can_multipart_data.pop(); + } + for (int i = datalen + idx; i < sizeof(formatted_msg_buff); i++) + formatted_msg_buff[i] = 0; + + if (this->verbose) { + printf("Multipart reply to %X.\n ", outaddr); + for (int i = 0; i < datalen + idx; i++) printf("%02X ", formatted_msg_buff[i]); + printf("\n"); + } + + this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, datalen + idx, panda::PANDA_CAN1); + msgnum = (msgnum + 1) % 0x10; + Sleep(10); + } + return; + } + + /////////// Normal message in + outmsg = this->process_obd_msg(dat[1], dat[2], doreply); + if (doreply) { + outaddr = (msg.addr_29b) ? 0x18DAF1EF : 0x7E8; + + if (outmsg.size() <= (this->ext_addr ? 4 : 5)) { + unsigned int idx = 0; + if(this->ext_addr) + formatted_msg_buff[idx++] = 0x13; //EXT ADDR + formatted_msg_buff[idx++] = outmsg.size() + 2; + formatted_msg_buff[idx++] = 0x40 | dat[1]; + formatted_msg_buff[idx++] = dat[2]; //PID + memcpy_s(&formatted_msg_buff[idx], sizeof(formatted_msg_buff) - idx, outmsg.c_str(), outmsg.size()); + for (int i = idx + outmsg.size(); i < 8; i++) + formatted_msg_buff[i] = 0; + + if (this->verbose) { + printf("Replying to %X.\n ", outaddr); + for (int i = 0; i < 8; i++) printf("%02X ", formatted_msg_buff[i]); + printf("\n"); + } + + this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, 8, panda::PANDA_CAN1); //outmsg.size() + 3 + } else { + uint8_t first_msg_len = this->ext_addr ? + min(2, outmsg.size() % 7) : //EXT ADDR VALUES + min(3, outmsg.size() % 7); //NORMAL ADDR VALUES + uint8_t payload_len = outmsg.size() + 3; + + unsigned int idx = 0; + if (this->ext_addr) + formatted_msg_buff[idx++] = 0x13; //EXT ADDR + formatted_msg_buff[idx++] = 0x10 | ((payload_len >> 8) & 0xF); + formatted_msg_buff[idx++] = payload_len & 0xFF; + formatted_msg_buff[idx++] = 0x40 | dat[1]; + formatted_msg_buff[idx++] = dat[2]; //PID + formatted_msg_buff[idx++] = 1; + memcpy_s(&formatted_msg_buff[idx], sizeof(formatted_msg_buff) - idx, outmsg.c_str(), first_msg_len); + + if (this->verbose) { + printf("Replying FIRST FRAME to %X.\n ", outaddr); + for (int i = 0; i < 8; i++) printf("%02X ", formatted_msg_buff[i]); + printf("\n"); + } + + this->panda->can_send(outaddr, msg.addr_29b, formatted_msg_buff, 8, panda::PANDA_CAN1); + for (int i = first_msg_len; i < outmsg.size(); i++) + this->can_multipart_data.push(outmsg[i]); + } + } + } +} + +std::string ECUsim::process_obd_msg(UCHAR mode, UCHAR pid, bool& return_data) { + std::string tmp; + return_data = TRUE; + + switch (mode) { + case 0x01: // Mode : Show current data + switch (pid) { + case 0x00: //List supported things + return "\xff\xff\xff\xfe"; //b"\xBE\x1F\xB8\x10" #Bitfield, random features + case 0x01: // Monitor Status since DTC cleared + return std::string("\x00\x00\x00\x00", 4); //Bitfield, random features + case 0x04: // Calculated engine load + return "\x2f"; + case 0x05: // Engine coolant temperature + return "\x3c"; + case 0x0B: // Intake manifold absolute pressure + return "\x90"; + case 0x0C: // Engine RPM + return "\x1A\xF8"; + case 0x0D: // Vehicle Speed + return "\x53"; + case 0x10: // MAF air flow rate + return "\x01\xA0"; + case 0x11: // Throttle Position + return "\x90"; + case 0x33: // Absolute Barometric Pressure + return "\x90"; + default: + return_data = FALSE; + return ""; + } + case 0x09: // Mode : Request vehicle information + switch (pid) { + case 0x02: // Show VIN + return "1D4GP00R55B123456"; + case 0xFC: // test long multi message.Ligned up for LIN responses + for (int i = 0; i < 80; i++) { + tmp += "\xAA\xAA"; + } + return tmp;//">BBH", 0xAA, 0xAA, num + 1) + case 0xFD: // test long multi message + for (int i = 0; i < 80; i++) { + tmp += "\xAA\xAA\xAA"; + tmp.push_back(i >> 24); + tmp.push_back((i >> 16) & 0xFF); + tmp.push_back((i >> 8) & 0xFF); + tmp.push_back(i & 0xFF); + } + return "\xAA\xAA\xAA" + tmp; + case 0xFE: // test very long multi message + tmp = "\xAA\xAA\xAA"; + for (int i = 0; i < 584; i++) { + tmp += "\xAA\xAA\xAA"; + tmp.push_back(i >> 24); + tmp.push_back((i >> 16) & 0xFF); + tmp.push_back((i >> 8) & 0xFF); + tmp.push_back(i & 0xFF); + } + return tmp + "\xAA"; + case 0xFF: + for (int i = 0; i < 584; i++) { + tmp += "\xAA\xAA\xAA\xAA\xAA"; + tmp.push_back(((i + 1) >> 8) & 0xFF); + tmp.push_back((i + 1) & 0xFF); + } + return std::string("\xAA\x00\x00", 3) + tmp; + default: + return_data = FALSE; + return ""; + } + case 0x3E: + if (pid == 0) { + return_data = TRUE; + return ""; + } + return_data = FALSE; + return ""; + default: + return_data = FALSE; + return ""; + } +} diff --git a/drivers/windows/ECUsim DLL/ECUsim.h b/drivers/windows/ECUsim DLL/ECUsim.h new file mode 100644 index 0000000..7378e2c --- /dev/null +++ b/drivers/windows/ECUsim DLL/ECUsim.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include "panda\panda.h" +#include + +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the ECUSIMDLL_EXPORTS +// symbol defined on the command line. This symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// ECUSIMDLL_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef ECUSIMDLL_EXPORTS +#define ECUSIMDLL_API __declspec(dllexport) +#else +#define ECUSIMDLL_API __declspec(dllimport) +#endif + +// This class is exported from the ECUsim DLL.dll +class ECUSIMDLL_API ECUsim { +public: + ECUsim(std::string sn, unsigned long can_baud, bool ext_addr = FALSE); + ECUsim(panda::Panda && p, unsigned long can_baud, bool ext_addr = FALSE); + ~ECUsim(); + + void stop(); + void join(); + + // Flag determines if verbose output is enabled + volatile bool verbose; + BOOL ext_addr; +private: + std::unique_ptr panda; + + static DWORD WINAPI _canthreadBootstrap(LPVOID This); + DWORD can_recv_thread_function(); + + BOOL _can_addr_matches(panda::PANDA_CAN_MSG & msg); + + void _CAN_process_msg(panda::PANDA_CAN_MSG & msg); + + std::string process_obd_msg(UCHAR mode, UCHAR pid, bool& return_data); + + HANDLE thread_can; + volatile bool doloop; + std::queue can_multipart_data; + + BOOL can11b_enabled; + BOOL can29b_enabled; +}; diff --git a/drivers/windows/ECUsim DLL/dllmain.cpp b/drivers/windows/ECUsim DLL/dllmain.cpp new file mode 100644 index 0000000..69b5891 --- /dev/null +++ b/drivers/windows/ECUsim DLL/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/drivers/windows/ECUsim DLL/stdafx.cpp b/drivers/windows/ECUsim DLL/stdafx.cpp new file mode 100644 index 0000000..b4056ec --- /dev/null +++ b/drivers/windows/ECUsim DLL/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// ECUsim DLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/ECUsim DLL/stdafx.h b/drivers/windows/ECUsim DLL/stdafx.h new file mode 100644 index 0000000..f3a0737 --- /dev/null +++ b/drivers/windows/ECUsim DLL/stdafx.h @@ -0,0 +1,16 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + + + +// TODO: reference additional headers your program requires here diff --git a/drivers/windows/ECUsim DLL/targetver.h b/drivers/windows/ECUsim DLL/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/drivers/windows/ECUsim DLL/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/README.md b/drivers/windows/README.md new file mode 100644 index 0000000..7219abd --- /dev/null +++ b/drivers/windows/README.md @@ -0,0 +1,177 @@ +``` + ;" ^; ;' ", +______/\\\\\\\\\\\____/\\\\\\\\\_______/\\\\\\\\\\\\\\\______/\\\\\\\\\\_____________/\\\____ ; s$$$$$$$s ; + _____\/////\\\///___/\\\///////\\\____\/\\\///////////_____/\\\///////\\\__________/\\\\\____ , ss$$$$$$$$$$s ,' + _________\/\\\_____\///______\//\\\___\/\\\_______________\///______/\\\_________/\\\/\\\____ ;s$$$$$$$$$$$$$$$ + _________\/\\\_______________/\\\/____\/\\\\\\\\\\\\_____________/\\\//________/\\\/\/\\\____ $$$$$$$$$$$$$$$$$$ + _________\/\\\____________/\\\//______\////////////\\\__________\////\\\_____/\\\/__\/\\\____ $$$$P""Y$$$Y""W$$$$$ + _________\/\\\_________/\\\//____________________\//\\\____________\//\\\__/\\\\\\\\\\\\\\\\_ $$$$ p"$$$"q $$$$$ + __/\\\___\/\\\_______/\\\/____________/\\\________\/\\\___/\\\______/\\\__\///////////\\\//__ $$$$ .$$$$$. $$$$ + _\//\\\\\\\\\_______/\\\\\\\\\\\\\\\_\//\\\\\\\\\\\\\/___\///\\\\\\\\\/_____________\/\\\____ _ $$$$$$$$$$$$$$$$ + __\/////////_______\///////////////___\/////////////_______\/////////_______________\///_____| | "Y$$$"*"$$$Y" + _ __ __ _ _ __ __| | __ _"$b.$$" + | '_ \ / _` | '_ \ / _` |/ _` | + | |_) | (_| | | | | (_| | (_| | + | .__/ \__,_|_| |_|\__,_|\__,_| + | | A comma.ai product. + |_| (Code by Jessy Diamond Exum) +``` + + +# What is J2534? + +J2534 is an API that tries to provide a consistent way to send/receive +messages over the many different protocols supported by the OBD II +port. The place this is perhaps most obvious, is sending data over +different protocols (each using unique packetizing methods) using the +same data format. + +For each PassThru Device that should be used with J2534 (in this case, +the panda), a 'driver' has to be written that can be loaded by a +client application wanting to send/receive data. + +A lot of J2534 has good ideas behind it, but the standard has some odd choices: + +* Platform Locked: Requires using the Windows Registry to find installed J2534 libraries/drivers. Drivers have to be DLLs. +* Architecture Locked: So far there is only support for x86. +* No device autodetect, and poor support for selecting from multiple devices. +* Constant vague language about important behavior (small differences between vendors). +* Most common differences become standard in later revisions. + +# Why use J2534 with the panda? + +J2534 is the only interface supported by most professional grade +vehicle diagnostics systems (such as HDS). These tools are useful for +diagnosing vehicles, as well as reverse engineering some lesser known +features. + +# What parts are supported with panda? + +- [ ] **J1850VPW** *(Outdated, and not physically supported by the panda)* +- [ ] **J1850PWM** *(Outdated, and not physically supported by the panda)* +- [X] **CAN** +- [X] **ISO15765** +- [ ] **ISO9141** *(This protocol could be implemented if 5 BAUD init support is added to the panda.)* +- [ ] **ISO14230/KWP2000** *(Could be supported with FAST init, 5baud init if panda adds support for 5bps serial)* + +# Building the Project: + +This project was developed with Visual Studio 2015, the Windows SDK, +and the Windows Driver Kit (WDK). At the time of writing, there is not +a stable WDK for Visual Studio 2017, but this project should build +with the new WDK and Visual Studio when it is available. + +The WDK is only required for creating the signed WinUSB inf file. The +WDK may also provide the headers for WinUSB. + +To build all the projects required for the installer, in Visual +Studio, select **Build->Batch Build.** In the project list select: + +- **"panda"** *Release|x86* +- **"panda"** *Release|x64* +- **"panda Driver Package"** Debug|x86 (Note this inf file works with x86/amd64). +- **"pandaJ2534DLL"** *Release|x86* + +The installer is generated with [NullSoft NSIS](http://nsis.sourceforge.net/Main_Page). +Use NSIS to run panda_install.nsi after building all the required projects. + +Before generating the installer, you must go to copy vscruntimeinfo.nsh.sample to +vscruntimeinfo.nsh and follow the instructions to bundle in the Visual Studio C +Runtime required by your version of Visual Studio. Without this runtime, the panda +code will not work, so without this file, the installer will refuse to build. + +# Installing: + +Either build the software yourself by following the steps in the +'Developing' section, or get the panda_installer.exe file and run +it. The wizard should correctly set up the drivers. + +Since this driver is still in development, there are some issues +that may occur. If after you install the driver and then plug in your +panda (unplug it first if it was already plugged in), Windows says +the driver is missing, refer to the section below 'Dealing with self +signed drivers.' + +# Using J2534: + +After installing the J2534 drivers for the panda, you can do... nothing. +You first need to get a J2534 client that can load the drivers and talk to +the panda for you. + +A simple tool for testing J2534 drivers is DrewTech's 'J2534-1 Bus Analysis +Tool' available in the 'Other Support Applications' section of their +[Download Page](http://www.drewtech.com/downloads/). + +# Dealing with self signed drivers: + +Installation would be straightforward were it not for the USB Driver +that needs to be setup. The driver itself is only a WinUSB inf file +(no actual driver), but it still needs to be signed. + +Driver signing is a requirement of Windows starting in 8 (64 bit +versions only for some reason). If your Windows refuses to install +the driver, there are three choices: + +- Self Sign the Driver. +- Disable Driver Signature Verification +- Purchase a certificate signed by a trusted authority. + +Since self signed certificates have no chain of trust to a known +certificate authority, if you self sign, you will have to add your +cert to the root certificate store of your Windows' installation. This +is dangerous because it means anything signed with your cert will be +trusted. If you make your own cert, add a password so someone can't +copy it and screw with your computer's trust. + +Disabling Signature Verification allows you to temporarily install +drivers without a trusted signature. Once you reboot, new drivers will +need to be verified again, but any installed drivers will stay where +they are. This option is irritating if you are installing and +uninstalling the inf driver multiple times, but overall, is safer than +the custom root certificate described above. + +Purchasing a signed certificate is the best long term option, but it +is not useful for open source contributors, since the certificate will +be kept safe by the comma.ai team. Developers should use one of the +other two options. + +**Note that certificate issues apply no matter if you are building + from source or running an insaller .exe file.** + +Some people have reported that the driver installs without needing to +disable driver signing, or that visual studio correctly sets up a +temporary signing key for them. I call witchcraft because I have not +had that happen to me. The signed certificate is still the correct +thing to do in the end. + +Windows 7 will not force driver signing. This software is not tested +on anything before 7. + +# Developing: + +- Edit and merge pandaJ2534DLL\J2534register_x64.reg to register your development J2534 DLL. +- Add your output directory (panda\drivers\windows\Debug_x86) to your system PATH to avoid insanity. + +# ToDo Items: + +- Get official signing key for WinUSB driver inf file. +- Implement TxClear and RxClear. (Requires reading vague specifications). +- Apply a style-guide and consistent naming convention for Classes/Functions/Variables. +- Send multiple messages (each with a different address) from a given connection at the same time. +- Implement ISO14230/KWP2000 FAST (LIN communication is already supported with the raw panda USB driver). +- Find more documentation about SW_CAN_PS (Single Wire CAN, aka GMLAN). +- Find example of client using a _PS version of a protocol (PS is pin select, and may support using different CAN buses). + + +# Known Issues: + +- ISO15765 Multi-frame TX: Hardware delays make transmission overshoot + STMIN by several milliseconds. This does not violate the requirements + of STMIN, it just means it is a little slower than it could be. + +- All Tx messages from a single Connection are serialized. This can be + relaxed to allow serialization of messages based on their address + (making multiple queues, effectively one queue per address). + +# Other: +Panda head ASCII art by dcau \ No newline at end of file diff --git a/drivers/windows/docs/Message_Size.png b/drivers/windows/docs/Message_Size.png new file mode 100644 index 0000000..3a20a2e Binary files /dev/null and b/drivers/windows/docs/Message_Size.png differ diff --git a/drivers/windows/docs/RxBits_defs.jpg b/drivers/windows/docs/RxBits_defs.jpg new file mode 100644 index 0000000..db01f62 Binary files /dev/null and b/drivers/windows/docs/RxBits_defs.jpg differ diff --git a/drivers/windows/docs/RxBits_valid.png b/drivers/windows/docs/RxBits_valid.png new file mode 100644 index 0000000..6f7c4c3 Binary files /dev/null and b/drivers/windows/docs/RxBits_valid.png differ diff --git a/drivers/windows/docs/bus_init_signla.png b/drivers/windows/docs/bus_init_signla.png new file mode 100644 index 0000000..9aa71fa Binary files /dev/null and b/drivers/windows/docs/bus_init_signla.png differ diff --git a/drivers/windows/docs/connection_flags.png b/drivers/windows/docs/connection_flags.png new file mode 100644 index 0000000..dd578a2 Binary files /dev/null and b/drivers/windows/docs/connection_flags.png differ diff --git a/drivers/windows/docs/iso15765_ioctls.png b/drivers/windows/docs/iso15765_ioctls.png new file mode 100644 index 0000000..7de4a11 Binary files /dev/null and b/drivers/windows/docs/iso15765_ioctls.png differ diff --git a/drivers/windows/docs/message_send.png b/drivers/windows/docs/message_send.png new file mode 100644 index 0000000..112aab1 Binary files /dev/null and b/drivers/windows/docs/message_send.png differ diff --git a/drivers/windows/docs/msg_filter_passfail.png b/drivers/windows/docs/msg_filter_passfail.png new file mode 100644 index 0000000..4a91fac Binary files /dev/null and b/drivers/windows/docs/msg_filter_passfail.png differ diff --git a/drivers/windows/docs/other notes.txt b/drivers/windows/docs/other notes.txt new file mode 100644 index 0000000..a066d26 --- /dev/null +++ b/drivers/windows/docs/other notes.txt @@ -0,0 +1,347 @@ +When using the ISO 15765-4 protocol, only SingleFrame messages can be transmitted without a matching +flow control filter. Also, PCI bytes are transparently added by the API. See PassThruStartMsgFilter +and Appendix A for a discussion of flow control filters. + + + +PassThruReadMsgs +This function reads messages and indications from the receive buffer. All messages and indications shall +be read in the order that they occurred on the bus. If a transmit message generated a loopback message +and TxDone indication, the TxDone indication shall always be queued first. Except for loopback messages +and indications, no messages shall be queued for reception without matching a PASS_FILTER +(for non-ISO 15765) or FLOW_CONTROL filter (for ISO 15765). On ISO 15765, PCI bytes are transparently +removed by the API. If the function is successful, a value of STATUS_NOERROR is returned. + + +PassThruWriteMsgs +Write timeout (in milliseconds). When a value of 0 is specified, the function queues as +many of the specified messages as possible and returns immediately. When a value +greater than 0 is specified, the function will block until the Timeout has expired, an error +has occurred, or the desired number of messages have been transmitted on the vehicle +network. Even if the device can buffer only one packet at a time, this function shall be +able to send an arbitrary number of packets if a Timeout value is supplied. Since the +function returns early if all the messages have been sent, there is normally no penalty for +having a large timeout (several seconds). If the number of messages requested have +been written, the function shall not return ERR_TIMEOUT, even if the timeout value is +zero. + +When an ERR_TIMEOUT is returned, only the number of messages that were sent on +the vehicle network is known. The number of messages queued is unknown. Application +writers should avoid this ambiguity by using a Timeout value large enough to work on +slow devices and networks with arbitration delays. + + + +PassThruStartPeriodicMsg +This function will immediately queue the specified message for transmission, and repeat at the specified +interval. Periodic messages are limited in length to a single frame message of 12 bytes or less, including +header or CAN ID. Periodic messages shall have priority over messages queued with +PassThruWriteMsgs, but periodic messages must not violate bus idle timing parameters (e.g. P3_MIN). +Periodic messages shall generate TxDone indications (ISO 15765) and loopback messages (on any +protocol, if enabled). On ISO 15765, periodic messages can be sent during a multi-frame transmission or +reception. If the function is successful, a value of STATUS_NOERROR is returned. The Pass-Thru +device must support a minimum of ten periodic messages. + +PassThruDisconnect shall delete all periodic messages on that channel. PassThruClose shall delete all +periodic messages on all channels for the device. All periodic messages will be stopped on a +PassThruDisconnect for the associated protocol or a PassThruClose for the device. + + + +PASSTHRUSTARTMSGFILTER +This function starts filtering of incoming messages. If the function is successful, a value of +STATUS_NOERROR is returned. A minimum of ten message filters shall be supported by the interface +for each supported protocol. PassThruDisconnect shall delete all message filters on that channel. + +PassThruClose shall delete all filters on all channels for the device. Pattern and Mask messages shall +follow the protocol formats specified in Section 8. However, only the first twelve (12) bytes, including +header or CAN ID, are used by the filter. ERR_INVALID_MSG shall be returned if the filter length +exceeds 12. Note that this function does not clear any messages that may have been received and +queued before the filter was set. Users are cautioned to consider performing a CLEAR_RX_BUFFER +after starting a message filter to be sure that unwanted frames are purged from any receive buffers. + + + + + + + + +FILTER RELATED STUFF +For all protocols except ISO 15765: +• PASS_FILTERs and BLOCK_FILTERs will be applied to all received messages. They shall not be +applied to indications or loopback messages + +• FLOW_CONTROL_FILTERs must not be used and shall cause the interface to return +ERR_INVALID_FILTER_ID + +• Both pMaskMsg and pPatternMsg must have the same DataSize and TxFlags. Otherwise, the +interface shall return ERR_INVALID_MSG + +• The default filter behavior after PassThruConnect is to block all messages, which means no messages +will be placed in the receive queue until a PASS_FILTER has been set. Messages that match a +PASS_FILTER can still be blocked by a BLOCK_FILTER + +• Figure 16 and Figure 17 show how the message filtering mechanism operates + +For ISO 15765: +• PASS_FILTERs and BLOCK_FILTERs must not be used and shall cause the interface to return +ERR_INVALID_FILTER_ID + +• Filters shall not be applied to indications or loopback messages. When loopback is on, the original +message shall be copied to the receive queue upon the last segment being transmitted on the bus. + +• Non-segmented messages do not need to match a FLOW_CONTROL_FILTER. + +• No segmented messages can be transmitted without matching an appropriate FLOW_CONTROL_FILTER. +An appropriate filter is one in which the pFlowControlMsg CAN ID matches the messages to be +transmitted. Also, the ISO 15765_ADDR_TYPE (reference TxFlags in Section 8.7.3) bits must match. +If that bit is set, the first byte after the CAN IDs (the extended address) +must match too. + +• No message (segmented or unsegmented) shall be received without matching an appropriate +FLOW_CONTROL_FILTER. An appropriate filter is one in which the pPatternMsg CAN ID matches +the incoming message ID. If the ISO 15765_ADDR_TYPE (reference TxFlags in Section 8.7.3) bit is +set in the filter, the first byte after the CAN IDs (the extended address) must match too. + +• All 3 message pointers must have the same DataSize and TxFlags. Otherwise, the interface shall +return ERR_INVALID_MSG. + +• Both the pFlowControlMsg ID and the pPatternMsg ID must be unique (not match any IDs in any other +filters). The only exception is that pPatternMsg can equal pFlowControlMsg to allow for receiving +functionally addressed messages. In this case, only non-segmented messages can be received. + +• See Appendix A for a detailed description of flow control filter usage. + + + + +8.4 Format Checks for Messages Passed to the API +The vendor DLL shall validate all PASSTHRU_MSG structures, and return an ERR_INVALID_MSG in the following cases: +• DataSize violates Min Tx or Max Tx columns in Figure 42 + +• Source address (Data[3]) is different from the Node ID (Ioctl SET_CONFIG, Parameter NODE_ADDRESS) on J1850PWM + +• The header length field is incorrect for the number of bytes in the message on ISO14230 + +• The CAN_29_BIT flag of the message does not match the CAN_29_BIT flag passed to +PassThruConnect, unless the CAN_ID_BOTH bit was set on connect + +The vendor DLL shall return ERR_MSG_PROTOCOL_ID when the ProtocolID field in the message does +not match the Protocol ID specified when opening the channel. + + + +8.5 Conventions for Returning Messages from the API +When returning a message in PassThruReadMsg: +– DataSize shall tell the application how many bytes in the Data array are valid. ExtraDataIndex will be +the (non-zero) index of the last byte of the message. If ExtraDataIndex is not equal to DataSize there +are extra data bytes after the message. If loopback is on, RxStatus must be consulted to tell if the +message came via loopback. + +– DataSize will be in the range shown in the Min Rx and Max Rx columns of Figure 42. If the device +receives a message from the vehicle bus that is too long or too short, the message shall be discarded +with no error. + +– For received messages, ExtraDataIndex shall be equal to DataSize, except when the interface is +returning SAE J1850 PWM IFR bytes. In no case shall ExtraDataIndex be larger than DataSize. + +– When receiving a message on an SAE J1850 PWM channel, the message shall have any IFR bytes +appended. In this case, ExtraDataIndex shall be the index of the first IFR byte, and DataSize shall be +the total length of the original message plus all IFR bytes. For example, if there are two IFR bytes, +DataSize will be incremented by two, and ExtraDataIndex will be DataSize - 2. When loopback is on, +the loopback message shall contain any IFR bytes. + + + +8.6 Conventions for Retuning Indications from the API +When returning an indication in PassThruReadMsg: +– ExtraDataIndex must be zero + +– DataSize shall tell the application how many bytes in the Data array are valid + +– RxStatus must be consulted to determine the indication type (See Section 8.4). + +– A TxDone indication (ISO 15765 only) is generated by the DLL after a SingleFrame message is sent, +or the last frame of a multi-segment transmission is sent. DataSize shall be 4 (or 5 when the message +was using Extended Addressing). Data shall contain the CAN ID (and possible Extended Address) of +the message just sent. If loopback is on, the TxDone indication shall precede the loopback message in +the receive queue. + +– An RxBreak indication (SAE J2610/SCI and SAE J1850VPW only) is generated by the DLL if a break +is received. + +– An RxStart indication is generated by the DLL when starting to receive a message on ISO9141 or +ISO14230, or when receiving the FirstFrame signal of a multi-segment ISO 15765 message. + + + +9.1 Naming of Files +Each vendor will provide a different name implementation of the API DLL and a number of these +implementations could simultaneously reside on the same PC. No vendor shall name its implementation +“J2534.DLL”. All implementations shall have the string “32” suffixed to end of the name of the API DLL to +indicate 32-bit. For example, if the company name is “Vendor X” the name could be VENDRX32.DLL. + +For simplicity, an API DLL shall be named in accordance with the file allocation table (FAT) file system +naming convention (which allows up to eight characters for the file name and three characters for the +extension with no spaces anywhere). Note that, given this criteria, the major name of an API DLL can be +no greater than six characters. The OEM application can determine the name of the appropriate vendor’s +DLL using the Win32 Registry mechanism described in this section. + + + + +A.1 Flow Control Overview +ISO 15765-2 was designed to send blocks of up to 4095 bytes on top of the limited 8-byte payload of raw +CAN frames. If the data is small enough, it can fit in a single frame and be transmitted like a raw CAN +message with additional headers. Otherwise, the block is broken up into segments and becomes a +segmented transmission, generating CAN frames in both directions. For flexibility, the receiver of the +segments can control the rate at which the segments are sent. + +Each transmission is actually part of a conversation between two nodes. There is no discovery +mechanism for conversation partners. Therefore, each desired conversation must be pre-defined on each +side before the conversation can start. Conversations are symmetric, meaning that either side can send a +block of data to the other. A conversation can only have one transfer (in one direction) in progress at a +time. One transfer must complete before the next transfer (in the same or in a different direction) can +start. The device must support multiple transfers at once, as long as each one is part of a different +conversation. Raw CAN frames are not allowed when using ISO15765-2. + +A key feature of a conversation is that each side has a unique CAN ID, and each side uses their unique +CAN ID for all transmissions during the conversation. No other CAN IDs are part of the conversation. +Even though the useful data is only flowing in one direction, both sides are transmitting. One side is +sending the flow control message to pace the segments of data coming from the other side. + +For example, during OBD communication, a pass-thru device and an ECU might have a conversation. +The pass-thru device will use the "Tester1" physical CAN ID ($241), and the first ECU will use the +"ECU1" physical CAN ID ($641). During a multi-segment transfer, both sides will be transmitting using +only their respective IDs. It does not matter if the data is being sent by the ECU or by the Tester, the IDs +remain the same. + +It is important to understand the difference between OBD Requests/Responses and ISO 15765-2 +transfers. The OBD Request is transmitted from the Tester to the ECU using functional addressing. +Because segmented transfer is not possible on functional addresses, the message must fit in a single +frame. The OBD Response is a message from the ECU to the Tester using physical addressing. Unlike +other protocols, the responses are not sequential. In fact, the responses can overlap, as if each ECU +were having a private conversation with the Tester. Some of the responses may fit in a single frame, +while others will require a segmented transfer from the ECU to the tester. + + +A.2 Transmitting a Segmented Message +When PassThruWrite is called, the API will search the list of flow control filters, looking for a +pFlowControlMsg that matches the CAN ID (and possible extended address) of the message being sent. +Upon matching a filter, the pass-thru device will: + +• Start the ISO 15765 transfer by sending a FirstFrame on the bus. The CAN ID of this segment was +specified in both the message and the matching pFlowControlMsg. In our example, this is $241. + +• Wait for a FlowControl frame from the conversation partner. The CAN ID to look for is specified in the +corresponding pPatternMsg. In our example, this is $641. + +• Transmit the message data in ConsecutiveFrames according to the FlowControl frame’s instructions +for BS (BlockSize) and STmin (SeparationTime minimum). Again, the pass-thru device transmits using +CAN ID specified in pFlowControlMsg. In our example, this is $241. + +• Repeat the previous two steps as required. + +• When finished, the pass-thru device will place a TxDone indication in the API receive queue. The data +will contain the CAN ID specified in pFlowControlMsg. In our example, this is $241. + +• If loopback is on, the entire message sent will appear in the API receive queue with the +TX_MSG_TYPE bit set to 1 in RxStatus. The loopback shall not precede the TxDone indication. + +Before any multi-segment transfer can take place, the conversation must be set up on both sides. It’s +assumed that the ECU is already setup. The application is responsible for setting up the pass-thru device. +This setup must be done once (and only once) per conversation. The setup involves a single call to +PassThruStartMsgFilter, with the following parameters: + +A.2.2 Data Transmission +Once the conversation is set up, any number of messages (to the conversation partner) can be +transmitted using PassThruWriteMsg. The interface shall handle all aspects of the transfer, including +pacing (slowing) the transmission to the requirements of the receiver. + +When there are multiple conversations setup, the pass-thru device will search all of the flow control filters +for a matching pFlowControlMsg. If there is no match, the message cannot be sent because the pass- +thru device doesn’t know which partner will be pacing the conversation. + +When doing blocking writes, it is important to pick a timeout long enough to cover entire transfer, even if +the ECU is pacing things slowly. Otherwise PassThruWriteMsg will return with a timeout, even though the +transmission is proceeding normally. + + +A.3 Transmitting an Unsegmented Message +As a special case, transfers that fit in a single frame can be transmitted without setting up a conversation. +This is useful during an OBD Request, which is a functionally addressed message that is broadcast to all +ECUs. This message must be small enough to fit into a single frame (including headers) because it is not +possible to do one segmented transfer to multiple ECUs. + +When using functional addressing for an OBD Request, it is important to remember that there can be no +direct reply. Instead, each ECU will send their OBD Response using physical addressing to their +conversation partner (e.g. ECU1 to Tester1, ECU2 to Tester2) as defined by ISO 15765-4. The OBD +Response may be a segmented transfer, or it may be a single frame. + +In this case, no conversation setup is necessary. The call to PassThruWriteMsg is the same as above, +except that the DataSize must be 7 bytes or less (6 bytes or less if extended addressing is turned on). +The pass-thru device will automatically insert a PCI byte before transmission. + + +A.4 Receiving a Segmented Message +Message reception is asynchronous to the application. When a FirstFrame is seen on the bus, the pass- +thru device will search the list of flow control filters, looking for a pPatternMsg message with the same +CAN ID (and possible extended address) as the FirstFrame. Upon matching a filter, the pass-thru device will: + +• Place an RxStart indication in the API receive queue. This indication has the START_OF_MESSAGE +bit set in RxFlags. The message data will contain the CAN ID of the sender. In our example, this is +$641. DataSize will be 4 bytes (5 with extended addressing), and ExtraDataIndex will be zero. + +• Send a FlowControl frame to the conversation partner. The FlowStatus field shall be set to +ontinueToSend. The CAN ID of this segment comes from the filter’s corresponding +pFlowControlMsg. In our example, this CAN ID is $241. The BS (BlockSize) and STmin +(SeparationTime minimum) parameters default to zero, but can be changed with the SET_CONFIGIoctl. + +• Wait for the conversation partner to send C +onsecutiveFrames containing the actual data. The +partner’s CAN ID is specified in pPatternMsg. In our example, this CAN ID is $641. + +• Repeat as necessary until the entire block has been received. When finished, the pass-thru device will +put the assembled message into the API receive queue. The CAN ID of the assembled message will +be the CAN ID of the sender. In our example, this CAN ID is $641. + +If the FirstFrame does not match any flow control filters, then the message must be ignored by the +device. + +Segmented messages cause the API to generate an RxStart indication. This lets the application know +that the device has started message reception. It may take a while before message reception is +complete, especially if the application has increased BS and STmin. + +Once the transfer is complete, the entire message can be read like on any other protocol. Usually, +applications will call PassThruReadMsg again immediately after getting an RxStart indication. Application +writers should not assume that the complete message will always follow the RxStart indication. If multiple +conversations are setup, indications and messages from other conversations can be received in between +the RxStart indication and the actual message. The parameters for PassThruReadMsg are exactly the +same as in the previous section. The only difference is that the DataSize will be larger and +ExtraDataIndex will be non-zero. + + + +A.5 Receiving an Unsegmented Message +No messages can be received until a conversation is setup. Each conversation setup will receive +messages from exactly one CAN ID (and extended address if present). Because setup is bi-directional, +the same PassThruStartMsgFilter call used for transmission will allow for message reception. + +When a SingleFrame is seen on the bus, the pass-thru device will search the list of flow control filters, +looking for a pPatternMsg message with the same C +AN ID (and possible extended address) as the +SingleFrame. Upon matching a filter, the pass-thru device will strip the PCI byte and queue the packet for +reception. If the SingleFrame does not match a flow control filter, it must be discarded. + +The only difference between the previous cases is that single-frame messages do not generate an +RxStart indication. + + + + + + + + + diff --git a/drivers/windows/docs/read_msg_flags.png b/drivers/windows/docs/read_msg_flags.png new file mode 100644 index 0000000..6d0a1f8 Binary files /dev/null and b/drivers/windows/docs/read_msg_flags.png differ diff --git a/drivers/windows/docs/reginfo.txt b/drivers/windows/docs/reginfo.txt new file mode 100644 index 0000000..0318283 --- /dev/null +++ b/drivers/windows/docs/reginfo.txt @@ -0,0 +1,2 @@ +#32 bit: HKEY_LOCAL_MACHINE\SOFTWARE\PassThruSupport +#64 bit: HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\PassThruSupport diff --git a/drivers/windows/docs/start_msg_filter.png b/drivers/windows/docs/start_msg_filter.png new file mode 100644 index 0000000..7cb1a7a Binary files /dev/null and b/drivers/windows/docs/start_msg_filter.png differ diff --git a/drivers/windows/docs/start_msg_filter2.png b/drivers/windows/docs/start_msg_filter2.png new file mode 100644 index 0000000..54063a5 Binary files /dev/null and b/drivers/windows/docs/start_msg_filter2.png differ diff --git a/drivers/windows/docs/start_msg_filter3.png b/drivers/windows/docs/start_msg_filter3.png new file mode 100644 index 0000000..ffc8e34 Binary files /dev/null and b/drivers/windows/docs/start_msg_filter3.png differ diff --git a/drivers/windows/docs/start_msg_filter4.png b/drivers/windows/docs/start_msg_filter4.png new file mode 100644 index 0000000..9c21fb0 Binary files /dev/null and b/drivers/windows/docs/start_msg_filter4.png differ diff --git a/drivers/windows/docs/timeout_info.txt b/drivers/windows/docs/timeout_info.txt new file mode 100644 index 0000000..22cfa2c --- /dev/null +++ b/drivers/windows/docs/timeout_info.txt @@ -0,0 +1,42 @@ +From focum information on NI hardware: https://forums.ni.com/t5/Automotive-and-Embedded-Networks/15765-2-with-NI-products/td-p/1454256 + +///////////////////////////////////////////////////////////////////// +Timeout Diag Command is the timeout in milliseconds the master +waits for the response to a diagnostic request message. The default is +1000 ms. + +Timeout FC (Bs) is the timeout in milliseconds the master waits +for a Flow Control frame after sending a First Frame or the last +Consecutive Frame of a block. The default is 250 ms. + +Timeout CF (Cr) is the timeout in milliseconds the master waits +for a Consecutive Frame in a multiframe response. The default is +250 ms. + +Receive Block Size (BS) is the number of Consecutive Frames the +slave sends in one block before waiting for the next Flow Control +frame. A value of 0 (default) means all Consecutive Frames are sent +in one run without interruption. + +Wait Time CF (STmin) defines the minimum time for the slave to +wait between sending two Consecutive Frames of a block. Values +from 0 to 127 are wait times in milliseconds. Values 241 to 249 +(Hex F1 to F9) mean wait times of 100 μs to 900 μs, respectively. +All other values are reserved. The default is 5 ms. + +Max Wait Frames (N_WFTmax) is the maximum number of WAIT +frames the master accepts before terminating the connection. The +default is 10. + + +There are no defined lower limits for these values; you can specify any +value down to 0. However, as you correctly pointed out, the timing is +done by Windows, and will be subject to the jitter introduced by the OS +which can easily be in the order of 10s of milliseconds. It is however +hard to give more accurate numbers as the actual jitter is dependent on +the workload of the computer +///////////////////////////////////////////////////////////////////// + +J2534 04.04 does not appear to have default adjustable parameters for +the timeout related fields. For now, these default values shall be used +in the Panda J2534 implementation. diff --git a/drivers/windows/panda Driver Package/panda Driver Package.vcxproj b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj new file mode 100644 index 0000000..5b448e9 --- /dev/null +++ b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj @@ -0,0 +1,99 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {BD34DB24-F5DC-4992-A74F-05FAF731ABED} + {a1357fe7-03e0-4d61-85f4-09c7ed38c0c1} + v4.5 + 12.0 + $driverCurrentWindowsConfigurationName$ Debug + Win32 + panda_Driver_Package + $(LatestTargetPlatformVersion) + + + + Windows10 + true + WindowsKernelModeDriver10.0 + Utility + Package + true + + + Windows10 + false + WindowsKernelModeDriver10.0 + Utility + Package + true + + + + + + + + + + + + DbgengRemoteDebugger + + + + False + False + True + + 133563 + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + DbgengRemoteDebugger + + + + False + False + True + + 133563 + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + + + + + + + + + + + $(KMDF_VERSION_MAJOR).$(KMDF_VERSION_MINOR) + + + + + + + $(KMDF_VERSION_MAJOR).$(KMDF_VERSION_MINOR) + + + + + + \ No newline at end of file diff --git a/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters new file mode 100644 index 0000000..b4cf077 --- /dev/null +++ b/drivers/windows/panda Driver Package/panda Driver Package.vcxproj.filters @@ -0,0 +1,14 @@ + + + + + {8E41214B-6785-4CFE-B992-037D68949A14} + inf;inv;inx;mof;mc; + + + + + Driver Files + + + \ No newline at end of file diff --git a/drivers/windows/panda Driver Package/panda.inf b/drivers/windows/panda Driver Package/panda.inf new file mode 100644 index 0000000..69390dc Binary files /dev/null and b/drivers/windows/panda Driver Package/panda.inf differ diff --git a/drivers/windows/panda.ico b/drivers/windows/panda.ico new file mode 100644 index 0000000..593a5cd Binary files /dev/null and b/drivers/windows/panda.ico differ diff --git a/drivers/windows/panda.sln b/drivers/windows/panda.sln new file mode 100644 index 0000000..a74e402 --- /dev/null +++ b/drivers/windows/panda.sln @@ -0,0 +1,90 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pandaJ2534DLL", "pandaJ2534DLL\pandaJ2534DLL.vcxproj", "{A2BB18A5-F26B-48D6-BBB5-B83D64473C77}" + ProjectSection(ProjectDependencies) = postProject + {5528AEFB-638D-49AF-B9D4-965154E7D531} = {5528AEFB-638D-49AF-B9D4-965154E7D531} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda", "panda\panda.vcxproj", "{5528AEFB-638D-49AF-B9D4-965154E7D531}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda_playground", "panda_playground\panda_playground.vcxproj", "{691DB635-C272-4B98-897E-0505B970DCA9}" + ProjectSection(ProjectDependencies) = postProject + {5528AEFB-638D-49AF-B9D4-965154E7D531} = {5528AEFB-638D-49AF-B9D4-965154E7D531} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "panda Driver Package", "panda Driver Package\panda Driver Package.vcxproj", "{BD34DB24-F5DC-4992-A74F-05FAF731ABED}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Tests", "pandaJ2534DLL Test\pandaJ2534DLL Test.vcxproj", "{7912F978-B48C-4C5D-8BFD-5D1E22158E47}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECUsim DLL", "ECUsim DLL\ECUsim DLL.vcxproj", "{96E0E646-EE76-444D-9A77-A0CD7F781DEB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECUsim CLI", "ECUsim CLI\ECUsim CLI.vcxproj", "{D99E2FCD-21A4-4065-949A-31E34E0E69D1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x64.ActiveCfg = Debug|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x86.ActiveCfg = Debug|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Debug|x86.Build.0 = Debug|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x64.ActiveCfg = Release|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x86.ActiveCfg = Release|Win32 + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77}.Release|x86.Build.0 = Release|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x64.ActiveCfg = Debug|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x64.Build.0 = Debug|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x86.ActiveCfg = Debug|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Debug|x86.Build.0 = Debug|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x64.ActiveCfg = Release|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x64.Build.0 = Release|x64 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x86.ActiveCfg = Release|Win32 + {5528AEFB-638D-49AF-B9D4-965154E7D531}.Release|x86.Build.0 = Release|Win32 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x64.ActiveCfg = Debug|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x64.Build.0 = Debug|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x86.ActiveCfg = Debug|Win32 + {691DB635-C272-4B98-897E-0505B970DCA9}.Debug|x86.Build.0 = Debug|Win32 + {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x64.ActiveCfg = Release|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x64.Build.0 = Release|x64 + {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x86.ActiveCfg = Release|Win32 + {691DB635-C272-4B98-897E-0505B970DCA9}.Release|x86.Build.0 = Release|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x64.ActiveCfg = Debug|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x86.ActiveCfg = Debug|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x86.Build.0 = Debug|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Debug|x86.Deploy.0 = Debug|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x64.ActiveCfg = Release|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x86.ActiveCfg = Release|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x86.Build.0 = Release|Win32 + {BD34DB24-F5DC-4992-A74F-05FAF731ABED}.Release|x86.Deploy.0 = Release|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x64.ActiveCfg = Debug|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x86.ActiveCfg = Debug|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Debug|x86.Build.0 = Debug|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x64.ActiveCfg = Release|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x86.ActiveCfg = Release|Win32 + {7912F978-B48C-4C5D-8BFD-5D1E22158E47}.Release|x86.Build.0 = Release|Win32 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x64.ActiveCfg = Debug|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x64.Build.0 = Debug|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x86.ActiveCfg = Debug|Win32 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Debug|x86.Build.0 = Debug|Win32 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x64.ActiveCfg = Release|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x64.Build.0 = Release|x64 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x86.ActiveCfg = Release|Win32 + {96E0E646-EE76-444D-9A77-A0CD7F781DEB}.Release|x86.Build.0 = Release|Win32 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x64.ActiveCfg = Debug|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x64.Build.0 = Debug|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x86.ActiveCfg = Debug|Win32 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Debug|x86.Build.0 = Debug|Win32 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x64.ActiveCfg = Release|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x64.Build.0 = Release|x64 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x86.ActiveCfg = Release|Win32 + {D99E2FCD-21A4-4065-949A-31E34E0E69D1}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/drivers/windows/panda/device.cpp b/drivers/windows/panda/device.cpp new file mode 100644 index 0000000..a204610 --- /dev/null +++ b/drivers/windows/panda/device.cpp @@ -0,0 +1,166 @@ +#include "stdafx.h" +#include +#include + +#include +#include + +#include "device.h" + +using namespace panda; + +//Returns the last Win32 error, in string format. Returns an empty string if there is no error. +tstring GetLastErrorAsString(){ + //Get the error message, if any. + DWORD errorMessageID = ::GetLastError(); + if (errorMessageID == 0) + return tstring(); //No error message has been recorded + + _TCHAR *messageBuffer = nullptr; + size_t size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (_TCHAR*)&messageBuffer, 0, NULL); + + tstring message(messageBuffer, size); + + //Free the buffer. + LocalFree(messageBuffer); + + return message; +} + +std::unordered_map panda::detect_pandas() { + HDEVINFO deviceInfo; + HRESULT hr; + SP_DEVINFO_DATA deviceInfoData; + SP_DEVICE_INTERFACE_DATA interfaceData; + unsigned int deviceIndex; + + std::unordered_map map_sn_to_devpath; + + deviceInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_panda, + NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); //DIGCF_ALLCLASSES + + if (deviceInfo == INVALID_HANDLE_VALUE) { + hr = HRESULT_FROM_WIN32(GetLastError()); + _tprintf(_T("Failed to get dev handle. HR: %d\n"), hr); + return map_sn_to_devpath; + } + + ZeroMemory(&deviceInfoData, sizeof(SP_DEVINFO_DATA)); + deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + deviceIndex = 0; + + while (SetupDiEnumDeviceInfo(deviceInfo, deviceIndex, &deviceInfoData)) { + deviceIndex++; + _tprintf(_T("Device info index %d\n"), deviceIndex); + + interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); + if (SetupDiEnumDeviceInterfaces(deviceInfo, &deviceInfoData, + &GUID_DEVINTERFACE_panda, 0, &interfaceData) == FALSE) { + _tprintf(_T(" Got unexpected error while accessing interface %d\n"), GetLastError()); + continue; + } + + DWORD requiredLength; + if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, NULL, 0, &requiredLength, NULL) == FALSE + && ERROR_INSUFFICIENT_BUFFER != GetLastError()) { + _tprintf(_T(" Got unexpected error while reading interface details %d\n"), GetLastError()); + continue; + } + + PSP_DEVICE_INTERFACE_DETAIL_DATA detailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, requiredLength); + if (NULL == detailData) { + _tprintf(_T(" Failed to allocate %d bytes for interface data\n"), requiredLength); + continue; + } + detailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA); + + DWORD length = requiredLength; + if (SetupDiGetDeviceInterfaceDetail(deviceInfo, &interfaceData, detailData, length, &requiredLength, NULL) == FALSE) { + _tprintf(_T(" Got unexpected error while reading interface details (2nd time) %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(detailData); + continue; + } + + //_tprintf(_T(" Path: '%s'\n"), detailData->DevicePath); + HANDLE deviceHandle = CreateFile(detailData->DevicePath, + GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (INVALID_HANDLE_VALUE == deviceHandle) { + _tprintf(_T(" Error opening Device Handle %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(detailData); + continue; + } + + WINUSB_INTERFACE_HANDLE winusbHandle; + if (WinUsb_Initialize(deviceHandle, &winusbHandle) == FALSE) { + _tprintf(_T(" Error initializing WinUSB %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + + USB_DEVICE_DESCRIPTOR deviceDesc; + unsigned long lengthReceived; + if (WinUsb_GetDescriptor(winusbHandle, USB_DEVICE_DESCRIPTOR_TYPE, 0, 0, + (PBYTE)&deviceDesc, sizeof(deviceDesc), &lengthReceived) == FALSE + || lengthReceived != sizeof(deviceDesc)) { + _tprintf(_T(" Error getting device descriptor %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + + #define SNDESCLEN 64 + PUSB_STRING_DESCRIPTOR psnDesc = (PUSB_STRING_DESCRIPTOR)LocalAlloc(LMEM_FIXED, SNDESCLEN); + if (NULL == psnDesc) { + _tprintf(_T(" Failed to allocate %d bytes for sn data\n"), SNDESCLEN); + continue; + } + + if (WinUsb_GetDescriptor(winusbHandle, USB_STRING_DESCRIPTOR_TYPE, deviceDesc.iSerialNumber, + 0x0409 /*Eng*/, (PBYTE)psnDesc, SNDESCLEN, &lengthReceived) == FALSE || lengthReceived == 0) { + _tprintf(_T(" Error getting serial number %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(psnDesc); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + //The minus 2 is for the two numbers, not the null term. + psnDesc->bString[(psnDesc->bLength - 2) / sizeof(_TCHAR)] = 0; + + char w_to_m_buff[256]; + size_t mbuff_len; + if (wcstombs_s(&mbuff_len, w_to_m_buff, sizeof(w_to_m_buff), psnDesc->bString, 24) != 0) { + _tprintf(_T(" Error generating mb SN string %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + LocalFree(psnDesc); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + continue; + } + std::string serialnum(w_to_m_buff, mbuff_len-1); + printf(" Device found: seriallen: %d; serial: %s\n", lengthReceived, serialnum.c_str()); + + map_sn_to_devpath[serialnum] = tstring(detailData->DevicePath); + + LocalFree(psnDesc); + WinUsb_Free(winusbHandle); + CloseHandle(deviceHandle); + LocalFree(detailData); + } + + if(deviceInfo) + SetupDiDestroyDeviceInfoList(deviceInfo); + + return map_sn_to_devpath; +} diff --git a/drivers/windows/panda/device.h b/drivers/windows/panda/device.h new file mode 100644 index 0000000..3b40485 --- /dev/null +++ b/drivers/windows/panda/device.h @@ -0,0 +1,33 @@ +#ifndef __PANDA_DEVICE +#define __PANDA_DEVICE + +// +// Define below GUIDs +// +#include "stdafx.h" +#include +#include + +#if defined(UNICODE) +#define _tcout std::wcout +#define tstring std::wstring +#else +#define _tcout std::cout +#define tstring std::string +#endif + +// +// Device Interface GUID. +// Used by all WinUsb devices that this application talks to. +// Must match "DeviceInterfaceGUIDs" registry value specified in the INF file. +// cce5291c-a69f-4995-a4c2-2ae57a51ade9 +// +DEFINE_GUID(GUID_DEVINTERFACE_panda, + 0xcce5291c,0xa69f,0x4995,0xa4,0xc2,0x2a,0xe5,0x7a,0x51,0xad,0xe9); + +tstring GetLastErrorAsString(); + +namespace panda { + std::unordered_map __declspec(dllexport) detect_pandas(); +} +#endif diff --git a/drivers/windows/panda/dllmain.cpp b/drivers/windows/panda/dllmain.cpp new file mode 100644 index 0000000..69b5891 --- /dev/null +++ b/drivers/windows/panda/dllmain.cpp @@ -0,0 +1,19 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "stdafx.h" + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/drivers/windows/panda/main.cpp b/drivers/windows/panda/main.cpp new file mode 100644 index 0000000..621c600 --- /dev/null +++ b/drivers/windows/panda/main.cpp @@ -0,0 +1,79 @@ +#include "stdafx.h" + +#include + +LONG __cdecl +_tmain( + LONG Argc, + LPTSTR * Argv + ) +/*++ + +Routine description: + + Sample program that communicates with a USB device using WinUSB + +--*/ +{ + DEVICE_DATA deviceData; + HRESULT hr; + USB_DEVICE_DESCRIPTOR deviceDesc; + BOOL bResult; + BOOL noDevice; + ULONG lengthReceived; + + UNREFERENCED_PARAMETER(Argc); + UNREFERENCED_PARAMETER(Argv); + + // + // Find a device connected to the system that has WinUSB installed using our + // INF + // + hr = OpenDevice(&deviceData, &noDevice); + + if (FAILED(hr)) { + + if (noDevice) { + + printf(_T("Device not connected or driver not installed\n")); + + } else { + + printf(_T("Failed looking for device, HRESULT 0x%x\n"), hr); + } + + return 0; + } + + // + // Get device descriptor + // + bResult = WinUsb_GetDescriptor(deviceData.WinusbHandle, + USB_DEVICE_DESCRIPTOR_TYPE, + 0, + 0, + (PBYTE) &deviceDesc, + sizeof(deviceDesc), + &lengthReceived); + + if (FALSE == bResult || lengthReceived != sizeof(deviceDesc)) { + + printf(_T("Error among LastError %d or lengthReceived %d\n"), + FALSE == bResult ? GetLastError() : 0, + lengthReceived); + CloseDevice(&deviceData); + return 0; + } + + // + // Print a few parts of the device descriptor + // + printf(_T("Device found: VID_%04X&PID_%04X; bcdUsb %04X; path: %s\n"), + deviceDesc.idVendor, + deviceDesc.idProduct, + deviceDesc.bcdUSB, + deviceData.DevicePath); + + CloseDevice(&deviceData); + return 0; +} diff --git a/drivers/windows/panda/panda.cpp b/drivers/windows/panda/panda.cpp new file mode 100644 index 0000000..79ec08e --- /dev/null +++ b/drivers/windows/panda/panda.cpp @@ -0,0 +1,479 @@ +// panda.cpp : Defines the exported functions for the DLL application. +// + +#include "stdafx.h" +#include "device.h" +#include "panda.h" + +#define REQUEST_IN 0xC0 +#define REQUEST_OUT 0x40 + +#define CAN_TRANSMIT 1 +#define CAN_EXTENDED 4 + +using namespace panda; + +#pragma pack(1) +typedef struct _PANDA_CAN_MSG_INTERNAL { + uint32_t rir; + uint32_t f2; + uint8_t dat[8]; +} PANDA_CAN_MSG_INTERNAL; + +Panda::Panda( + WINUSB_INTERFACE_HANDLE WinusbHandle, + HANDLE DeviceHandle, + tstring devPath_, + std::string sn_ +) : usbh(WinusbHandle), devh(DeviceHandle), devPath(devPath_), sn(sn_) { + printf("CREATED A PANDA %s\n", this->sn.c_str()); + this->set_can_loopback(FALSE); + this->set_alt_setting(0); +} + +Panda::~Panda() { + WinUsb_Free(this->usbh); + CloseHandle(this->devh); + printf("Cleanup Panda %s\n", this->sn.c_str()); +} + +std::vector Panda::listAvailablePandas() { + std::vector ret; + auto map_sn_to_devpath = detect_pandas(); + + for (auto kv : map_sn_to_devpath) { + ret.push_back(std::string(kv.first)); + } + + return ret; +} + +std::unique_ptr Panda::openPanda(std::string sn) +{ + auto map_sn_to_devpath = detect_pandas(); + + if (map_sn_to_devpath.empty()) return nullptr; + if (map_sn_to_devpath.find(sn) == map_sn_to_devpath.end() && sn != "") return nullptr; + + tstring devpath; + if (sn.empty()) { + sn = map_sn_to_devpath.begin()->first; + devpath = map_sn_to_devpath.begin()->second; + } else { + devpath = map_sn_to_devpath[sn]; + } + + HANDLE deviceHandle = CreateFile(devpath.c_str(), + GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, + NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, NULL); + + if (INVALID_HANDLE_VALUE == deviceHandle) { + _tprintf(_T(" Error opening Device Handle %d.\n"),// Msg: '%s'\n"), + GetLastError());// , GetLastErrorAsString().c_str()); + return nullptr; + } + + WINUSB_INTERFACE_HANDLE winusbHandle; + if (WinUsb_Initialize(deviceHandle, &winusbHandle) == FALSE) { + _tprintf(_T(" Error initializing WinUSB %d.\n"),// Msg: '%s'\n"), + GetLastError());// , GetLastErrorAsString().c_str()); + CloseHandle(deviceHandle); + return nullptr; + } + + return std::unique_ptr(new Panda(winusbHandle, deviceHandle, map_sn_to_devpath[sn], sn)); +} + +std::string Panda::get_usb_sn() { + return std::string(this->sn); +} + +int Panda::control_transfer( + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + void * data, + uint16_t wLength, + unsigned int timeout +) { + UNREFERENCED_PARAMETER(timeout); + + WINUSB_SETUP_PACKET SetupPacket; + ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET)); + ULONG cbSent = 0; + + //Create the setup packet + SetupPacket.RequestType = bmRequestType; + SetupPacket.Request = bRequest; + SetupPacket.Value = wValue; + SetupPacket.Index = wIndex; + SetupPacket.Length = wLength; + + //ULONG timeout = 10; // ms + //WinUsb_SetPipePolicy(interfaceHandle, pipeID, PIPE_TRANSFER_TIMEOUT, sizeof(ULONG), &timeout); + + if (WinUsb_ControlTransfer(this->usbh, SetupPacket, (PUCHAR)data, wLength, &cbSent, 0) == FALSE) { + return -1; + } + + return cbSent; +} + +int Panda::bulk_write(UCHAR endpoint, const void * buff, ULONG length, PULONG transferred, ULONG timeout) { + if (this->usbh == INVALID_HANDLE_VALUE || !buff || !length || !transferred) return FALSE; + + if (WinUsb_WritePipe(this->usbh, endpoint, (PUCHAR)buff, length, transferred, NULL) == FALSE) { + _tprintf(_T(" Got error during bulk xfer: %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + return TRUE; +} + +int Panda::bulk_read(UCHAR endpoint, void * buff, ULONG buff_size, PULONG transferred, ULONG timeout) { + if (this->usbh == INVALID_HANDLE_VALUE || !buff || !buff_size || !transferred) return FALSE; + + if (WinUsb_ReadPipe(this->usbh, endpoint, (PUCHAR)buff, buff_size, transferred, NULL) == FALSE) { + _tprintf(_T(" Got error during bulk xfer: %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + return TRUE; +} + +bool Panda::set_alt_setting(UCHAR alt_setting) { + if (WinUsb_AbortPipe(this->usbh, 0x81) == FALSE) { + _tprintf(_T(" Error abobrting pipe before setting altsetting. continue. %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + } + if (WinUsb_SetCurrentAlternateSetting(this->usbh, alt_setting) == FALSE) { + _tprintf(_T(" Error setting usb altsetting %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + + // Either the panda or the windows usb stack can drop messages + // if an odd number of messages are sent before an interrupt IN + // message is canceled. There are some other odd behaviors, but + // the best solution so far has been to send a few messages + // before using the device to clear out the pipe. No, the windows + // functions for clearing/resetting/etc the pipe did not work. + // This took way too to figure out a workaround. + // New info. The most repeatable behavior is losing the first + // message sent after setting alt setting to 1 (even without + // receiving). Something like this happened on linux sometimes. + bool loopback_backup = this->loopback; + this->set_can_loopback(TRUE); + Sleep(20); // Give time for any sent messages to appear in the RX buffer. + this->can_clear(PANDA_CAN_RX); + for (int i = 0; i < 2; i++) { + printf("Sending PAD %d\n", i); + if (this->can_send(0x7FF, FALSE, {}, 0, PANDA_CAN1) == FALSE) { + auto err = GetLastError(); + printf("Got err on first send: %d\n", err); + } + } + Sleep(10); + //this->can_clear(PANDA_CAN_RX); + + std::vector msg_recv; + if (alt_setting == 1) { + //Read the messages so they do not contaimnate the real message stream. + auto err = this->can_recv_async(NULL, msg_recv, 1000); + } + else { + msg_recv = this->can_recv(); + } + + //this->set_can_loopback(FALSE); + this->set_can_loopback(loopback_backup); + + return TRUE; +} + +UCHAR Panda::get_current_alt_setting() { + UCHAR alt_setting; + if (WinUsb_GetCurrentAlternateSetting(this->usbh, &alt_setting) == FALSE) { + _tprintf(_T(" Error getting usb altsetting %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + + return alt_setting; +} + +PANDA_HEALTH Panda::get_health() +{ + WINUSB_SETUP_PACKET SetupPacket; + ZeroMemory(&SetupPacket, sizeof(WINUSB_SETUP_PACKET)); + ULONG cbSent = 0; + + //Create the setup packet + SetupPacket.RequestType = REQUEST_IN; + SetupPacket.Request = 0xD2; + SetupPacket.Value = 0; + SetupPacket.Index = 0; + SetupPacket.Length = sizeof(UCHAR); + + //uint8_t health[13]; + PANDA_HEALTH health; + + if (WinUsb_ControlTransfer(this->usbh, SetupPacket, (PUCHAR)&health, sizeof(health), &cbSent, 0) == FALSE) { + _tprintf(_T(" Got unexpected error while reading panda health (2nd time) %d. Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + } + + return health; +} + +bool Panda::enter_bootloader() { + return this->control_transfer(REQUEST_OUT, 0xd1, 0, 0, NULL, 0, 0) != -1; +} + +std::string Panda::get_version() { + char buff[0x40]; + ZeroMemory(&buff, sizeof(buff)); + + int xferCount = this->control_transfer(REQUEST_IN, 0xd6, 0, 0, buff, 0x40, 0); + if (xferCount == -1) return std::string(); + return std::string(buff); +} + +//TODO: Do hash stuff for calculating the serial. +std::string Panda::get_serial() { + char buff[0x20]; + ZeroMemory(&buff, sizeof(buff)); + + int xferCount = this->control_transfer(REQUEST_IN, 0xD0, 0, 0, buff, 0x20, 0); + if (xferCount == -1) return std::string(); + return std::string(buff); + + //dat = self._handle.controlRead(REQUEST_IN, 0xd0, 0, 0, 0x20); + //hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4] + // assert(hashsig == calc_hash) + // return[dat[0:0x10], dat[0x10:0x10 + 10]] +} + +//Secret appears to by raw bytes, not a string. TODO: Change returned type. +std::string Panda::get_secret() { + char buff[0x10]; + ZeroMemory(&buff, sizeof(buff)); + + int xferCount = this->control_transfer(REQUEST_IN, 0xd0, 1, 0, buff, 0x10, 0); + if (xferCount == -1) return std::string(); + return std::string(buff); +} + +bool Panda::set_usb_power(bool on) { + return this->control_transfer(REQUEST_OUT, 0xe6, (int)on, 0, NULL, 0, 0) != -1; +} + +bool Panda::set_esp_power(bool on) { + return this->control_transfer(REQUEST_OUT, 0xd9, (int)on, 0, NULL, 0, 0) != -1; +} + +bool Panda::esp_reset(uint16_t bootmode = 0) { + return this->control_transfer(REQUEST_OUT, 0xda, bootmode, 0, NULL, 0, 0) != -1; +} + +bool Panda::set_safety_mode(PANDA_SAFETY_MODE mode = SAFETY_NOOUTPUT) { + return this->control_transfer(REQUEST_OUT, 0xdc, mode, 0, NULL, 0, 0) != -1; +} + +bool Panda::set_can_forwarding(PANDA_CAN_PORT from_bus, PANDA_CAN_PORT to_bus) { + if (from_bus == PANDA_CAN_UNK) return FALSE; + return this->control_transfer(REQUEST_OUT, 0xdd, from_bus, to_bus, NULL, 0, 0) != -1; +} + +bool Panda::set_gmlan(PANDA_GMLAN_HOST_PORT bus = PANDA_GMLAN_CAN3) { + return this->control_transfer(REQUEST_OUT, 0xdb, 1, (bus == PANDA_GMLAN_CLEAR) ? 0 : bus, NULL, 0, 0) != -1; +} + +bool Panda::set_can_loopback(bool enable) { + this->loopback = enable; + return this->control_transfer(REQUEST_OUT, 0xe5, enable, 0, NULL, 0, 0) != -1; +} + +//Can not use the full range of 16 bit speed. +//cbps means centa bits per second (tento of kbps) +bool Panda::set_can_speed_cbps(PANDA_CAN_PORT bus, uint16_t speed) { + if (bus == PANDA_CAN_UNK) return FALSE; + return this->control_transfer(REQUEST_OUT, 0xde, bus, speed, NULL, 0, 0) != -1; +} + +//Can not use the full range of 16 bit speed. +bool Panda::set_can_speed_kbps(PANDA_CAN_PORT bus, uint16_t speed) { + return set_can_speed_cbps(bus, speed * 10); +} + +//Can not use full 32 bit range of rate +bool Panda::set_uart_baud(PANDA_SERIAL_PORT uart, uint32_t rate) { + return this->control_transfer(REQUEST_OUT, 0xe4, uart, rate / 300, NULL, 0, 0) != -1; +} + +bool Panda::set_uart_parity(PANDA_SERIAL_PORT uart, PANDA_SERIAL_PORT_PARITY parity) { + return this->control_transfer(REQUEST_OUT, 0xe2, uart, parity, NULL, 0, 0) != -1; +} + +bool Panda::can_send_many(const std::vector& can_msgs) { + std::vector formatted_msgs; + formatted_msgs.reserve(can_msgs.size()); + + for (auto msg : can_msgs) { + if (msg.bus == PANDA_CAN_UNK) continue; + if (msg.len > 8) continue; + PANDA_CAN_MSG_INTERNAL tmpmsg = {}; + tmpmsg.rir = (msg.addr_29b) ? + ((msg.addr << 3) | CAN_TRANSMIT | CAN_EXTENDED) : + (((msg.addr & 0x7FF) << 21) | CAN_TRANSMIT); + tmpmsg.f2 = msg.len | (msg.bus << 4); + memcpy(tmpmsg.dat, msg.dat, msg.len); + formatted_msgs.push_back(tmpmsg); + } + + if (formatted_msgs.size() == 0) return FALSE; + + unsigned int retcount; + return this->bulk_write(3, formatted_msgs.data(), + sizeof(PANDA_CAN_MSG_INTERNAL)*formatted_msgs.size(), (PULONG)&retcount, 0); +} + +bool Panda::can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus) { + if (bus == PANDA_CAN_UNK) return FALSE; + if (len > 8) return FALSE; + PANDA_CAN_MSG msg; + msg.addr_29b = addr_29b; + msg.addr = addr; + msg.len = len; + memcpy(msg.dat, dat, msg.len); + msg.bus = bus; + return this->can_send_many(std::vector{msg}); +} + +void Panda::parse_can_recv(std::vector& msg_recv, char *buff, int retcount) { + for (int i = 0; i < retcount; i += sizeof(PANDA_CAN_MSG_INTERNAL)) { + PANDA_CAN_MSG_INTERNAL *in_msg_raw = (PANDA_CAN_MSG_INTERNAL *)(buff + i); + PANDA_CAN_MSG in_msg; + + in_msg.addr_29b = (bool)(in_msg_raw->rir & CAN_EXTENDED); + in_msg.addr = (in_msg.addr_29b) ? (in_msg_raw->rir >> 3) : (in_msg_raw->rir >> 21); + in_msg.recv_time = this->runningTime.getTimePassedUS(); + in_msg.recv_time_point = std::chrono::steady_clock::now(); + //The timestamp from the device is (in_msg_raw->f2 >> 16), + //but this 16 bit value is a little hard to use. Using a + //timer since the initialization of this device. + in_msg.len = in_msg_raw->f2 & 0xF; + memcpy(in_msg.dat, in_msg_raw->dat, 8); + + in_msg.is_receipt = ((in_msg_raw->f2 >> 4) & 0x80) == 0x80; + switch ((in_msg_raw->f2 >> 4) & 0x7F) { + case PANDA_CAN1: + in_msg.bus = PANDA_CAN1; + break; + case PANDA_CAN2: + in_msg.bus = PANDA_CAN2; + break; + case PANDA_CAN3: + in_msg.bus = PANDA_CAN3; + break; + default: + in_msg.bus = PANDA_CAN_UNK; + } + msg_recv.push_back(in_msg); + } +} + +bool Panda::can_recv_async(HANDLE kill_event, std::vector& msg_buff, DWORD timeoutms) { + int retcount; + char buff[sizeof(PANDA_CAN_MSG_INTERNAL) * 4]; + + // Overlapped structure required for async read. + HANDLE m_hReadFinishedEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + OVERLAPPED m_overlappedData; + memset(&m_overlappedData, sizeof(OVERLAPPED), 0); + m_overlappedData.hEvent = m_hReadFinishedEvent; + + HANDLE phSignals[2] = { m_hReadFinishedEvent, kill_event }; + + if (!WinUsb_ReadPipe(this->usbh, 0x81, (PUCHAR)buff, sizeof(buff), (PULONG)&retcount, &m_overlappedData)) { + // An overlapped read will return true if done, or false with an + // error of ERROR_IO_PENDING if the transfer is still in process. + DWORD dwError = GetLastError(); + if (dwError == ERROR_IO_PENDING) { + dwError = WaitForMultipleObjects(kill_event ? 2 : 1, phSignals, FALSE, timeoutms); + + // Check if packet, timeout (nope), or break + if (dwError == WAIT_OBJECT_0) { + // Signal came from our usb object. Read the returned data. + if (!GetOverlappedResult(this->usbh, &m_overlappedData, (PULONG)&retcount, FALSE)) { + // TODO: handle other error cases better. + dwError = GetLastError(); + printf("Got overlap error %d\n", dwError); + + return TRUE; + } + } else { + WinUsb_AbortPipe(this->usbh, 0x81); + + // Return FALSE to show that the optional signal + // was set instead of the wait breaking from a + // message or recoverable error. + if (dwError == (WAIT_OBJECT_0 + 1)) { + return FALSE; + } + return TRUE; + } + } else { // ERROR_BAD_COMMAND happens when device is unplugged. + return FALSE; + } + } + + parse_can_recv(msg_buff, buff, retcount); + return TRUE; +} + +std::vector Panda::can_recv() { + std::vector msg_recv; + int retcount; + char buff[sizeof(PANDA_CAN_MSG_INTERNAL) * 4]; + + if (this->bulk_read(0x81, buff, sizeof(buff), (PULONG)&retcount, 0) == FALSE) + return msg_recv; + + parse_can_recv(msg_recv, buff, retcount); + + return msg_recv; +} + +bool Panda::can_clear(PANDA_CAN_PORT_CLEAR bus) { + /*Clears all messages from the specified internal CAN ringbuffer as though it were drained. + bus(int) : can bus number to clear a tx queue, or 0xFFFF to clear the global can rx queue.*/ + return this->control_transfer(REQUEST_OUT, 0xf1, bus, 0, NULL, 0, 0) != -1; +} + +std::string Panda::serial_read(PANDA_SERIAL_PORT port_number) { + std::string result; + char buff[0x40]; + while (TRUE) { + int retlen = this->control_transfer(REQUEST_IN, 0xe0, port_number, 0, &buff, 0x40, 0); + if (retlen <= 0) + break; + result += std::string(buff, retlen); + if (retlen < 0x40) break; + } + return result; +} + +int Panda::serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len) { + std::string dat; + dat += port_number; + dat += std::string((char*)buff, len); + int retcount; + if (this->bulk_write(2, dat.c_str(), len+1, (PULONG)&retcount, 0) == FALSE) return -1; + return retcount; +} + +bool Panda::serial_clear(PANDA_SERIAL_PORT port_number) { + return this->control_transfer(REQUEST_OUT, 0xf2, port_number, 0, NULL, 0, 0) != -1; +} diff --git a/drivers/windows/panda/panda.h b/drivers/windows/panda/panda.h new file mode 100644 index 0000000..12a4fbb --- /dev/null +++ b/drivers/windows/panda/panda.h @@ -0,0 +1,214 @@ +#pragma once + +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the PANDA_EXPORTS +// symbol defined on the command line. This symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// PANDA_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef PANDA_EXPORTS +#define PANDA_API __declspec(dllexport) +#else +#define PANDA_API __declspec(dllimport) +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(UNICODE) +#define _tcout std::wcout +#define tstring std::wstring +#else +#define _tcout std::cout +#define tstring std::string +#endif + +#define LIN_MSG_MAX_LEN 10 + +//template class __declspec(dllexport) std::basic_string; + +namespace panda { + typedef enum _PANDA_SAFETY_MODE : uint16_t { + SAFETY_NOOUTPUT = 0, + SAFETY_HONDA = 1, + SAFETY_ALLOUTPUT = 0x1337, + } PANDA_SAFETY_MODE; + + typedef enum _PANDA_SERIAL_PORT : uint8_t { + SERIAL_DEBUG = 0, + SERIAL_ESP = 1, + SERIAL_LIN1 = 2, + SERIAL_LIN2 = 3, + } PANDA_SERIAL_PORT; + + typedef enum _PANDA_SERIAL_PORT_PARITY : uint8_t { + PANDA_PARITY_OFF = 0, + PANDA_PARITY_EVEN = 1, + PANDA_PARITY_ODD = 2, + } PANDA_SERIAL_PORT_PARITY; + + typedef enum _PANDA_CAN_PORT : uint8_t { + PANDA_CAN1 = 0, + PANDA_CAN2 = 1, + PANDA_CAN3 = 2, + PANDA_CAN_UNK = 0xFF, + } PANDA_CAN_PORT; + + typedef enum _PANDA_CAN_PORT_CLEAR : uint16_t { + PANDA_CAN1_TX = 0, + PANDA_CAN2_TX = 1, + PANDA_CAN3_TX = 2, + PANDA_CAN_RX = 0xFFFF, + } PANDA_CAN_PORT_CLEAR; + + typedef enum _PANDA_GMLAN_HOST_PORT : uint8_t { + PANDA_GMLAN_CLEAR = 0, + PANDA_GMLAN_CAN2 = 1, + PANDA_GMLAN_CAN3 = 2, + } PANDA_GMLAN_HOST_PORT; + + #pragma pack(1) + typedef struct _PANDA_HEALTH { + uint32_t voltage; + uint32_t current; + uint8_t started; + uint8_t controls_allowed; + uint8_t gas_interceptor_detected; + uint8_t started_signal_detected; + uint8_t started_alt; + } PANDA_HEALTH, *PPANDA_HEALTH; + + typedef struct _PANDA_CAN_MSG { + uint32_t addr; + unsigned long long recv_time; //In microseconds since device initialization + std::chrono::time_point recv_time_point; + uint8_t dat[8]; + uint8_t len; + PANDA_CAN_PORT bus; + bool is_receipt; + bool addr_29b; + } PANDA_CAN_MSG; + + //Copied from https://stackoverflow.com/a/31488113 + class Timer + { + using clock = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point < clock, std::chrono::microseconds >; + public: + Timer() { + start = std::chrono::time_point_cast(clock::now()); + } + + // gets the time elapsed from construction. + unsigned long long /*microseconds*/ Timer::getTimePassedUS() { + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + return (end - start).count(); + } + + // gets the time elapsed from construction. + unsigned long long /*milliseconds*/ Timer::getTimePassedMS() { + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + auto startms = std::chrono::time_point_cast(start); + return (end - startms).count(); + } + private: + time_point_type start; + }; + + // This class is exported from the panda.dll + class PANDA_API Panda { + public: + static std::vector listAvailablePandas(); + static std::unique_ptr openPanda(std::string sn); + + ~Panda(); + + std::string get_usb_sn(); + bool set_alt_setting(UCHAR alt_setting); + UCHAR get_current_alt_setting(); + + PANDA_HEALTH get_health(); + bool enter_bootloader(); + std::string get_version(); + std::string get_serial(); + std::string get_secret(); + + bool set_usb_power(bool on); + bool set_esp_power(bool on); + bool esp_reset(uint16_t bootmode); + bool set_safety_mode(PANDA_SAFETY_MODE mode); + bool set_can_forwarding(PANDA_CAN_PORT from_bus, PANDA_CAN_PORT to_bus); + bool set_gmlan(PANDA_GMLAN_HOST_PORT bus); + bool set_can_loopback(bool enable); + bool set_can_speed_cbps(PANDA_CAN_PORT bus, uint16_t speed); + bool set_can_speed_kbps(PANDA_CAN_PORT bus, uint16_t speed); + bool set_uart_baud(PANDA_SERIAL_PORT uart, uint32_t rate); + bool set_uart_parity(PANDA_SERIAL_PORT uart, PANDA_SERIAL_PORT_PARITY parity); + + bool can_send_many(const std::vector& can_msgs); + bool can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus); + void parse_can_recv(std::vector& msg_recv, char *buff, int retcount); + bool can_recv_async(HANDLE kill_event, std::vector& msg_buff, DWORD timeoutms = INFINITE); + std::vector can_recv(); + bool can_clear(PANDA_CAN_PORT_CLEAR bus); + + std::string serial_read(PANDA_SERIAL_PORT port_number); + int serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len); + bool serial_clear(PANDA_SERIAL_PORT port_number); + private: + Panda( + WINUSB_INTERFACE_HANDLE WinusbHandle, + HANDLE DeviceHandle, + tstring devPath_, + std::string sn_ + ); + + int control_transfer( + uint8_t bmRequestType, + uint8_t bRequest, + uint16_t wValue, + uint16_t wIndex, + void * data, + uint16_t wLength, + unsigned int timeout + ); + + int bulk_write( + UCHAR endpoint, + const void * buff, + ULONG length, + PULONG transferred, + ULONG timeout + ); + + int Panda::bulk_read( + UCHAR endpoint, + void * buff, + ULONG buff_size, + PULONG transferred, + ULONG timeout + ); + + WINUSB_INTERFACE_HANDLE usbh; + HANDLE devh; + tstring devPath; + std::string sn; + bool loopback; + + Timer runningTime; + }; + +} diff --git a/drivers/windows/panda/panda.ico b/drivers/windows/panda/panda.ico new file mode 100644 index 0000000..ff0e071 Binary files /dev/null and b/drivers/windows/panda/panda.ico differ diff --git a/drivers/windows/panda/panda.rc b/drivers/windows/panda/panda.rc new file mode 100644 index 0000000..88cf9f7 Binary files /dev/null and b/drivers/windows/panda/panda.rc differ diff --git a/drivers/windows/panda/panda.vcxproj b/drivers/windows/panda/panda.vcxproj new file mode 100644 index 0000000..147c58c --- /dev/null +++ b/drivers/windows/panda/panda.vcxproj @@ -0,0 +1,193 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {5528AEFB-638D-49AF-B9D4-965154E7D531} + Win32Proj + panda + 8.1 + + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + false + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + Use + Level3 + Disabled + _DEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_WINDOWS;_USRDLL;PANDA_EXPORTS;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);winusb.lib;setupapi.lib + + + + + + + + + + + + + false + + + false + + + false + + + false + + + + + + Create + Create + Create + Create + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/windows/panda/panda.vcxproj.filters b/drivers/windows/panda/panda.vcxproj.filters new file mode 100644 index 0000000..cded701 --- /dev/null +++ b/drivers/windows/panda/panda.vcxproj.filters @@ -0,0 +1,58 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/drivers/windows/panda/resource.h b/drivers/windows/panda/resource.h new file mode 100644 index 0000000..bf006ff Binary files /dev/null and b/drivers/windows/panda/resource.h differ diff --git a/drivers/windows/panda/stdafx.cpp b/drivers/windows/panda/stdafx.cpp new file mode 100644 index 0000000..8793e09 --- /dev/null +++ b/drivers/windows/panda/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// panda.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/panda/stdafx.h b/drivers/windows/panda/stdafx.h new file mode 100644 index 0000000..cc3d3b9 --- /dev/null +++ b/drivers/windows/panda/stdafx.h @@ -0,0 +1,19 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +#endif +// Windows Header Files: +#include + +#include +#include +#include +#include diff --git a/drivers/windows/panda/targetver.h b/drivers/windows/panda/targetver.h new file mode 100644 index 0000000..1bf4ee6 --- /dev/null +++ b/drivers/windows/panda/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include + +#define WINVER _WIN32_WINNT_WIN7 +#define _WIN32_WINNT _WIN32_WINNT_WIN7 + +#include diff --git a/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp b/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp new file mode 100644 index 0000000..1effc49 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/ECUsim_tests.cpp @@ -0,0 +1,87 @@ +#include "stdafx.h" +#include "Loader4.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda/panda.h" +#include "Timer.h" +#include "ECUsim DLL\ECUsim.h" +#include "TestHelpers.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace pandaWCUsimTest +{ + + TEST_CLASS(ECUsimTests) + { + public: + + TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bStandardAddrPad500k) + { + ECUsim sim("", 500000); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x01\x00", 3, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x01\x00", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x06\x41\x00\xff\xff\xff\xfe\x00", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bStandardAddrPad250k) + { + ECUsim sim("", 250000); + auto p = getPanda(250); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x01\x00", 3, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x01\x00", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x06""\x41\x00""\xff\xff\xff\xfe""\x00", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_SingleFrameTx_29bExtAddrPad500k) + { + ECUsim sim("", 500000, TRUE); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x02\x01\x00", 4, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x02\x01\x00", 4), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x06""\x41\x00""\xff\xff\xff\xfe", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_MultiFrameTx_29bStandardAddrPad500k) + { + ECUsim sim("", 500000); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x02\x09\x02", 3, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x02\x09\x02", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x10\x14""\x49\x02\x01""1D4", 8), LINE_INFO()); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x30\x00\x00", 3, panda::PANDA_CAN1); + msg_recv = panda_recv_loop(p, 3); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x30\x0\x0", 3), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x21""GP00R55", 8), LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x22""B123456", 8), LINE_INFO()); + } + + TEST_METHOD(ECUsim_ISO15765_MultiFrameTx_29bExtAddrPad500k) + { + ECUsim sim("", 500000, TRUE); + auto p = getPanda(500); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x02\x09\x02", 4, panda::PANDA_CAN1); + auto msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x02\x09\x02", 4), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x10\x14""\x49\x02\x01""1D", 8), LINE_INFO()); + + p->can_send(0x18daeff1, TRUE, (const uint8_t*)"\x13""\x30\x00\x00", 4, panda::PANDA_CAN1); + msg_recv = panda_recv_loop(p, 4); + check_panda_can_msg(msg_recv[0], 0, 0x18daeff1, TRUE, TRUE, std::string("\x13""\x30\x0\x0", 4), LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x21""4GP00R", 8), LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x22""55B123", 8), LINE_INFO()); + check_panda_can_msg(msg_recv[3], 0, 0x18daf1ef, TRUE, FALSE, std::string("\x13""\x23""456", 5), LINE_INFO()); + } + }; + +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/Loader4.cpp b/drivers/windows/pandaJ2534DLL Test/Loader4.cpp new file mode 100644 index 0000000..f4a0b70 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Loader4.cpp @@ -0,0 +1,240 @@ +// Loader4.cpp +// (c) 2005 National Control Systems, Inc. +// Portions (c) 2004 Drew Technologies, Inc. +// Dynamic J2534 v04.04 dll loader for VB + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to: +// the Free Software Foundation, Inc. +// 51 Franklin Street, Fifth Floor +// Boston, MA 02110-1301, USA + +// National Control Systems, Inc. +// 10737 Hamburg Rd +// Hamburg, MI 48139 +// 810-231-2901 + +// Drew Technologies, Inc. +// 7012 E.M -36, Suite 3B +// Whitmore Lake, MI 48189 +// 810-231-3171 + +#define STRICT +#include "stdafx.h" +#include +#include "Loader4.h" + +PTOPEN LocalOpen; +PTCLOSE LocalClose; +PTCONNECT LocalConnect; +PTDISCONNECT LocalDisconnect; +PTREADMSGS LocalReadMsgs; +PTWRITEMSGS LocalWriteMsgs; +PTSTARTPERIODICMSG LocalStartPeriodicMsg; +PTSTOPPERIODICMSG LocalStopPeriodicMsg; +PTSTARTMSGFILTER LocalStartMsgFilter; +PTSTOPMSGFILTER LocalStopMsgFilter; +PTSETPROGRAMMINGVOLTAGE LocalSetProgrammingVoltage; +PTREADVERSION LocalReadVersion; +PTGETLASTERROR LocalGetLastError; +PTIOCTL LocalIoctl; + +HINSTANCE hDLL = NULL; +//BOOL bIsCorrectVersion = FALSE; + +BOOL WINAPI DllMain(HINSTANCE hInstA, DWORD dwReason, LPVOID lpvReserved) +{ + switch (dwReason) { + case DLL_PROCESS_ATTACH: + // The DLL is being mapped into the process's address space + + case DLL_THREAD_ATTACH: + // A thread is being created + break; + + case DLL_THREAD_DETACH: + // A thread is exiting cleanly + break; + + case DLL_PROCESS_DETACH: + // The DLL is being unmapped from the process's address space + break; + } + + return TRUE; +} + + +long WINAPI LoadJ2534Dll(char *sLib) +{ + long lFuncList = 0; + + if (hDLL != NULL) UnloadJ2534Dll(); + hDLL = LoadLibraryA (sLib); + if (hDLL == NULL) return ERR_NO_DLL; + + LocalOpen = (PTOPEN)(GetProcAddress(hDLL, "PassThruOpen")); + if (LocalOpen == NULL) lFuncList = lFuncList | ERR_NO_PTOPEN; + + LocalClose = (PTCLOSE)(GetProcAddress(hDLL, "PassThruClose")); + if (LocalClose == NULL) lFuncList = lFuncList | ERR_NO_PTCLOSE; + + LocalConnect = (PTCONNECT)(GetProcAddress(hDLL,"PassThruConnect")); + if (LocalConnect == NULL) lFuncList = lFuncList | ERR_NO_PTCONNECT; + + LocalDisconnect = (PTDISCONNECT)(GetProcAddress(hDLL,"PassThruDisconnect")); + if (LocalDisconnect == NULL) lFuncList = lFuncList | ERR_NO_PTDISCONNECT; + + LocalReadMsgs = (PTREADMSGS)(GetProcAddress(hDLL,"PassThruReadMsgs")); + if (LocalReadMsgs == NULL) lFuncList = lFuncList | ERR_NO_PTREADMSGS; + + LocalWriteMsgs = (PTWRITEMSGS)(GetProcAddress(hDLL,"PassThruWriteMsgs")); + if (LocalWriteMsgs == NULL) lFuncList = lFuncList | ERR_NO_PTWRITEMSGS; + + LocalStartPeriodicMsg = (PTSTARTPERIODICMSG)(GetProcAddress(hDLL,"PassThruStartPeriodicMsg")); + if (LocalStartPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTARTPERIODICMSG; + + LocalStopPeriodicMsg = (PTSTOPPERIODICMSG)(GetProcAddress(hDLL,"PassThruStopPeriodicMsg")); + if (LocalStopPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTOPPERIODICMSG; + + LocalStartMsgFilter = (PTSTARTMSGFILTER)(GetProcAddress(hDLL,"PassThruStartMsgFilter")); + if (LocalStartPeriodicMsg == NULL) lFuncList = lFuncList | ERR_NO_PTSTARTMSGFILTER; + + LocalStopMsgFilter = (PTSTOPMSGFILTER)(GetProcAddress(hDLL,"PassThruStopMsgFilter")); + if (LocalStopMsgFilter == NULL) lFuncList = lFuncList | ERR_NO_PTSTOPMSGFILTER; + + LocalSetProgrammingVoltage = (PTSETPROGRAMMINGVOLTAGE)(GetProcAddress(hDLL,"PassThruSetProgrammingVoltage")); + if (LocalSetProgrammingVoltage == NULL) lFuncList = lFuncList | ERR_NO_PTSETPROGRAMMINGVOLTAGE; + + LocalReadVersion = (PTREADVERSION)(GetProcAddress(hDLL,"PassThruReadVersion")); + if (LocalReadVersion == NULL) lFuncList = lFuncList | ERR_NO_PTREADVERSION; + + LocalGetLastError = (PTGETLASTERROR)(GetProcAddress(hDLL,"PassThruGetLastError")); + if (LocalGetLastError == NULL) lFuncList = lFuncList | ERR_NO_PTGETLASTERROR; + + LocalIoctl = (PTIOCTL)(GetProcAddress(hDLL,"PassThruIoctl")); + if (LocalIoctl == NULL) lFuncList = lFuncList | ERR_NO_PTIOCTL; + + if (lFuncList == ERR_NO_FUNCTIONS) return ERR_WRONG_DLL_VER; + + return lFuncList; +} + +long WINAPI UnloadJ2534Dll() +{ + if (FreeLibrary(hDLL)) + { + hDLL = NULL; + LocalOpen = NULL; + LocalClose = NULL; + LocalConnect = NULL; + LocalDisconnect = NULL; + LocalReadMsgs = NULL; + LocalWriteMsgs = NULL; + LocalStartPeriodicMsg = NULL; + LocalStopPeriodicMsg = NULL; + LocalStartMsgFilter = NULL; + LocalStopMsgFilter = NULL; + LocalSetProgrammingVoltage = NULL; + LocalReadVersion = NULL; + LocalGetLastError = NULL; + LocalIoctl = NULL; + return 0; + } + return ERR_NO_DLL; +} + +long WINAPI PassThruOpen(void *pName, unsigned long *pDeviceID) +{ + if (LocalOpen == NULL) return ERR_FUNC_MISSING; + return LocalOpen(pName, pDeviceID); +} + +long WINAPI PassThruClose(unsigned long DeviceID) +{ + if (LocalOpen == NULL) return ERR_FUNC_MISSING; + return LocalClose(DeviceID); +} + +long WINAPI PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long Baudrate, unsigned long *pChannelID) +{ + if (LocalConnect == NULL) return ERR_FUNC_MISSING; + return LocalConnect(DeviceID, ProtocolID, Flags, Baudrate, pChannelID); +} + +long WINAPI PassThruDisconnect(unsigned long ChannelID) +{ + if (LocalDisconnect == NULL) return ERR_FUNC_MISSING; + return LocalDisconnect(ChannelID); +} + +long WINAPI PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) +{ + if (LocalReadMsgs == NULL) return ERR_FUNC_MISSING; + return LocalReadMsgs(ChannelID, pMsg, pNumMsgs, Timeout); +} + +long WINAPI PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) +{ + if (LocalWriteMsgs == NULL) return ERR_FUNC_MISSING; + return LocalWriteMsgs(ChannelID, pMsg, pNumMsgs, Timeout); +} + +long WINAPI PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval) +{ + if (LocalStartPeriodicMsg == NULL) return ERR_FUNC_MISSING; + return LocalStartPeriodicMsg(ChannelID, pMsg, pMsgID, TimeInterval); +} + +long WINAPI PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID) +{ + if (LocalStopPeriodicMsg == NULL) return ERR_FUNC_MISSING; + return LocalStopPeriodicMsg(ChannelID, MsgID); +} + +long WINAPI PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, + PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) +{ + if (LocalStartMsgFilter == NULL) return ERR_FUNC_MISSING; + return LocalStartMsgFilter(ChannelID, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID); +} + +long WINAPI PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID) +{ + if (LocalStopMsgFilter == NULL) return ERR_FUNC_MISSING; + return LocalStopMsgFilter(ChannelID, FilterID); +} + +long WINAPI PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage) +{ + if (LocalSetProgrammingVoltage == NULL) return ERR_FUNC_MISSING; + return LocalSetProgrammingVoltage(DeviceID, PinNumber, Voltage); +} + +long WINAPI PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion) +{ + if (LocalReadVersion == NULL) return ERR_FUNC_MISSING; + return LocalReadVersion(DeviceID, pFirmwareVersion, pDllVersion, pApiVersion); +} + +long WINAPI PassThruGetLastError(char *pErrorDescription) +{ + if (LocalGetLastError == NULL) return ERR_FUNC_MISSING; + return LocalGetLastError(pErrorDescription); +} + +long WINAPI PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput) +{ + if (LocalIoctl == NULL) return ERR_FUNC_MISSING; + return LocalIoctl(ChannelID, IoctlID, pInput, pOutput); +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/Loader4.h b/drivers/windows/pandaJ2534DLL Test/Loader4.h new file mode 100644 index 0000000..9710144 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Loader4.h @@ -0,0 +1,55 @@ +// Loader4.h +// (c) 2005 National Control Systems, Inc. +// Portions (c) 2004 Drew Technologies, Inc. + +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to: +// the Free Software Foundation, Inc. +// 51 Franklin Street, Fifth Floor +// Boston, MA 02110-1301, USA + +// National Control Systems, Inc. +// 10737 Hamburg Rd +// Hamburg, MI 48139 +// 810-231-2901 + +// Drew Technologies, Inc. +// 7012 E.M -36, Suite 3B +// Whitmore Lake, MI 48189 +// 810-231-3171 + +#include "pandaJ2534DLL/J2534_v0404.h" + +//Other Functions +long WINAPI LoadJ2534Dll(char *); +long WINAPI UnloadJ2534Dll(); + +// NCS Returns of any functions not found +#define ERR_NO_PTOPEN 0x0001 +#define ERR_NO_PTCLOSE 0x0002 +#define ERR_NO_PTCONNECT 0x0004 +#define ERR_NO_PTDISCONNECT 0x0008 +#define ERR_NO_PTREADMSGS 0x0010 +#define ERR_NO_PTWRITEMSGS 0x0020 +#define ERR_NO_PTSTARTPERIODICMSG 0x0040 +#define ERR_NO_PTSTOPPERIODICMSG 0x0080 +#define ERR_NO_PTSTARTMSGFILTER 0x0100 +#define ERR_NO_PTSTOPMSGFILTER 0x0200 +#define ERR_NO_PTSETPROGRAMMINGVOLTAGE 0x0400 +#define ERR_NO_PTREADVERSION 0x0800 +#define ERR_NO_PTGETLASTERROR 0x1000 +#define ERR_NO_PTIOCTL 0x2000 +#define ERR_NO_FUNCTIONS 0x3fff +#define ERR_NO_DLL -1 +#define ERR_WRONG_DLL_VER -2 +#define ERR_FUNC_MISSING -3 diff --git a/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp b/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp new file mode 100644 index 0000000..62096d1 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/TestHelpers.cpp @@ -0,0 +1,254 @@ +#include "stdafx.h" +#include "TestHelpers.h" +#include "Loader4.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda/panda.h" +#include "Timer.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +void write_ioctl(unsigned int chanid, unsigned int param, unsigned int val, const __LineInfo* pLineInfo) { + SCONFIG config = { param, val }; + SCONFIG_LIST inconfig = { 1, &config }; + + Assert::AreEqual(STATUS_NOERROR, PassThruIoctl(chanid, SET_CONFIG, &inconfig, NULL), _T("Failed to set IOCTL."), pLineInfo); +} + +std::vector panda_recv_loop_loose(std::unique_ptr& p, unsigned int min_num, unsigned long timeout_ms) { + std::vector ret_messages; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + Sleep(10); + std::vectormsg_recv = p->can_recv(); + if (msg_recv.size() > 0) { + ret_messages.insert(std::end(ret_messages), std::begin(msg_recv), std::end(msg_recv)); + } + } + + Assert::IsTrue(min_num <= ret_messages.size(), _T("Received too few messages.")); + return ret_messages; +} + +std::vector panda_recv_loop(std::unique_ptr& p, unsigned int num_expected, unsigned long timeout_ms) { + std::vector ret_messages; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + Sleep(10); + std::vectormsg_recv = p->can_recv(); + if (msg_recv.size() > 0) { + ret_messages.insert(std::end(ret_messages), std::begin(msg_recv), std::end(msg_recv)); + } + if (ret_messages.size() >= num_expected) break; + } + + std::ostringstream stringStream; + + stringStream << "j2534_recv_loop Broke at " << t.getTimePassed() << " ms size is " << ret_messages.size() << std::endl; + + if (num_expected != ret_messages.size()) { + stringStream << "Incorrect number of messages received. Displaying the messages:" << std::endl; + for (auto msg : ret_messages) { + stringStream << " TS: " << msg.recv_time << "; Dat: "; + for (int i = 0; i < msg.len; i++) stringStream << std::hex << std::setw(2) << std::setfill('0') << int(msg.dat[i] & 0xFF) << " "; + stringStream << std::endl; + } + } + + Logger::WriteMessage(stringStream.str().c_str()); + + Assert::AreEqual(num_expected, ret_messages.size(), _T("Received wrong number of messages.")); + return ret_messages; +} + +void check_panda_can_msg(panda::PANDA_CAN_MSG& msgin, uint8_t bus, unsigned long addr, bool addr_29b, + bool is_receipt, std::string dat, const __LineInfo* pLineInfo) { + Assert::AreEqual(bus, msgin.bus, _T("Wrong msg bus"), pLineInfo); + Assert::AreEqual(addr, msgin.addr, _T("Wrong msg addr"), pLineInfo); + Assert::AreEqual(addr_29b, msgin.addr_29b, _T("Wrong msg 29b flag"), pLineInfo); + Assert::AreEqual(is_receipt, msgin.is_receipt, _T("Wrong msg receipt flag"), pLineInfo); + + std::ostringstream logmsg; + logmsg << "Expected Hex ("; + for (int i = 0; i < dat.size(); i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(dat[i] & 0xFF) << " "; + logmsg << "); Actual Hex ("; + for (int i = 0; i < msgin.len; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(((char*)msgin.dat)[i] & 0xFF) << " "; + logmsg << ")"; + Logger::WriteMessage(logmsg.str().c_str()); + + Assert::AreEqual(dat.size(), msgin.len, _T("Wrong msg len"), pLineInfo); + Assert::AreEqual(dat, std::string((char*)msgin.dat, msgin.len), _T("Wrong msg payload"), pLineInfo); +} + +unsigned long J2534_start_periodic_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, const __LineInfo * pLineInfo) { + PASSTHRU_MSG msg = { ProtocolID, 0, TxFlags, 0, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + unsigned long msgID; + Assert::AreEqual(STATUS_NOERROR, J2534_start_periodic_msg(chanid, ProtocolID, TxFlags, DataSize, + ExtraDataIndex, Data, TimeInterval, &msgID, pLineInfo), _T("Failed to start Periodic Message."), pLineInfo); + return msgID; +} + +unsigned long J2534_start_periodic_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, unsigned long* msgID, const __LineInfo * pLineInfo) { + PASSTHRU_MSG msg = { ProtocolID, 0, TxFlags, 0, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + return PassThruStartPeriodicMsg(chanid, &msg, msgID, TimeInterval); +} + +void J2534_send_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo) { + + PASSTHRU_MSG msg = { ProtocolID, RxStatus, TxFlags, Timestamp, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + unsigned long msgcount = 1; + Assert::AreEqual(STATUS_NOERROR, PassThruWriteMsgs(chanid, &msg, &msgcount, 0), _T("Failed to write message."), pLineInfo); + Assert::AreEqual(1, msgcount, _T("Wrong message count after tx."), pLineInfo); +} + +long J2534_send_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data) { + + PASSTHRU_MSG msg = { ProtocolID, RxStatus, TxFlags, Timestamp, DataSize, ExtraDataIndex }; + memcpy_s(msg.Data, 4128, Data, DataSize); + unsigned long msgcount = 1; + return PassThruWriteMsgs(chanid, &msg, &msgcount, 0); +} + +//Allow more messages to come in than the min. +std::vector j2534_recv_loop_loose(unsigned int chanid, unsigned int min_num, unsigned long timeout_ms) { + std::vector ret_messages; + PASSTHRU_MSG recvbuff[4] = {}; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + unsigned long msgcount = 4; + unsigned int res = PassThruReadMsgs(chanid, recvbuff, &msgcount, 0); + if (res == ERR_BUFFER_EMPTY) continue; + Assert::IsFalse(msgcount > 4, _T("PassThruReadMsgs returned more data than the buffer could hold.")); + Assert::AreEqual(STATUS_NOERROR, res, _T("Failed to read message.")); + if (msgcount > 0) { + for (unsigned int i = 0; i < msgcount; i++) { + ret_messages.push_back(recvbuff[i]); + } + } + } + + Assert::IsTrue(min_num <= ret_messages.size(), _T("Received too few messages.")); + return ret_messages; +} + +std::vector j2534_recv_loop(unsigned int chanid, unsigned int num_expected, unsigned long timeout_ms) { + std::vector ret_messages; + PASSTHRU_MSG recvbuff[4] = {}; + Timer t = Timer(); + + while (t.getTimePassed() < timeout_ms) { + unsigned long msgcount = 4; + unsigned int res = PassThruReadMsgs(chanid, recvbuff, &msgcount, 0); + if (res == ERR_BUFFER_EMPTY) continue; + Assert::IsFalse(msgcount > 4, _T("PassThruReadMsgs returned more data than the buffer could hold.")); + Assert::AreEqual(STATUS_NOERROR, res, _T("Failed to read message.")); + if (msgcount > 0) { + for (unsigned int i = 0; i < msgcount; i++) { + ret_messages.push_back(recvbuff[i]); + } + } + if (ret_messages.size() >= num_expected) break; + } + + std::ostringstream stringStream; + stringStream << "j2534_recv_loop Broke at " << t.getTimePassed() << " ms size is " << ret_messages.size() << std::endl; + + if (num_expected != ret_messages.size()) { + stringStream << "Incorrect number of messages received. Displaying the messages:" << std::endl; + for (auto msg : ret_messages) { + stringStream << " TS: " << msg.Timestamp << "; Dat: "; + for (int i = 0; i < msg.DataSize; i++) stringStream << std::hex << std::setw(2) << std::setfill('0') << int(msg.Data[i] & 0xFF) << " "; + stringStream << std::endl; + } + } + + Logger::WriteMessage(stringStream.str().c_str()); + + Assert::AreEqual(num_expected, ret_messages.size(), _T("Received wrong number of messages.")); + return ret_messages; +} + +void check_J2534_can_msg(PASSTHRU_MSG& msgin, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo) { + Assert::AreEqual(DataSize, msgin.DataSize, _T("Wrong msg len"), pLineInfo); + + std::ostringstream logmsg; + logmsg << "Expected Hex ("; + for (int i = 0; i < DataSize; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(Data[i] & 0xFF) << " "; + logmsg << "); Actual Hex ("; + for (int i = 0; i < msgin.DataSize; i++) logmsg << std::hex << std::setw(2) << std::setfill('0') << int(((char*)msgin.Data)[i] & 0xFF) << " "; + logmsg << ")"; + Logger::WriteMessage(logmsg.str().c_str()); + Assert::AreEqual(std::string(Data, DataSize), std::string((char*)msgin.Data, msgin.DataSize), _T("Wrong msg payload"), pLineInfo); + + Assert::AreEqual(ProtocolID, msgin.ProtocolID, _T("Wrong msg protocol"), pLineInfo); + Assert::AreEqual(RxStatus, msgin.RxStatus, _T("Wrong msg receipt rxstatus"), pLineInfo); + Assert::AreEqual(TxFlags, msgin.TxFlags, _T("Wrong msg receipt txflag"), pLineInfo); + Assert::AreEqual(ExtraDataIndex, msgin.ExtraDataIndex, _T("Wrong msg ExtraDataIndex"), pLineInfo); +} + +unsigned long J2534_set_PASS_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo) { + unsigned long filterid; + PASSTHRU_MSG mask_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG pattern_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + memcpy(mask_msg.Data, mask, len); + memcpy(pattern_msg.Data, pattern, len); + Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, PASS_FILTER, &mask_msg, &pattern_msg, NULL, &filterid), + _T("Failed to create filter."), pLineInfo); + return filterid; +} + +unsigned long J2534_set_BLOCK_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo) { + unsigned long filterid; + PASSTHRU_MSG mask_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG pattern_msg = { ProtocolID, 0, tx, 0, len, 0, 0 }; + memcpy(mask_msg.Data, mask, len); + memcpy(pattern_msg.Data, pattern, len); + Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, BLOCK_FILTER, &mask_msg, &pattern_msg, NULL, &filterid), + _T("Failed to create filter."), pLineInfo); + return filterid; +} + +unsigned long J2534_set_flowctrl_filter(unsigned long chanid, unsigned long tx, + unsigned long len, char* mask, char* pattern, char* flow, const __LineInfo* pLineInfo) { + unsigned long filterid; + PASSTHRU_MSG mask_msg = { ISO15765, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG pattern_msg = { ISO15765, 0, tx, 0, len, 0, 0 }; + PASSTHRU_MSG flow_msg = { ISO15765, 0, tx, 0, len, 0, 0 }; + memcpy(mask_msg.Data, mask, len); + memcpy(pattern_msg.Data, pattern, len); + memcpy(flow_msg.Data, flow, len); + Assert::AreEqual(STATUS_NOERROR, PassThruStartMsgFilter(chanid, FLOW_CONTROL_FILTER, &mask_msg, &pattern_msg, &flow_msg, &filterid), + _T("Failed to create filter."), pLineInfo); + return filterid; +} + +std::unique_ptr getPanda(unsigned long kbaud, BOOL loopback) { + auto p = panda::Panda::openPanda(""); + Assert::IsTrue(p != nullptr, _T("Could not open raw panda device to test communication.")); + p->set_can_speed_kbps(panda::PANDA_CAN1, kbaud); + p->set_safety_mode(panda::SAFETY_ALLOUTPUT); + p->set_can_loopback(loopback); + p->can_clear(panda::PANDA_CAN_RX); + return p; +} + +std::vector checked_panda_send(std::unique_ptr& p, uint32_t addr, bool is_29b, + char* msg, uint8_t len, unsigned int num_expected, const __LineInfo* pLineInfo, unsigned long timeout_ms) { + Assert::IsTrue(p->can_send(addr, is_29b, (const uint8_t*)msg, len, panda::PANDA_CAN1), _T("Panda send says it failed."), pLineInfo); + auto panda_msg_recv = panda_recv_loop(p, 1 + num_expected, timeout_ms); + check_panda_can_msg(panda_msg_recv[0], 0, addr, is_29b, TRUE, std::string(msg, len), pLineInfo); + panda_msg_recv.erase(panda_msg_recv.begin()); + return panda_msg_recv; +} diff --git a/drivers/windows/pandaJ2534DLL Test/TestHelpers.h b/drivers/windows/pandaJ2534DLL Test/TestHelpers.h new file mode 100644 index 0000000..17dcee5 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/TestHelpers.h @@ -0,0 +1,48 @@ +#pragma once +#include "stdafx.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda/panda.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +extern void write_ioctl(unsigned int chanid, unsigned int param, unsigned int val, const __LineInfo* pLineInfo = NULL); + +extern std::vector panda_recv_loop_loose(std::unique_ptr& p, unsigned int min_num, unsigned long timeout_ms = 100); + +extern std::vector panda_recv_loop(std::unique_ptr& p, unsigned int num_expected, unsigned long timeout_ms = 100); + +extern void check_panda_can_msg(panda::PANDA_CAN_MSG& msgin, uint8_t bus, unsigned long addr, bool addr_29b, + bool is_receipt, std::string dat, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_start_periodic_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char * Data, unsigned long TimeInterval, const __LineInfo * pLineInfo); + +extern unsigned long J2534_start_periodic_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long TxFlags, unsigned long DataSize, + unsigned long ExtraDataIndex, const char* Data, unsigned long TimeInterval, unsigned long* msgID, const __LineInfo* pLineInfo = NULL); + +extern void J2534_send_msg_checked(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo = NULL); + +extern long J2534_send_msg(unsigned long chanid, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long Timestamp, unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data); + +extern std::vector j2534_recv_loop_loose(unsigned int chanid, unsigned int min_num, unsigned long timeout_ms = 100); + +extern std::vector j2534_recv_loop(unsigned int chanid, unsigned int num_expected, unsigned long timeout_ms = 100); + +extern void check_J2534_can_msg(PASSTHRU_MSG& msgin, unsigned long ProtocolID, unsigned long RxStatus, unsigned long TxFlags, + unsigned long DataSize, unsigned long ExtraDataIndex, const char* Data, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_set_PASS_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_set_BLOCK_filter(unsigned long chanid, unsigned long ProtocolID, unsigned long tx, + unsigned long len, char* mask, char* pattern, const __LineInfo* pLineInfo = NULL); + +extern unsigned long J2534_set_flowctrl_filter(unsigned long chanid, unsigned long tx, + unsigned long len, char* mask, char* pattern, char* flow, const __LineInfo* pLineInfo = NULL); + +extern std::unique_ptr getPanda(unsigned long kbaud = 500, BOOL loopback = FALSE); + +extern std::vector checked_panda_send(std::unique_ptr& p, uint32_t addr, bool is_29b, + char* msg, uint8_t len, unsigned int num_expected=0, const __LineInfo* pLineInfo = NULL, unsigned long timeout_ms = 100); diff --git a/drivers/windows/pandaJ2534DLL Test/Timer.cpp b/drivers/windows/pandaJ2534DLL Test/Timer.cpp new file mode 100644 index 0000000..33d029e --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Timer.cpp @@ -0,0 +1,21 @@ +#include "stdafx.h" +#include "Timer.h" + + +Timer::Timer() +{ + reset(); +} + +// gets the time elapsed from construction. +unsigned long long /*milliseconds*/ Timer::getTimePassed(){ + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + return (end - start).count(); +} + +void Timer::reset() { + start = std::chrono::time_point_cast(clock::now()); +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/Timer.h b/drivers/windows/pandaJ2534DLL Test/Timer.h new file mode 100644 index 0000000..cbf5579 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/Timer.h @@ -0,0 +1,20 @@ +#pragma once +#include + +//Copied from https://stackoverflow.com/a/31488113 + +class Timer +{ + using clock = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point < clock, std::chrono::milliseconds >; +public: + Timer(); + + // gets the time elapsed from construction. + unsigned long long /*milliseconds*/ getTimePassed(); + + void reset(); + +private: + time_point_type start; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp b/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp new file mode 100644 index 0000000..774b8ed --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/j2534_tests.cpp @@ -0,0 +1,1602 @@ +#include "stdafx.h" +#include "Loader4.h" +#include "pandaJ2534DLL/J2534_v0404.h" +#include "panda/panda.h" +#include "Timer.h" +#include "ECUsim DLL\ECUsim.h" +#include "TestHelpers.h" + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; + +namespace pandaJ2534DLLTest +{ + TEST_CLASS(J2534DLLInitialization) + { + public: + + TEST_CLASS_CLEANUP(deinit) { + UnloadJ2534Dll(); + } + + TEST_METHOD(J2534_Driver_Init) + { + long err = LoadJ2534Dll("pandaJ2534_0404_32.dll"); + Assert::IsTrue(err == 0, _T("Library failed to load properly. Check the export names and library location.")); + } + + }; + + TEST_CLASS(J2534DeviceInitialization) + { + public: + + TEST_METHOD_INITIALIZE(init) { + LoadJ2534Dll("pandaJ2534_0404_32.dll"); + } + + TEST_METHOD_CLEANUP(deinit) { + if (didopen) { + PassThruClose(devid); + didopen = FALSE; + } + UnloadJ2534Dll(); + } + + TEST_METHOD(J2534_Device_OpenDevice__Empty) + { + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_OpenDevice__J2534_2) + { + Assert::AreEqual(STATUS_NOERROR, open_dev("J2534-2:"), _T("Failed to open device."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_OpenDevice__SN) + { + auto pandas_available = panda::Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas detected.")); + + Assert::AreEqual(STATUS_NOERROR, open_dev(pandas_available[0].c_str()), _T("Failed to open device."), LINE_INFO()); + + auto pandas_available_2 = panda::Panda::listAvailablePandas(); + for (auto panda_sn : pandas_available_2) + Assert::AreNotEqual(panda_sn, pandas_available[0]); + } + + TEST_METHOD(J2534_Device_CloseDevice) + { + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(STATUS_NOERROR, close_dev(devid), _T("Failed to close device."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_DEVICE_ID, PassThruClose(devid), _T("The 2nd close should have failed with ERR_INVALID_DEVICE_ID."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_ConnectDisconnect) + { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, CAN, 0, 500000, &chanid), _T("Failed to open channel."), LINE_INFO()); + + Assert::AreEqual(STATUS_NOERROR, PassThruDisconnect(chanid), _T("Failed to close channel."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_CHANNEL_ID, PassThruDisconnect(chanid), _T("The 2nd disconnect should have failed with ERR_INVALID_CHANNEL_ID."), LINE_INFO()); + } + + TEST_METHOD(J2534_Device_ConnectInvalidProtocol) + { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_PROTOCOL_ID, PassThruConnect(devid, 999, 0, 500000, &chanid), + _T("Did not report ERR_INVALID_PROTOCOL_ID."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_CHANNEL_ID, PassThruDisconnect(chanid), _T("The channel should not have been created."), LINE_INFO()); + } + + bool didopen = FALSE; + unsigned long devid; + + unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) { + unsigned int res = PassThruOpen((void*)name, &devid); + if (res == STATUS_NOERROR) didopen = TRUE; + return res; + } + + unsigned long close_dev(unsigned long devid) { + unsigned long res = PassThruClose(devid); + if (res == STATUS_NOERROR) didopen = FALSE; + return res; + } + + }; + + TEST_CLASS(J2534DeviceCAN) + { + public: + + TEST_METHOD_INITIALIZE(init) { + LoadJ2534Dll("pandaJ2534_0404_32.dll"); + } + + TEST_METHOD_CLEANUP(deinit) { + if (didopen) { + PassThruClose(devid); + didopen = FALSE; + } + UnloadJ2534Dll(); + } + + //Test that the BAUD rate of a CAN connection can be changed. + TEST_METHOD(J2534_CAN_SetBaud) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + auto p = getPanda(250); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + write_ioctl(chanid, DATA_RATE, 250000, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_11b_Tx) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + j2534_recv_loop(chanid, 0, 50); // Check no message is returned (since loopback is off) + } + + TEST_METHOD(J2534_CAN_29b_Tx) + { + auto chanid = J2534_open_and_connect("", CAN, CAN_29BIT_ID, 500000, LINE_INFO()); + auto p = getPanda(500); + + Assert::AreEqual(ERR_INVALID_MSG, J2534_send_msg(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI"), _T("11b address should fail to tx."), LINE_INFO()); + J2534_send_msg_checked(chanid, CAN, 0, CAN_29BIT_ID, 0, 6, 6, "\x0\x0\x3\xAB""YO", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, TRUE, FALSE, "YO", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_11b29b_Tx) + { + auto chanid = J2534_open_and_connect("", CAN, CAN_ID_BOTH, 500000, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO()); + J2534_send_msg_checked(chanid, CAN, 0, CAN_29BIT_ID, 0, 6, 6, "\x0\x0\x3\xAB""YO", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, TRUE, FALSE, "YO", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_TxEcho) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 9, 9, "\x0\x0\x3\xAB""HIDOG", LINE_INFO()); + + auto msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HIDOG", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 0); + + ///////////////////////////////// + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 7, 7, "\x0\x0\x3\xAB""SUP", LINE_INFO()); + + msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "SUP", LINE_INFO()); + + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 3 + 4, 0, "\x0\x0\x3\xAB""SUP", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_RxAndPassAllFilters) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_RxAndLimitedPassFilter) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_RxAndPassBlockFilter) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2, 1000); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO()); + } + + //Check that the order of the pass and block filter do not matter + TEST_METHOD(J2534_CAN_RxAndFilterBlockPass) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); // Should not pass filter + p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2, 2000); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO()); + } + + //Check that the order of the pass and block filter do not matter + TEST_METHOD(J2534_CAN_RxAndFilterRemoval) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto filterid0 = J2534_set_BLOCK_filter(chanid, CAN, 0, 4, "\xFF\xFF\xFF\xFF", "\x0\x0\x02\xAC", LINE_INFO()); + auto filterid1 = J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + + Assert::AreEqual(STATUS_NOERROR, PassThruStopMsgFilter(chanid, filterid0), _T("Failed to delete filter."), LINE_INFO()); + + auto p = getPanda(500); + + p->can_send(0x1FA, FALSE, (const uint8_t*)"ABCDE", 5, panda::PANDA_CAN1); + p->can_send(0x2AC, FALSE, (const uint8_t*)"HIJKL", 5, panda::PANDA_CAN1); + p->can_send(0x3FA, FALSE, (const uint8_t*)"MNOPQ", 5, panda::PANDA_CAN1); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 3, 1000); + check_J2534_can_msg(j2534_msg_recv[0], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x1\xFA""ABCDE", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x2\xAC""HIJKL", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], CAN, 0, 0, 5 + 4, 0, "\x0\x0\x3\xFA""MNOPQ", LINE_INFO()); + } + + //Check that the order of the pass and block filter do not matter + TEST_METHOD(J2534_CAN_RxWithTimeout) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + J2534_set_PASS_filter(chanid, CAN, 0, 4, "\x0\x0\x0\x0", "\x0\x0\x0\x0", LINE_INFO()); + auto p = getPanda(500); + + PASSTHRU_MSG recvbuff; + unsigned long msgcount = 1; + unsigned int res = PassThruReadMsgs(chanid, &recvbuff, &msgcount, 100); // Here is where we test the timeout + Assert::AreEqual(ERR_BUFFER_EMPTY, res, _T("No message should be found"), LINE_INFO()); + Assert::AreEqual(0, msgcount, _T("Received wrong number of messages.")); + + //TODO Test that the timings work right instead of just testing it doesn't crash. + } + + TEST_METHOD(J2534_CAN_Baud) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 250000, LINE_INFO()); + auto p = getPanda(250); + + J2534_send_msg_checked(chanid, CAN, 0, 0, 0, 6, 6, "\x0\x0\x3\xAB""HI", LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageStartStop) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 3, 250); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + auto timediff_1_0 = msg_recv[1].recv_time - msg_recv[0].recv_time; + auto timediff_2_1 = msg_recv[2].recv_time - msg_recv[1].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_1_0 > 90000); + Assert::IsTrue(timediff_1_0 < 110000); + Assert::IsTrue(timediff_2_1 > 90000); + Assert::IsTrue(timediff_2_1 < 110000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageMultipleStartStop) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + + auto msgid0 = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + auto msgid1 = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x1\x23""YO", 80, LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 9, 370); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid0), _T("Failed to delete filter."), LINE_INFO()); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid1), _T("Failed to delete filter."), LINE_INFO()); + //time diagram. 10 ms per character. * is send event. : is termination of periodic messages. + //*---------*---------*---------*-----:----* HI + //*-------*-------*-------*-------*---:----* YO + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[3], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[4], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[5], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[6], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + check_panda_can_msg(msg_recv[7], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[8], 0, 0x123, FALSE, FALSE, "YO", LINE_INFO()); + + auto timediff_HI_3_0 = msg_recv[3].recv_time - msg_recv[0].recv_time; + auto timediff_HI_5_3 = msg_recv[5].recv_time - msg_recv[3].recv_time; + auto timediff_HI_7_5 = msg_recv[7].recv_time - msg_recv[5].recv_time; + + auto timediff_YO_2_1 = msg_recv[2].recv_time - msg_recv[1].recv_time; + auto timediff_YO_4_2 = msg_recv[4].recv_time - msg_recv[2].recv_time; + auto timediff_YO_6_4 = msg_recv[6].recv_time - msg_recv[4].recv_time; + auto timediff_YO_8_6 = msg_recv[8].recv_time - msg_recv[6].recv_time; + + std::ostringstream stringStreamHi; + stringStreamHi << "HiTimes: " << timediff_HI_3_0 << ", " << timediff_HI_5_3 << ", " << timediff_HI_7_5 << std::endl; + Logger::WriteMessage(stringStreamHi.str().c_str()); + + std::ostringstream stringStreamYo; + stringStreamYo << "HiTimes: " << timediff_YO_2_1 << ", " << timediff_YO_4_2 << ", " << timediff_YO_6_4 << ", " << timediff_YO_8_6 << std::endl; + Logger::WriteMessage(stringStreamYo.str().c_str()); + + Assert::IsTrue(timediff_HI_3_0 > 90000); + Assert::IsTrue(timediff_HI_3_0 < 110000); + Assert::IsTrue(timediff_HI_5_3 > 90000); + Assert::IsTrue(timediff_HI_5_3 < 110000); + Assert::IsTrue(timediff_HI_7_5 > 90000); + Assert::IsTrue(timediff_HI_7_5 < 110000); + + Assert::IsTrue(timediff_YO_2_1 > 80000-10000); + Assert::IsTrue(timediff_YO_2_1 < 80000+1000); + Assert::IsTrue(timediff_YO_4_2 > 80000 - 10000); + Assert::IsTrue(timediff_YO_4_2 < 80000 + 10000); + Assert::IsTrue(timediff_YO_6_4 > 80000 - 10000); + Assert::IsTrue(timediff_YO_6_4 < 80000 + 10000); + Assert::IsTrue(timediff_YO_8_6 > 80000 - 10000); + Assert::IsTrue(timediff_YO_8_6 < 80000 + 10000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageStartStop_Loopback) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + auto p = getPanda(500); + auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + + std::vector msg_recv = panda_recv_loop(p, 3, 250); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 3); + check_J2534_can_msg(j2534_msg_recv[0], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], CAN, TX_MSG_TYPE, 0, 6, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + + auto timediff_1_0 = j2534_msg_recv[1].Timestamp - j2534_msg_recv[0].Timestamp; + auto timediff_2_1 = j2534_msg_recv[2].Timestamp - j2534_msg_recv[1].Timestamp; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_1_0 > 90000); + Assert::IsTrue(timediff_1_0 < 110000); + Assert::IsTrue(timediff_2_1 > 90000); + Assert::IsTrue(timediff_2_1 < 110000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_PeriodicMessageWithTx) + { + auto chanid = J2534_open_and_connect("", CAN, 0, 500000, LINE_INFO()); + auto p = getPanda(500); + auto msgid = J2534_start_periodic_msg_checked(chanid, CAN, 0, 6, 0, "\x0\x0\x3\xAB""HI", 100, LINE_INFO()); + + J2534_send_msg(chanid, CAN, 0, 0, 0, 7, 0, "\x0\x0\x3\xAB""LOL"); + + std::vector msg_recv = panda_recv_loop(p, 4, 250); + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + check_panda_can_msg(msg_recv[0], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[1], 0, 0x3AB, FALSE, FALSE, "LOL", LINE_INFO());//Staggered write inbetween multiple scheduled TXs + check_panda_can_msg(msg_recv[2], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + check_panda_can_msg(msg_recv[3], 0, 0x3AB, FALSE, FALSE, "HI", LINE_INFO()); + + auto timediff_2_0 = msg_recv[2].recv_time - msg_recv[0].recv_time; + auto timediff_3_2 = msg_recv[3].recv_time - msg_recv[2].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_2_0 << ", " << timediff_3_2 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_2_0 > 90000); + Assert::IsTrue(timediff_2_0 < 110000); + Assert::IsTrue(timediff_3_2 > 90000); + Assert::IsTrue(timediff_3_2 < 110000); + + msg_recv = panda_recv_loop(p, 0, 300); + } + + TEST_METHOD(J2534_CAN_BaudInvalid) + { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(""), _T("Failed to open device."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 6000000, &chanid), _T("Baudrate should have been invalid."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 200, &chanid), _T("Baudrate should have been invalid."), LINE_INFO()); + Assert::AreEqual(ERR_INVALID_BAUDRATE, PassThruConnect(devid, CAN, 0, 250010, &chanid), _T("Baudrate should have been invalid."), LINE_INFO()); + } + + bool didopen = FALSE; + unsigned long devid; + + unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) { + unsigned int res = PassThruOpen((void*)name, &devid); + if (res == STATUS_NOERROR) didopen = TRUE; + return res; + } + + unsigned long J2534_open_and_connect(const char* name, unsigned long ProtocolID, unsigned long Flags, unsigned long bps, const __LineInfo* pLineInfo = NULL) { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(name), _T("Failed to open device."), pLineInfo); + Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, ProtocolID, Flags, bps, &chanid), _T("Failed to open channel."), pLineInfo); + write_ioctl(chanid, LOOPBACK, FALSE, LINE_INFO()); // DISABLE J2534 ECHO/LOOPBACK + return chanid; + } + + }; + + TEST_CLASS(J2534DeviceISO15765) + { + public: + + TEST_METHOD_INITIALIZE(init) { + LoadJ2534Dll("pandaJ2534_0404_32.dll"); + } + + TEST_METHOD_CLEANUP(deinit) { + if (didopen) { + PassThruClose(devid); + didopen = FALSE; + } + UnloadJ2534Dll(); + } + + //Test that the BAUD rate of a ISO15765 connection can be changed. + TEST_METHOD(J2534_ISO15765_SetBaud) + { + auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); // ENABLE J2534 ECHO/LOOPBACK + auto p = getPanda(250); + + J2534_send_msg_checked(chanid, ISO15765, 0, 0, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + write_ioctl(chanid, DATA_RATE, 250000, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, TX_INDICATION, 0, 4, 0, "\x0\x0\x3\xAB", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, TX_MSG_TYPE, 0, 4 + 2, 0, "\x0\x0\x3\xAB""HI", LINE_INFO()); + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x3AB, FALSE, FALSE, "\x2""HI", LINE_INFO()); + } + + ///////////////////// Tests checking things don't send/receive ///////////////////// + + //Check tx PASSES and rx FAIL WITHOUT a filter. 29 bit. NO Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_PassTxFailRx_29b_NoFilter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + auto p = getPanda(500); + + //TX: works because all single frame writes should work (with or without a flow contorl filter) + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + + //RX: Reads require a flow control filter, and should fail without one. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //Check tx and rx FAIL WITHOUT a filter. 29 bit. NO Filter. NoPadding. STD address. First Frame. + TEST_METHOD(J2534_ISO15765_FailTxRx_29b_NoFilter_NoPad_STD_FF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + auto p = getPanda(500); + + //TX + Assert::AreEqual(ERR_NO_FLOW_CONTROL, J2534_send_msg(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 12, 0, "\x18\xda\xef\xf1\xA1\xB2\xC3\xD4\xE5\xF6\x09\x1A"), + _T("Should fail to tx without a filter."), LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + //RX; Send full response and check didn't receive flow control from J2534 device + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x14\x49\x02\x01""1D4", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GP00R55", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""B123456", 8, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0);//Check a full message is not accepted. + } + + //Check tx PASSES and rx FAIL with a MISMATCHED filter. 29 bit. Mismatch Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_PassTxFailRx_29b_MismatchFilter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //TX: works because all single frame writes should work (with or without a flow contorl filter) + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 6, 0, "\x18\xda\xe0\xf1""\x11\x22", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xe0\xf1", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAE0F1, TRUE, FALSE, "\x02""\x11\x22", LINE_INFO()); + + //RX. Send ISO15765 single frame to device. Address still doesn't match filter, so should not be received. + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //Check tx and rx FAIL with a MISMATCHED filter. 29 bit. Mismatch Filter. NoPadding. STD address. First Frame. + TEST_METHOD(J2534_ISO15765_FailTxRx_29b_MismatchFilter_NoPad_STD_FF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //TX + Assert::AreEqual(ERR_NO_FLOW_CONTROL, J2534_send_msg(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 12, 0, "\x18\xda\xe0\xf1""USELESS STUFF"), + _T("Should fail to tx without a filter."), LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + //RX; Send a full response and check didn't receive flow control from J2534 device + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x10\x14\x49\x02\x01""1D4", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x21""GP00R55", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1E0, TRUE, "\x22""B123456", 8, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0);//Check a full message is not accepted. + } + + //Check tx FAILS with a MISMATCHED filter 29bit flag. 29 bit. Mismatch Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_FailTxRx_29b_MismatchFilterFlag29b_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO()); + auto p = getPanda(500); + + //TX + Assert::AreEqual(ERR_INVALID_MSG, J2534_send_msg(chanid, ISO15765, 0, 0, 0, 6, 0, "\x0/x0/x1/xcd\x01\x00"), + _T("mismatched address should fail to tx."), LINE_INFO()); + j2534_recv_loop(chanid, 0); + panda_recv_loop(p, 0); + + //RX. Send ISO15765 single frame to device. Address still doesn't match filter, so should not be received. + checked_panda_send(p, 0x1ab, FALSE, "\x06\x41\x00\xff\xff\xff\xfe", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + ///////////////////// Tests checking things actually send/receive. Standard Addressing ///////////////////// + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x07""ABCD123", 8, 0, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 11, 11, "\x18\xda\xf1\xef""ABCD123", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Single Frame. Loopback. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SF_LOOPBACK) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + } + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""nineteen bytes here", LINE_INFO()); + } + + //Check multi frame tx passes with filter. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 14, 0, "\x18\xda\xef\xf1""\xAA\xBB\xCC\xDD\xEE\xFF\x11\x22\x33\x44", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 0); // No TxDone msg until after the final tx frame is sent + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x0A""\xAA\xBB\xCC\xDD\xEE\xFF", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""\x11\x22\x33\x44", LINE_INFO()); + + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check rx passes with filter. 11 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_11b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, 0, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO()); + auto p = getPanda(500); + + checked_panda_send(p, 0x1ab, FALSE, "\x07""ABCD123", 8, 0, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, 0, 0, 11, 11, "\x0\x0\x1\xab""ABCD123", LINE_INFO()); + } + + //Check tx passes with filter. 11 bit. Good Filter. NoPadding. STD address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_11b_Filter_NoPad_STD_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, 0, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, 0, 4, "\xff\xff\xff\xff", "\x0\x0\x1\xab", "\x0\x0\x1\xcd", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, 0, 0, 11, 0, "\x0\x0\x1\xcd""TX_TEST", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, TX_INDICATION, 0, 4, 0, "\x0\x0\x1\xcd", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x1CD, FALSE, FALSE, "\x07""TX_TEST", LINE_INFO()); + } + + //Check tx passes with filter multiple times. 29 bit. Good Filter. NoPadding. STD address. Multiple Single Frames. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MultipleSF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 4); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[3], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); + } + + //Check that receiver's flow control block size requests are respected. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with multiple flow control. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLBlockSize) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x00", 3, 2, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 3, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check that receiver's flow control separation time requests are respected. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with multiple flow control. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLSTMinMultiFc) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x03\x0A", 3, 3, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + auto timediff0_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time; + auto timediff0_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time; + + std::ostringstream stringStream0; + stringStream0 << "times0: " << timediff0_1_0 << ", " << timediff0_2_1 << std::endl; + Logger::WriteMessage(stringStream0.str().c_str()); + + Assert::IsTrue(timediff0_1_0 > 10000); + Assert::IsTrue(timediff0_1_0 < 32000);//Flexible, but trying to make sure things don't just all lag for a second or something + Assert::IsTrue(timediff0_2_1 > 10000); + Assert::IsTrue(timediff0_2_1 < 32000); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x04\x20", 3, 4, LINE_INFO(), 500); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO()); + auto timediff1_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time; + auto timediff1_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time; + auto timediff1_3_2 = panda_msg_recv[3].recv_time - panda_msg_recv[2].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff1_1_0 << ", " << timediff1_2_1 << ", " << timediff1_3_2 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff1_1_0 > 32000); + Assert::IsTrue(timediff1_2_1 > 32000); + Assert::IsTrue(timediff1_3_2 > 32000); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check that receiver's flow control separation time requests are respected 2. 29 bit. Good Filter. NoPadding. STD address. Multiple Frames with one flow control. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_FLOWCONTROLSTMinSingleFc) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 52, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXXYYZZ", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x34""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x07\x0A", 3, 7, LINE_INFO(), 500); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[6], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ", LINE_INFO()); + + auto timediff_1_0 = panda_msg_recv[1].recv_time - panda_msg_recv[0].recv_time; + auto timediff_2_1 = panda_msg_recv[2].recv_time - panda_msg_recv[1].recv_time; + auto timediff_3_2 = panda_msg_recv[3].recv_time - panda_msg_recv[2].recv_time; + auto timediff_4_3 = panda_msg_recv[4].recv_time - panda_msg_recv[3].recv_time; + auto timediff_5_4 = panda_msg_recv[5].recv_time - panda_msg_recv[4].recv_time; + auto timediff_6_5 = panda_msg_recv[6].recv_time - panda_msg_recv[5].recv_time; + + std::ostringstream stringStream1; + stringStream1 << "times1: " << timediff_1_0 << ", " << timediff_2_1 << ", " << timediff_3_2 << + ", " << timediff_4_3 << ", " << timediff_5_4 << ", " << timediff_6_5 << std::endl; + Logger::WriteMessage(stringStream1.str().c_str()); + + Assert::IsTrue(timediff_1_0 > 10000); + Assert::IsTrue(timediff_2_1 > 10000); + Assert::IsTrue(timediff_3_2 > 10000); + Assert::IsTrue(timediff_4_3 > 10000); + Assert::IsTrue(timediff_5_4 > 10000); + Assert::IsTrue(timediff_6_5 > 10000); + } + + //Check that tx works for messages with more than 16 frames. 29 bit. Good Filter. NoPadding. STD address. Large multiframe message. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MF_LotsOfFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 125, 0, + "\x18\xda\xef\xf1" + "AABBCC""DDEEFFG""GHHIIJJ""KKLLMMN""NOOPPQQ""RRSSTTU""UVVWWXX""YYZZ112""2334455""6677889" + "900abcd""efghijk""lmnopqr""stuvwxy""z!@#$%^""&*()_+-""=`~ABCD""EFGHIJK", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x7D""AABBCC", LINE_INFO()); + + // [flow_status, block_size, st_min] + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 17, LINE_INFO(), 1000); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[6], 0, 0x18DAEFF1, TRUE, FALSE, "\x27""YYZZ112", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[7], 0, 0x18DAEFF1, TRUE, FALSE, "\x28""2334455", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[8], 0, 0x18DAEFF1, TRUE, FALSE, "\x29""6677889", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[9], 0, 0x18DAEFF1, TRUE, FALSE, "\x2A""900abcd", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[10], 0, 0x18DAEFF1, TRUE, FALSE, "\x2B""efghijk", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[11], 0, 0x18DAEFF1, TRUE, FALSE, "\x2C""lmnopqr", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[12], 0, 0x18DAEFF1, TRUE, FALSE, "\x2D""stuvwxy", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[13], 0, 0x18DAEFF1, TRUE, FALSE, "\x2E""z!@#$%^", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[14], 0, 0x18DAEFF1, TRUE, FALSE, "\x2F""&*()_+-", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[15], 0, 0x18DAEFF1, TRUE, FALSE, "\x20""=`~ABCD", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[16], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""EFGHIJK", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + } + + //Check tx passes with filter multiple times. 29 bit. Good Filter. NoPadding. STD address. Multiple Single Frames. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MultipleMFSF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x00\x00", 3, 4, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""ata bec", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""ause I ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""can", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 4); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[2], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[3], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + } + + //Check tx passes after message timeout. 29 bit. Good Filter. NoPadding. STD address. Multiple Frame timeout then Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFTimeoutSFSuccess) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2, 1000); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); //First Frame. Not replying so it needs to time out. + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); //Reply to the next message. + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + } + + //Check tx passes after mid-message timeout. 29 bit. Good Filter. NoPadding. STD address. Multiple Frame mid-timeout then Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFMidTimeoutSFSuccess) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 23, 0, "\x18\xda\xef\xf1""Long data because I can", LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 9, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x17""Long d", LINE_INFO()); //First Frame. Not replying so it needs to time out. + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x01\x00", 3, 2, LINE_INFO(), 1000);//Start a conversation + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""ata bec", LINE_INFO());//Check passthru device sent more data, but don't reply to it + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x05""HELLO", LINE_INFO()); //Reply to the next message. + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 5, 0, "\x18\xda\xef\xf1""HELLO", LINE_INFO()); + } + + //Check slow tx passes without hitting FC timeout. 29 bit. Good Filter. NoPadding. STD address. Long STmin, catches if FC timeout applies before needed. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SLOWMF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x7F", 3, 6, LINE_INFO(), 3000);//Start a conversation... but slow. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO());//Check this convo doesn't trigger that timeout. + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[4], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue. + check_panda_can_msg(panda_msg_recv[5], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + } + + //Check MF tx can be sent along side of a periodic message. 29 bit. Good Filter. NoPadding. STD address. Long STmin, checks that MF tx and periodic TX don't break each other. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_SLOWMF_WithPeriodicMsg) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + //Timing diagram of this test. + //* is a periodic msg transfer; F is first frame, L is Flow control, C is Consecutive Frame. + // *~~~~~~~*~~~~~~~*~~~~~~~* (The alignment here is unimportant. The exact order is not checked. + //F C----C----C----C----C----C (100 ms between Cs) + // L + + auto msgid = J2534_start_periodic_msg_checked(chanid, ISO15765, CAN_29BIT_ID, 6, 0, "\x18\xda\xef\xf1""HI", 130, LINE_INFO()); + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x02""HI", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + Assert::IsTrue(p->can_send(0x18DAF1EF, TRUE, (const uint8_t*)"\x30\x06\x64", 3, panda::PANDA_CAN1), _T("Panda send says it failed."), LINE_INFO()); + + Timer t_permsg = Timer(); + Timer t_MFmsg = Timer(); + unsigned int MFframesReceived = 0; + unsigned int PeriodicMsgReceived = 1; //Because of the first panda_recv_loop above. + std::array const mfMsgExpectedParts{ "\x21""DDEEFFG", "\x22""GHHIIJJ", "\x23""KKLLMMN", "\x24""NOOPPQQ", "\x25""RRSSTTU", "\x26""UVVWWXX" }; + + while (TRUE) { + std::vectormsg_recv = p->can_recv(); + for (auto msg : msg_recv) { + if (msg.is_receipt) continue; + if ((msg.dat[0] & 0xf0) == 0x20) { + Assert::AreEqual(mfMsgExpectedParts[MFframesReceived], std::string((const char*)msg.dat, msg.len), _T("Got wrong part of MF msg."), LINE_INFO()); + MFframesReceived++; + t_MFmsg.reset(); + } else if (std::string((const char*)msg.dat, msg.len) == "\x02HI") { + PeriodicMsgReceived++; + t_permsg.reset(); + } else { + Assert::IsTrue(FALSE, _T("Got impossible message. Something is very wrong. Check other tests."), LINE_INFO()); + } + } + + if (MFframesReceived >= 6) break; + Assert::IsTrue(300 > t_permsg.getTimePassed(), _T("Timed out waiting for periodic msessage frame."), LINE_INFO()); + Assert::IsTrue(300 > t_MFmsg.getTimePassed(), _T("Timed out waiting for multiframe msessage frame."), LINE_INFO()); + + if (msg_recv.size() == 0) + Sleep(10); + } + + //Stop the periodic message and grab any data it may have sent since we last checked. + //Not sure if this is needed. + Assert::AreEqual(STATUS_NOERROR, PassThruStopPeriodicMsg(chanid, msgid), _T("Failed to delete filter."), LINE_INFO()); + auto extra_panda_msg = panda_recv_loop_loose(p, 0, 200); + for (auto msg : extra_panda_msg) { + if (std::string((const char*)msg.dat, msg.len) == "\x02HI") { + PeriodicMsgReceived++; + Logger::WriteMessage("Received extra periodic message."); + } else { + Assert::IsTrue(FALSE, _T("Got impossible message. Something is very wrong. Check other tests."), LINE_INFO()); + } + } + + Assert::IsTrue(PeriodicMsgReceived > 3, _T("Did not receive enough periodic messages. Likely canceled or delayed."), LINE_INFO()); + + std::ostringstream stringStream; + stringStream << "PeriodicMsgReceived = " << PeriodicMsgReceived << std::endl; + Logger::WriteMessage(stringStream.str().c_str()); + + unsigned int periodicTxIndicationCount = 0; + unsigned int TxIndicationCount = 0; + auto j2534_msg_recv = j2534_recv_loop(chanid, 2 + (PeriodicMsgReceived * 2)); + for (int i = 0; i < PeriodicMsgReceived + 1; i++) { + check_J2534_can_msg(j2534_msg_recv[(i * 2) + 0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + switch (j2534_msg_recv[(i * 2) + 1].DataSize) { + case 4 + 2: + check_J2534_can_msg(j2534_msg_recv[(i * 2) + 1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 2, 0, "\x18\xda\xef\xf1""HI", LINE_INFO()); + break; + case 4 + 48: + check_J2534_can_msg(j2534_msg_recv[(i * 2) + 1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + break; + default: + Assert::IsTrue(FALSE, _T("Got unexpected data!"), LINE_INFO()); + } + } + + Assert::AreNotEqual(PeriodicMsgReceived, periodicTxIndicationCount, _T("Wrong number of periodic msgs reported by passthru device."), LINE_INFO()); + Assert::AreNotEqual(1, TxIndicationCount, _T("Wrong number of TX msgs reported by passthru device."), LINE_INFO()); + } + + ///////////////////// Tests checking things break or recover during send/receive ///////////////////// + + //Check rx FAILS when frame is dropped. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRx_29b_Filter_NoPad_STD_FFCF_DropFrame) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + //Missing the 2nd frame "\x21""en byte" + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO()); + + //Check J2534 DOES NOT construct the incomplete message + j2534_recv_loop(chanid, 0); + } + + //Check rx ignores frames that arrive out of order. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_FFCF_FrameNumSkip) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""XXXXXX", 7, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x23""ZZZZZZ", 7, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + + //Check J2534 constructa the complete message from the correctly numbered frames. + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""ABCDEFGHIJKLMNOPQRS", LINE_INFO()); + } + + //Check Single Frame rx RESETS ongoing multiframe transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_SFRxResetsMFRx) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the next part of the message multi message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + + //ABORTING MESSAGE + //Send a NEW single frame message and check the J2534 device gets it (but not the original message it was receiving. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x06""ABC123", 7, 0, LINE_INFO()); + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 10, 10, "\x18\xda\xf1\xef""ABC123", LINE_INFO()); + + //Resume sending the old message, and check th eJ2534 device didn't get a message. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //The documentation says that a s ingle channel can not send and receive messages trhough a + //single conversation (flow control filter) at the same time. However, the required behavior + //when this is detected is not described. This test was my best understanding of how it was + //wanted, but I no longer see the point. For now I am disabling it. + /*//Check Single Frame tx RESETS ongoing multiframe rx transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_SFTxResetsMFRx) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the next part of the message multi message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + + //ABORTING MESSAGE + //Send a NEW single frame message and check the J2534 device gets it (but not the original message it was receiving. + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 11, 0, "\x18\xda\xef\xf1""TX_TEST", LINE_INFO()); + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + + panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x07""TX_TEST", LINE_INFO()); + /////////////////////////// + + //Resume sending the old message, and check th eJ2534 device didn't get a message. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + }*/ + + //TODO check rx is cleared by tx (multi). Or not.... read above note. + + //Check multiframe rx RESETS ongoing multiframe transmission. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_PassRx_29b_Filter_NoPad_STD_FFCF_MFRxResetsMFRx) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ABCDEF", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the next part of the multi message A + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""GHIJKLM", 8, 0, LINE_INFO()); + + //ABORTING MESSAGE A + //Send a NEW multi frame message (B) and check the J2534 device gets it (but not the original message it was receiving. + //Send first frame, then check we get a flow control frame + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s here", 7, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID, 0, 4 + 0x13, 4 + 0x13, "\x18\xda\xf1\xef""nineteen bytes here", LINE_INFO()); + //////////////////////// End sending B + + //Resume sending the multi message A, and check th eJ2534 device didn't get a message. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""NOPQRS", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + } + + //Check rx fails gracefully if final CF of MF rx is too short. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRxFinalCFTooShort_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninete", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x30\x00\x00", 3), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE, 0, 4, 0, "\x18\xda\xf1\xef", LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""en byte", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""s her", 6, 0, LINE_INFO()); //The transaction should reset here because more data could have been sent. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x23""e", 2, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 0); + } + + //Check rx fails gracefully if first frame is too short. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRxFFTooShort_29b_Filter_NoPad_STD_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame. The transaction should reset immediately because more data could have been sent in this frame. + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x10\x13""ninet", 7, 0, LINE_INFO()); + j2534_recv_loop(chanid, 0); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x21""een byt", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x22""es here", 8, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_recv_loop(chanid, 0); + } + + //Check MF tx will stop upon receiving a flow control ABORT. 29 bit. Good Filter. NoPadding. STD address. Large STmin, then abort, then send SF. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD__MF_FCAbort_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x20", 3, 2, LINE_INFO());//Start a conversation. FC timeout is 32 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x32\x0\x0", 3, 0, LINE_INFO());//Abort the conversation + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + } + + //Check MF tx will stop upon receiving a flow control ABORT during valid blocksize. 29 bit. Good Filter. NoPadding. STD address. Large STmin, then mid tx abort, then send SF. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD__MF_FCMixTXAbort_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x7F", 3, 1, LINE_INFO(), 200);//Start a conversation. FC timeout is 127 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x32\x0\x0", 3, 0, LINE_INFO());//Abort the conversation + panda_recv_loop(p, 0, 200); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + } + + //Check slow tx can be stalled past timeout with CF WAIT frames. 29 bit. Good Filter. NoPadding. STD address. MF tx that would timeout without WAIT frames. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFWithWaitFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + write_ioctl(chanid, ISO15765_WFT_MAX, 10, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x40", 3, 2, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 4, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue. + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + } + + //Check slow tx can be stalled past timeout with CF WAIT frames during normal TX. 29 bit. Good Filter. NoPadding. STD address. Stalling working MF tx. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFWithMidTXWaitFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + write_ioctl(chanid, ISO15765_WFT_MAX, 10, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x06\x64", 3, 2, LINE_INFO(), 120);//Start a conversation. STmin 100. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 4, LINE_INFO(), 3000);//Start a conversation. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[2], 0, 0x18DAEFF1, TRUE, FALSE, "\x25""RRSSTTU", LINE_INFO());//Some of these should fail to recv if there is an issue. + check_panda_can_msg(panda_msg_recv[3], 0, 0x18DAEFF1, TRUE, FALSE, "\x26""UVVWWXX", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + } + + //Check that too many WAIT frames will abort the transfer. 29 bit. Good Filter. NoPadding. STD address. Too much stalling causes abort. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_STD_MFTooManyWaitFrames) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID, 4, "\xff\xff\xff\xff", "\x18\xda\xf1\xef", "\x18\xda\xef\xf1", LINE_INFO()); + write_ioctl(chanid, LOOPBACK, TRUE, LINE_INFO()); + write_ioctl(chanid, ISO15765_WFT_MAX, 2, LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 48, 0, "\x18\xda\xef\xf1""AABBCCDDEEFFGGHHIIJJKKLLMMNNOOPPQQRRSSTTUUVVWWXX", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x10\x30""AABBCC", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x64", 3, 2, LINE_INFO(), 120);//Start a conversation. STmin 100. FC timeout is 250 ms. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x21""DDEEFFG", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x22""GHHIIJJ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x02\x64", 3, 2, LINE_INFO(), 120);//Resume the conversation. + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x23""KKLLMMN", LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, "\x24""NOOPPQQ", LINE_INFO()); + + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x31\x0\x0", 3, 0, LINE_INFO(), 100);//Delay the conversation. + Sleep(100); + + //Should not resume because the conversation has been delayed too long. + panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x30\x0\x0", 3, 0, LINE_INFO(), 300); + + //Send a SF message to check the tubes are not clogged. + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 2); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION, 0, 4, 0, "\x18\xda\xef\xf1", LINE_INFO()); + check_J2534_can_msg(j2534_msg_recv[1], ISO15765, CAN_29BIT_ID | TX_MSG_TYPE, 0, 4 + 4, 0, "\x18\xda\xef\xf1""SUP!", LINE_INFO()); + } + + ///////////////////// Tests checking things actually send/receive. Ext 5 byte Addressing ///////////////////// + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_EXT_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x06""ABC123", 8, 0, LINE_INFO()); + + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 11, 11, "\x18\xda\xf1\xef\x13""ABC123", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Single Frame. + TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_EXT_SF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + J2534_send_msg_checked(chanid, ISO15765, 0, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 11, 0, "\x18\xda\xef\xf1\x13""DERP!!", LINE_INFO()); + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | TX_INDICATION | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xef\xf1\x13", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, "\x13""\x06""DERP!!", LINE_INFO()); + } + + //Check rx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Multi Frame. + TEST_METHOD(J2534_ISO15765_SuccessRx_29b_Filter_NoPad_EXT_FFCF) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + Assert::IsTrue(p->can_send(0x18DAF1EF, TRUE, (const uint8_t*)"\x13""\x10\x13""ninet", 8, panda::PANDA_CAN1), _T("Panda send says it failed."), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xf1\xef\x13", LINE_INFO()); + + auto panda_msg_recv = panda_recv_loop(p, 2); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAF1EF, TRUE, TRUE, std::string("\x13""\x10\x13""ninet", 8), LINE_INFO()); + check_panda_can_msg(panda_msg_recv[1], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x13""\x30\x00\x00", 4), LINE_INFO()); + + //Send the rest of the message + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x21""een by", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x22""tes he", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x23""re", 8, 0, LINE_INFO()); + + //Check J2534 constructed the whole message + j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 0, 5 + 0x13, 5 + 0x13, "\x18\xda\xf1\xef\x13""nineteen bytes here", LINE_INFO()); + } + + //Check tx passes with filter. 29 bit. Good Filter. NoPadding. EXT address. Multi Frame. + /*TEST_METHOD(J2534_ISO15765_SuccessTx_29b_Filter_NoPad_EXT_FFCF) + { //TODO when TX works with flow control}*/ + + ///////////////////// Tests checking things break or recover during send/receive. Ext 5 byte Addressing ///////////////////// + + //Check rx FAILS when frame is dropped. 29 bit. Good Filter. NoPadding. STD address. Multi Frame. + TEST_METHOD(J2534_ISO15765_FailRx_29b_Filter_NoPad_EXT_FFCF_DropFrame) + { + auto chanid = J2534_open_and_connect("", ISO15765, CAN_29BIT_ID, 500000, LINE_INFO()); + J2534_set_flowctrl_filter(chanid, CAN_29BIT_ID | ISO15765_ADDR_TYPE, 5, "\xff\xff\xff\xff\xff", "\x18\xda\xf1\xef\x13", "\x18\xda\xef\xf1\x13", LINE_INFO()); + auto p = getPanda(500); + + //Send first frame, then check we get a flow control frame + auto panda_msg_recv = checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13\x10\x13""ninet", 8, 1, LINE_INFO()); + check_panda_can_msg(panda_msg_recv[0], 0, 0x18DAEFF1, TRUE, FALSE, std::string("\x13\x30\x00\x00", 4), LINE_INFO()); + + //Check first frame is registered with J2534 + auto j2534_msg_recv = j2534_recv_loop(chanid, 1); + check_J2534_can_msg(j2534_msg_recv[0], ISO15765, CAN_29BIT_ID | START_OF_MESSAGE | ISO15765_ADDR_TYPE, 0, 5, 0, "\x18\xda\xf1\xef\x13", LINE_INFO()); + + //Send the rest of the message + //Missing the 2nd frame "\x13""\x21""een by" + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x22""tes he", 8, 0, LINE_INFO()); + checked_panda_send(p, 0x18DAF1EF, TRUE, "\x13""\x23""re", 8, 0, LINE_INFO()); + + //Check J2534 DOES NOT construct the incomplete message + j2534_recv_loop(chanid, 0); + } + + bool didopen = FALSE; + unsigned long devid; + + unsigned long open_dev(const char* name, long assert_err = STATUS_NOERROR, TCHAR* failmsg = _T("Failed to open device.")) { + unsigned int res = PassThruOpen((void*)name, &devid); + if (res == STATUS_NOERROR) didopen = TRUE; + return res; + } + + unsigned long J2534_open_and_connect(const char* name, unsigned long ProtocolID, unsigned long Flags, unsigned long bps, const __LineInfo* pLineInfo = NULL) { + unsigned long chanid; + Assert::AreEqual(STATUS_NOERROR, open_dev(name), _T("Failed to open device."), pLineInfo); + Assert::AreEqual(STATUS_NOERROR, PassThruConnect(devid, ProtocolID, Flags, bps, &chanid), _T("Failed to open channel."), pLineInfo); + write_ioctl(chanid, LOOPBACK, FALSE, LINE_INFO()); // DISABLE J2534 ECHO/LOOPBACK + return chanid; + } + + }; +} diff --git a/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj new file mode 100644 index 0000000..5692382 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj @@ -0,0 +1,125 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {7912F978-B48C-4C5D-8BFD-5D1E22158E47} + Win32Proj + pandaJ2534DLLTest + 8.1 + Tests + + + + DynamicLibrary + true + v140 + Unicode + false + + + DynamicLibrary + false + v140 + true + Unicode + false + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir) + WIN32;_DEBUG;%(PreprocessorDefinitions) + true + + + Windows + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + Level3 + Use + MaxSpeed + true + true + $(VCInstallDir)UnitTest\include;%(AdditionalIncludeDirectories);$(SolutionDir) + WIN32;NDEBUG;%(PreprocessorDefinitions) + true + + + Windows + true + true + $(VCInstallDir)UnitTest\lib;%(AdditionalLibraryDirectories) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + {96e0e646-ee76-444d-9a77-a0cd7f781deb} + + + {a2bb18a5-f26b-48d6-bbb5-b83d64473c77} + + + {5528aefb-638d-49af-b9d4-965154e7d531} + + + + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters new file mode 100644 index 0000000..476ce45 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/pandaJ2534DLL Test.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp b/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp new file mode 100644 index 0000000..65e549c --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/panda_tests.cpp @@ -0,0 +1,187 @@ +#include "stdafx.h" +#include "panda/panda.h" +#include "TestHelpers.h" + +#include + +using namespace Microsoft::VisualStudio::CppUnitTestFramework; +using namespace panda; + +namespace pandaTestNative +{ + TEST_CLASS(DeviceDiscovery) + { + public: + + TEST_METHOD(Panda_DevDiscover_ListDevices) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + for (auto sn : pandas_available) { + Assert::IsTrue(sn.size() == 24, _T("panda Serial Number not 24 characters long.")); + } + } + + TEST_METHOD(Panda_DevDiscover_OpenFirstDevice) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(pandas_available[0]); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + } + + TEST_METHOD(Panda_DevDiscover_OpenDeviceNoName) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(""); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + Assert::IsTrue(p1->get_usb_sn() == pandas_available[0], _T("Could not open panda.")); + } + + TEST_METHOD(Panda_DevDiscover_OpenDeviceUnavailable) + { + auto p1 = Panda::openPanda("ZZZZZZZZZZZZZZZZZZZZZZZZ"); + Assert::IsTrue(p1 == nullptr, _T("Invalid sn still worked.")); + } + + TEST_METHOD(Panda_DevDiscover_WillNotOpenAlreadyOpenedDevice) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(pandas_available[0]); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + + auto p2 = Panda::openPanda(pandas_available[0]); + Assert::IsTrue(p2 == nullptr, _T("Opened an already open panda.")); + } + + TEST_METHOD(Panda_DevDiscover_OpenedDeviceNotListed) + { + auto pandas_available = Panda::listAvailablePandas(); + Assert::IsTrue(pandas_available.size() > 0, _T("No pandas were found.")); + + auto p1 = Panda::openPanda(pandas_available[0]); + Assert::IsFalse(p1 == nullptr, _T("Could not open panda.")); + + auto pandas_available2 = Panda::listAvailablePandas(); + for (auto sn : pandas_available2) { + Assert::IsFalse(p1->get_usb_sn() == sn, _T("Opened panda appears in list of available pandas.")); + } + + } + }; + + TEST_CLASS(CANOperations) + { + public: + + TEST_METHOD(Panda_CAN_Echo) + { + auto p0 = getPanda(500, TRUE); + + uint32_t addr = 0xAA; + bool is_29b = FALSE; + uint8_t candata[8]; + + for (auto canbus : { PANDA_CAN1, PANDA_CAN2, PANDA_CAN3 }) { + uint8_t len = (rand() % 8) + 1; + for (size_t i = 0; i < len; i++) + candata[i] = rand() % 256; + + p0->can_send(addr, is_29b, candata, len, canbus); + Sleep(10); + + auto can_msgs = p0->can_recv(); + + Assert::AreEqual(2, can_msgs.size(), _T("Received the wrong number of CAN messages."), LINE_INFO()); + + for (auto msg : can_msgs) { + Assert::IsTrue(msg.addr == addr, _T("Wrong addr.")); + Assert::IsTrue(msg.bus == canbus, _T("Wrong bus.")); + Assert::IsTrue(msg.len == len, _T("Wrong len.")); + Assert::AreEqual(memcmp(msg.dat, candata, msg.len), 0, _T("Received CAN data not equal")); + for (int i = msg.len; i < 8; i++) + Assert::IsTrue(msg.dat[i] == 0, _T("Received CAN data not trailed by 0s")); + } + + Assert::IsTrue(can_msgs[0].is_receipt, _T("Didn't get receipt.")); + Assert::IsFalse(can_msgs[1].is_receipt, _T("Didn't get echo.")); + } + } + + TEST_METHOD(Panda_CAN_ChangeBaud) + { + auto p0 = getPanda(250); + auto p1 = getPanda(500); + + p0->can_send(0xAA, FALSE, (const uint8_t*)"\x1\x2\x3\x4\x5\x6\x7\x8", 8, panda::PANDA_CAN1); + panda_recv_loop(p0, 0); + panda_recv_loop(p1, 0); + + p0->set_can_speed_kbps(panda::PANDA_CAN1, 500); + + auto panda_msg_recv = panda_recv_loop(p0, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xAA, FALSE, TRUE, "\x1\x2\x3\x4\x5\x6\x7\x8", LINE_INFO()); + panda_msg_recv = panda_recv_loop(p1, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xAA, FALSE, FALSE, "\x1\x2\x3\x4\x5\x6\x7\x8", LINE_INFO()); + + ////////////////// + + p0->set_can_speed_kbps(panda::PANDA_CAN1, 250); + p0->can_send(0xC4, FALSE, (const uint8_t*)"\xA\B\xC\xD\xE\xF\x10\x11", 8, panda::PANDA_CAN1); + panda_recv_loop(p0, 0); + panda_recv_loop(p1, 0); + + p1->set_can_speed_kbps(panda::PANDA_CAN1, 250); + + panda_msg_recv = panda_recv_loop(p0, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xC4, FALSE, TRUE, "\xA\B\xC\xD\xE\xF\x10\x11", LINE_INFO()); + panda_msg_recv = panda_recv_loop(p1, 1); + check_panda_can_msg(panda_msg_recv[0], 0, 0xC4, FALSE, FALSE, "\xA\B\xC\xD\xE\xF\x10\x11", LINE_INFO()); + } + + TEST_METHOD(Panda_CAN_ClearClears) + { + auto p0 = getPanda(500, TRUE); + p0->can_send(0xAA, FALSE, (const uint8_t*)"\x0\x1\x2\x3\x4\x5\x6\x7", 8, panda::PANDA_CAN1); + Sleep(100); + p0->can_clear(PANDA_CAN_RX); + + auto can_msgs = p0->can_recv(); + Assert::IsTrue(can_msgs.size() == 0, _T("Received messages after a clear.")); + } + }; + + TEST_CLASS(SerialOperations) + { + public: + + TEST_METHOD(Panda_LIN_Echo) + { + auto p0 = getPanda(500); + + for (auto lin_port : { SERIAL_LIN1, SERIAL_LIN2 }) { + p0->serial_clear(lin_port); + + for (int i = 0; i < 10; i++) { + uint8_t len = (rand() % LIN_MSG_MAX_LEN) + 1; + std::string lindata; + lindata.reserve(len); + + for (size_t j = 0; j < len; j++) + lindata += (const char)(rand() % 256); + + p0->serial_write(lin_port, lindata.c_str(), len); + Sleep(10); + + auto retdata = p0->serial_read(lin_port); + Assert::AreEqual(retdata, lindata); + } + } + } + }; +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL Test/stdafx.cpp b/drivers/windows/pandaJ2534DLL Test/stdafx.cpp new file mode 100644 index 0000000..84a1f0a --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pandaJ2534DLL Test.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/pandaJ2534DLL Test/stdafx.h b/drivers/windows/pandaJ2534DLL Test/stdafx.h new file mode 100644 index 0000000..1ac8bd8 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/stdafx.h @@ -0,0 +1,14 @@ +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include + +// Headers for CppUnitTest +#include "CppUnitTest.h" +#include //Used for formatting in TestHelpers.cpp +#include +#include diff --git a/drivers/windows/pandaJ2534DLL Test/targetver.h b/drivers/windows/pandaJ2534DLL Test/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL Test/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/pandaJ2534DLL/Action.h b/drivers/windows/pandaJ2534DLL/Action.h new file mode 100644 index 0000000..e9721c2 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/Action.h @@ -0,0 +1,57 @@ +#pragma once +#include + +#include "J2534Frame.h" + +class J2534Connection; + +/** +An Action represents a unit of work that can be scheduled for execution at a later time. +Actions are not guaranteed to be run at their specified time, but a best effort is made. +An Action will never execute early, but can execute later depending on what is in the +queus. +Many different operations are based on this base class. Instead of making a thread, +consider if the work can be offloaded to the Task Queue. +*/ +class Action +{ +public: + Action( + std::weak_ptr connection, + std::chrono::microseconds delay + ) : connection(connection), delay(delay) { }; + + Action( + std::weak_ptr connection + ) : connection(connection), delay(std::chrono::microseconds(0)) { }; + + //The function called by the task runner when this action is to be invoked. + virtual void execute() = 0; + + //Reschedule this Action for now(). + void scheduleImmediate() { + expire = std::chrono::steady_clock::now(); + } + + //Reschedule this Action relative to its last expiration time. + void scheduleDelay() { + expire += this->delay; + } + + //Reschedule this action {delay} after now(). + void scheduleImmediateDelay() { + expire = std::chrono::steady_clock::now() + this->delay; + } + + //Reschedule this Action based on a specific base time. + void schedule(std::chrono::time_point starttine, BOOL adddelayed) { + this->expire = starttine; + if (adddelayed) + expire += this->delay; + } + + std::weak_ptr connection; + std::chrono::microseconds delay; + //The timestamp at which point this Action is ready to be executed. + std::chrono::time_point expire; +}; diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp new file mode 100644 index 0000000..358158b --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp @@ -0,0 +1,257 @@ +#include "stdafx.h" +#include "J2534Connection.h" +#include "Timer.h" + +J2534Connection::J2534Connection( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate +) : panda_dev(panda_dev), ProtocolID(ProtocolID), Flags(Flags), BaudRate(BaudRate), port(0) { } + +unsigned long J2534Connection::validateTxMsg(PASSTHRU_MSG* msg) { + if (msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen()) + return ERR_INVALID_MSG; + return STATUS_NOERROR; +} + +long J2534Connection::PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) { + //Timeout of 0 means return immediately. Non zero means WAIT for that time then return. Dafuk. + long err_code = STATUS_NOERROR; + Timer t = Timer(); + + unsigned long msgnum = 0; + while (msgnum < *pNumMsgs) { + if (Timeout > 0 && t.getTimePassed() >= Timeout) { + err_code = ERR_TIMEOUT; + break; + } + + //Synchronized won't work where we have to break out of a loop + messageRxBuff_mutex.lock(); + if (this->messageRxBuff.empty()) { + messageRxBuff_mutex.unlock(); + if (Timeout == 0) + break; + continue; + } + + auto msg_in = this->messageRxBuff.front(); + this->messageRxBuff.pop(); + messageRxBuff_mutex.unlock(); + + PASSTHRU_MSG *msg_out = &pMsg[msgnum++]; + msg_out->ProtocolID = this->ProtocolID; + msg_out->DataSize = msg_in.Data.size(); + memcpy(msg_out->Data, msg_in.Data.c_str(), msg_in.Data.size()); + msg_out->Timestamp = msg_in.Timestamp; + msg_out->RxStatus = msg_in.RxStatus; + msg_out->ExtraDataIndex = msg_in.ExtraDataIndex; + msg_out->TxFlags = 0; + if (msgnum == *pNumMsgs) break; + } + + if (msgnum == 0) + err_code = ERR_BUFFER_EMPTY; + *pNumMsgs = msgnum; + return err_code; +} + +long J2534Connection::PassThruWriteMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) { + //There doesn't seem to be much reason to implement the timeout here. + for (int msgnum = 0; msgnum < *pNumMsgs; msgnum++) { + PASSTHRU_MSG* msg = &pMsg[msgnum]; + if (msg->ProtocolID != this->ProtocolID) { + *pNumMsgs = msgnum; + return ERR_MSG_PROTOCOL_ID; + } + + auto retcode = this->validateTxMsg(msg); + if (retcode != STATUS_NOERROR) { + *pNumMsgs = msgnum; + return retcode; + } + + auto msgtx = this->parseMessageTx(*pMsg); + if (msgtx != nullptr) //Nullptr is supported for unimplemented connection types. + this->schedultMsgTx(std::dynamic_pointer_cast(msgtx)); + } + return STATUS_NOERROR; +} + +//The docs say that a device has to support 10 periodic messages, though more is ok. +//It is easier to store them on the connection, so 10 per connection it is. +long J2534Connection::PassThruStartPeriodicMsg(PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval) { + if (pMsg->DataSize < getMinMsgLen() || pMsg->DataSize > getMaxMsgSingleFrameLen()) return ERR_INVALID_MSG; + if (pMsg->ProtocolID != this->ProtocolID) return ERR_MSG_PROTOCOL_ID; + if (TimeInterval < 5 || TimeInterval > 65535) return ERR_INVALID_TIME_INTERVAL; + + for (int i = 0; i < this->periodicMessages.size(); i++) { + if (periodicMessages[i] != nullptr) continue; + + *pMsgID = i; + auto msgtx = this->parseMessageTx(*pMsg); + if (msgtx != nullptr) { + periodicMessages[i] = std::make_shared(std::chrono::microseconds(TimeInterval*1000), msgtx); + periodicMessages[i]->scheduleImmediate(); + if (auto panda_dev = this->getPandaDev()) { + panda_dev->insertActionIntoTaskList(periodicMessages[i]); + } + } + return STATUS_NOERROR; + } + return ERR_EXCEEDED_LIMIT; +} + +long J2534Connection::PassThruStopPeriodicMsg(unsigned long MsgID) { + if (MsgID >= this->periodicMessages.size() || this->periodicMessages[MsgID] == nullptr) + return ERR_INVALID_MSG_ID; + this->periodicMessages[MsgID]->cancel(); + this->periodicMessages[MsgID] = nullptr; + return STATUS_NOERROR; +} + +long J2534Connection::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) { + for (int i = 0; i < this->filters.size(); i++) { + if (filters[i] == nullptr) { + try { + auto newfilter = std::make_shared(this, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg); + for (int check_idx = 0; check_idx < filters.size(); check_idx++) { + if (filters[check_idx] == nullptr) continue; + if (filters[check_idx] == newfilter) { + filters[i] = nullptr; + return ERR_NOT_UNIQUE; + } + } + *pFilterID = i; + filters[i] = newfilter; + return STATUS_NOERROR; + } catch (int e) { + return e; + } + } + } + return ERR_EXCEEDED_LIMIT; +} + +long J2534Connection::PassThruStopMsgFilter(unsigned long FilterID) { + if (FilterID >= this->filters.size() || this->filters[FilterID] == nullptr) + return ERR_INVALID_FILTER_ID; + this->filters[FilterID] = nullptr; + return STATUS_NOERROR; +} + +long J2534Connection::PassThruIoctl(unsigned long IoctlID, void *pInput, void *pOutput) { + return STATUS_NOERROR; +} + +long J2534Connection::init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput) { return STATUS_NOERROR; } +long J2534Connection::initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput) { return STATUS_NOERROR; } +long J2534Connection::clearTXBuff() { return STATUS_NOERROR; } +long J2534Connection::clearRXBuff() { + synchronized(messageRxBuff_mutex) { + this->messageRxBuff = {}; + } + return STATUS_NOERROR; +} +long J2534Connection::clearPeriodicMsgs() { return STATUS_NOERROR; } +long J2534Connection::clearMsgFilters() { + for (auto& filter : this->filters) filter = nullptr; + return STATUS_NOERROR; +} + +void J2534Connection::setBaud(unsigned long baud) { + this->BaudRate = baud; +} + +void J2534Connection::schedultMsgTx(std::shared_ptr msgout) { + if (auto panda_ps = this->panda_dev.lock()) { + synchronized(staged_writes_lock) { + this->txbuff.push(msgout); + panda_ps->registerConnectionTx(shared_from_this()); + } + } +} + +void J2534Connection::rescheduleExistingTxMsgs() { + if (auto panda_ps = this->panda_dev.lock()) { + synchronized(staged_writes_lock) { + panda_ps->unstallConnectionTx(shared_from_this()); + } + } +} + +//Works well as long as the protocol doesn't support flow control. +void J2534Connection::processMessage(const J2534Frame& msg) { + FILTER_RESULT filter_res = FILTER_RESULT_NEUTRAL; + + for (auto filter : this->filters) { + if (filter == nullptr) continue; + FILTER_RESULT current_check_res = filter->check(msg); + if (current_check_res == FILTER_RESULT_BLOCK) return; + if (current_check_res == FILTER_RESULT_PASS) filter_res = FILTER_RESULT_PASS; + } + + if (filter_res == FILTER_RESULT_PASS) { + addMsgToRxQueue(msg); + } +} + +void J2534Connection::processIOCTLSetConfig(unsigned long Parameter, unsigned long Value) { + switch (Parameter) { + case DATA_RATE: // 5-500000 + this->setBaud(Value); + case LOOPBACK: // 0 (OFF), 1 (ON) [0] + this->loopback = (Value != 0); + break; + case ISO15765_WFT_MAX: + break; + case NODE_ADDRESS: // J1850PWM Related (Not supported by panda). HDS requires these to 'work'. + case NETWORK_LINE: + case P1_MIN: // A bunch of stuff relating to ISO9141 and ISO14230 that the panda + case P1_MAX: // currently doesn't support. Don't let HDS know we can't use these. + case P2_MIN: + case P2_MAX: + case P3_MIN: + case P3_MAX: + case P4_MIN: + case P4_MAX: + case W0: + case W1: + case W2: + case W3: + case W4: + case W5: + case TIDLE: + case TINIL: + case TWUP: + case PARITY: + case T1_MAX: // SCI related options. The panda does not appear to support this + case T2_MAX: + case T3_MAX: + case T4_MAX: + case T5_MAX: + break; // Just smile and nod. + default: + printf("Got unknown SET code %X\n", Parameter); + } +} + +unsigned long J2534Connection::processIOCTLGetConfig(unsigned long Parameter) { + switch (Parameter) { + case DATA_RATE: + return this->getBaud(); + case LOOPBACK: + return this->loopback; + break; + case BIT_SAMPLE_POINT: + return 80; + case SYNC_JUMP_WIDTH: + return 15; + default: + // HDS rarely reads off values through ioctl GET_CONFIG, but it often + // just wants the call to pass without erroring, so just don't do anything. + printf("Got unknown code %X\n", Parameter); + } +} diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection.h b/drivers/windows/pandaJ2534DLL/J2534Connection.h new file mode 100644 index 0000000..cdb2150 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection.h @@ -0,0 +1,141 @@ +#pragma once +#include "panda/panda.h" +#include "J2534_v0404.h" +#include "synchronize.h" +#include "J2534Frame.h" +#include "PandaJ2534Device.h" +#include "J2534MessageFilter.h" +#include "MessagePeriodic.h" + +class J2534Frame; +class Action; +class PandaJ2534Device; +class J2534MessageFilter; + +#define check_bmask(num, mask)(((num) & mask) == mask) + +/** +Class representing a generic J2534 Connection created by PassThruConnect, +and is associated with a channelID given to the J2534 API user. +Subclasses implement specific J2534 supported protocols. +*/ +class J2534Connection : public std::enable_shared_from_this { + friend class PandaJ2534Device; + +public: + J2534Connection( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ); + virtual ~J2534Connection() {}; + + //J2534 API functions + + virtual long PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); + long PassThruWriteMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); + virtual long PassThruStartPeriodicMsg(PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval); + virtual long PassThruStopPeriodicMsg(unsigned long MsgID); + + virtual long PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID); + + virtual long PassThruStopMsgFilter(unsigned long FilterID); + virtual long PassThruIoctl(unsigned long IoctlID, void *pInput, void *pOutput); + + //Functions for parsing messages to be send with PassThruWriteMsgs. + + virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg); + virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& msg) { return nullptr; }; + + //IOCTL functions + + long init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput); + long initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput); + long clearTXBuff(); + long clearRXBuff(); + long clearPeriodicMsgs(); + long clearMsgFilters(); + + virtual void setBaud(unsigned long baud); + + unsigned long getBaud() { + return this->BaudRate; + } + + unsigned long getProtocol() { + return this->ProtocolID; + }; + + virtual bool isProtoCan() { + return FALSE; + } + + //Port is used in a protocol specific way to differentiate tranceivers. + unsigned long getPort() { + return this->port; + } + + virtual void processIOCTLSetConfig(unsigned long Parameter, unsigned long Value); + + virtual unsigned long processIOCTLGetConfig(unsigned long Parameter); + + //Called when the passthru device has received a message for this connection + //Loopback messages are processed separately. + virtual void processMessage(const J2534Frame& msg); + + //Limitations on message size. Override in every subclass. + + virtual unsigned long getMinMsgLen() { + return 1; + } + + virtual unsigned long getMaxMsgLen() { + return 4128; + } + + virtual unsigned long getMaxMsgSingleFrameLen() { + return 12; + } + + //Add an Action to the Task Queue for future processing. + //The task should be set its expire time before being submitted. + void schedultMsgTx(std::shared_ptr msgout); + + void rescheduleExistingTxMsgs(); + + std::shared_ptr getPandaDev() { + if (auto panda_dev_sp = this->panda_dev.lock()) + return panda_dev_sp; + return nullptr; + } + + //Add a message to the queue read by PassThruReadMsgs(). + void addMsgToRxQueue(const J2534Frame& frame) { + synchronized(messageRxBuff_mutex) { + messageRxBuff.push(frame); + } + } + + bool loopback = FALSE; + +protected: + unsigned long ProtocolID; + unsigned long Flags; + unsigned long BaudRate; + unsigned long port; + + std::weak_ptr panda_dev; + + Mutex messageRxBuff_mutex; + std::queue messageRxBuff; + + std::array, 10> filters; + std::queue> txbuff; + + std::array, 10> periodicMessages; + +private: + Mutex staged_writes_lock; +}; diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp new file mode 100644 index 0000000..3426169 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.cpp @@ -0,0 +1,41 @@ +#include "stdafx.h" +#include "J2534Connection_CAN.h" +#include "MessageTx_CAN.h" +#include "Timer.h" + +J2534Connection_CAN::J2534Connection_CAN( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate) { + this->port = 0; + + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_INVALID_BAUDRATE; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, BaudRate/100); +}; + +unsigned long J2534Connection_CAN::validateTxMsg(PASSTHRU_MSG* msg) { + if ((msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen() || + (val_is_29bit(msg->TxFlags) != this->_is_29bit() && !check_bmask(this->Flags, CAN_ID_BOTH)))) + return ERR_INVALID_MSG; + return STATUS_NOERROR; +} + +std::shared_ptr J2534Connection_CAN::parseMessageTx(PASSTHRU_MSG& msg) { + return std::dynamic_pointer_cast(std::make_shared(shared_from_this(), msg)); +} + +void J2534Connection_CAN::setBaud(unsigned long BaudRate) { + if (auto panda_dev = this->getPandaDev()) { + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_NOT_SUPPORTED; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); + return J2534Connection::setBaud(BaudRate); + } else { + throw ERR_DEVICE_NOT_CONNECTED; + } +} diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h new file mode 100644 index 0000000..4dd950b --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_CAN.h @@ -0,0 +1,43 @@ +#pragma once + +#include "J2534Connection.h" +#include "panda/panda.h" + +#define val_is_29bit(num) check_bmask(num, CAN_29BIT_ID) + +class J2534Connection_CAN : public J2534Connection { +public: + J2534Connection_CAN( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ); + + virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg); + + virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& pMsg); + + virtual void setBaud(unsigned long baud); + + virtual unsigned long getMinMsgLen() { + return 4; + } + + virtual unsigned long getMaxMsgLen() { + return 12; + } + + virtual unsigned long getMaxMsgSingleFrameLen() { + return 12; + } + + virtual bool isProtoCan() { + return TRUE; + } + + bool _is_29bit() { + return (this->Flags & CAN_29BIT_ID) == CAN_29BIT_ID; + } + +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp new file mode 100644 index 0000000..2b9d97c --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.cpp @@ -0,0 +1,229 @@ +#include "stdafx.h" +#include "J2534Connection_ISO15765.h" +#include "Timer.h" +#include "constants_ISO15765.h" +#include + +J2534Connection_ISO15765::J2534Connection_ISO15765( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate +) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate), wftMax(0) { + this->port = 0; + + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_INVALID_BAUDRATE; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); +} + +unsigned long J2534Connection_ISO15765::validateTxMsg(PASSTHRU_MSG* msg) { + if ((msg->DataSize < this->getMinMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) || + msg->DataSize > this->getMaxMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) || + (val_is_29bit(msg->TxFlags) != this->_is_29bit() && !check_bmask(this->Flags, CAN_ID_BOTH)))) + return ERR_INVALID_MSG; + + int fid = get_matching_out_fc_filter_id(std::string((const char*)msg->Data, msg->DataSize), msg->TxFlags, 0xFFFFFFFF); + if (msg->DataSize > getMaxMsgSingleFrameLen() && fid == -1) return ERR_NO_FLOW_CONTROL; //11 bytes (4 for CANid, 7 payload) is max length of input frame. + + return STATUS_NOERROR; +} + +std::shared_ptr J2534Connection_ISO15765::parseMessageTx(PASSTHRU_MSG& msg) { + int fid = get_matching_out_fc_filter_id(std::string((const char*)msg.Data, msg.DataSize), msg.TxFlags, 0xFFFFFFFF); + if (msg.DataSize > getMaxMsgSingleFrameLen() && fid == -1) 1; + + return std::dynamic_pointer_cast( + std::make_shared(shared_from_this(), msg, (fid == -1) ? nullptr : this->filters[fid]) + ); +} + +//https://happilyembedded.wordpress.com/2016/02/15/can-multiple-frame-transmission/ +void J2534Connection_ISO15765::processMessage(const J2534Frame& msg) { + if (msg.ProtocolID != CAN) return; + + int fid = get_matching_in_fc_filter_id(msg); + if (fid == -1) return; + + auto filter = this->filters[fid]; + bool is_ext_addr = check_bmask(filter->flags, ISO15765_ADDR_TYPE); + uint8_t addrlen = is_ext_addr ? 5 : 4; + + switch (msg_get_type(msg, addrlen)) { + case FRAME_FLOWCTRL: + { + if (this->txbuff.size() == 0) + return; + if (msg.Data.size() < addrlen + 3) return; + uint8_t flow_status = msg.Data[addrlen] & 0x0F; + uint8_t block_size = msg.Data[addrlen + 1]; + uint8_t st_min = msg.Data[addrlen + 2]; + + auto txConvo = std::static_pointer_cast(this->txbuff.front()); + switch (flow_status) { + case FLOWCTRL_CONTINUE: { + if (st_min > 0xF9) break; + if (st_min >= 0xf1 && st_min <= 0xf9) { + txConvo->flowControlContinue(block_size, std::chrono::microseconds((st_min & 0x0F) * 100)); + } else if(st_min <= 0x7f) { + txConvo->flowControlContinue(block_size, std::chrono::microseconds(st_min * 1000)); + } else { + break; + } + txConvo->scheduleImmediate(); + this->rescheduleExistingTxMsgs(); + break; + } + case FLOWCTRL_WAIT: + txConvo->flowControlWait(this->wftMax); + break; + case FLOWCTRL_ABORT: + txConvo->flowControlAbort(); + break; + } + break; + } + case FRAME_SINGLE: + { + this->rxConversations[fid] = nullptr; //Reset any current transaction. + + if (is_ext_addr) { + if ((msg.Data[5] & 0x0F) > 6) return; + } else { + if ((msg.Data[4] & 0x0F) > 7) return; + } + + J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp); + if (msg.Data.size() != 8 && check_bmask(this->Flags, ISO15765_FRAME_PAD)) + outframe.RxStatus |= ISO15765_PADDING_ERROR; + if (is_ext_addr) + outframe.RxStatus |= ISO15765_ADDR_TYPE; + outframe.Data = msg.Data.substr(0, addrlen) + msg.Data.substr(addrlen + 1, msg.Data[addrlen]); + outframe.ExtraDataIndex = outframe.Data.size(); + + addMsgToRxQueue(outframe); + break; + } + case FRAME_FIRST: + { + if (msg.Data.size() < 12) { + //A frame was received that could have held more data. + //No examples of this protocol show that happening, so + //it will be assumed that it is grounds to reset rx. + this->rxConversations[fid] = nullptr; + return; + } + + J2534Frame outframe(ISO15765, msg.RxStatus | START_OF_MESSAGE, 0, msg.Timestamp); + if (is_ext_addr) + outframe.RxStatus |= ISO15765_ADDR_TYPE; + outframe.Data = msg.Data.substr(0, addrlen); + + addMsgToRxQueue(outframe); + + this->rxConversations[fid] = std::make_shared( + ((msg.Data[addrlen] & 0x0F) << 8) | msg.Data[addrlen + 1], + msg.Data.substr(addrlen + 2, 12 - (addrlen + 2)), + msg.RxStatus, filter); + + //TODO maybe the flow control should also be scheduled in the TX list. + //Doing it this way because the filter can be 5 bytes in ext address mode. + std::string flowfilter = filter->get_flowctrl(); + uint32_t flow_addr = (((uint8_t)flowfilter[0]) << 24) | ((uint8_t)(flowfilter[1]) << 16) | ((uint8_t)(flowfilter[2]) << 8) | ((uint8_t)flowfilter[3]); + + std::string flowstrlresp; + if (flowfilter.size() > 4) + flowstrlresp += flowfilter[4]; + flowstrlresp += std::string("\x30\x00\x00", 3); + + if (auto panda_dev_sp = this->panda_dev.lock()) { + panda_dev_sp->panda->can_send(flow_addr, val_is_29bit(msg.RxStatus), (const uint8_t *)flowstrlresp.c_str(), (uint8_t)flowstrlresp.size(), panda::PANDA_CAN1); + } + break; + } + case FRAME_CONSEC: + { + auto& convo = this->rxConversations[fid]; + if (convo == nullptr) return; + + if (!convo->rx_add_frame(msg.Data[addrlen], (is_ext_addr ? 6 : 7), msg.Data.substr(addrlen + 1))) { + //Delete this conversation. + convo = nullptr; + return; + } + + std::string final_msg; + if (convo->flush_result(final_msg)) { + convo = nullptr; + J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp); + if (is_ext_addr) + outframe.RxStatus |= ISO15765_ADDR_TYPE; + outframe.Data = msg.Data.substr(0, addrlen) + final_msg; + outframe.ExtraDataIndex = outframe.Data.size(); + + addMsgToRxQueue(outframe); + } + break; + } + } +} + +void J2534Connection_ISO15765::setBaud(unsigned long BaudRate) { + if (auto panda_dev = this->getPandaDev()) { + if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) + throw ERR_NOT_SUPPORTED; + + panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); + return J2534Connection::setBaud(BaudRate); + } else { + throw ERR_DEVICE_NOT_CONNECTED; + } +} + +long J2534Connection_ISO15765::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) { + + if (FilterType != FLOW_CONTROL_FILTER) return ERR_INVALID_FILTER_ID; + return J2534Connection::PassThruStartMsgFilter(FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID); +} + +int J2534Connection_ISO15765::get_matching_out_fc_filter_id(const std::string& msgdata, unsigned long flags, unsigned long flagmask) { + for (unsigned int i = 0; i < this->filters.size(); i++) { + if (this->filters[i] == nullptr) continue; + auto filter = this->filters[i]->get_flowctrl(); + if (filter == msgdata.substr(0, filter.size()) && + (this->filters[i]->flags & flagmask) == (flags & flagmask)) + return i; + } + return -1; +} + +int J2534Connection_ISO15765::get_matching_in_fc_filter_id(const J2534Frame& msg, unsigned long flagmask) { + for (unsigned int i = 0; i < this->filters.size(); i++) { + if (this->filters[i] == nullptr) continue; + if (this->filters[i]->check(msg) == FILTER_RESULT_MATCH && + (this->filters[i]->flags & flagmask) == (msg.RxStatus & flagmask)) + return i; + } + return -1; +} + +void J2534Connection_ISO15765::processIOCTLSetConfig(unsigned long Parameter, unsigned long Value) { + switch (Parameter) { + case ISO15765_WFT_MAX: + this->wftMax = Value; + break; + default: + J2534Connection::processIOCTLSetConfig(Parameter, Value); + } +} + +unsigned long J2534Connection_ISO15765::processIOCTLGetConfig(unsigned long Parameter) { + switch (Parameter) { + case ISO15765_WFT_MAX: + return this->wftMax; + default: + return J2534Connection::processIOCTLGetConfig(Parameter); + } +} diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h new file mode 100644 index 0000000..ddbf2dc --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO15765.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include "J2534Connection.h" +#include "J2534Connection_CAN.h" +#include "MessageTx_ISO15765.h" +#include "MessageRx.h" + +class MessageTx_ISO15765; + +typedef struct { + std::string dispatched_msg; + std::string remaining_payload; +} PRESTAGED_WRITE; + +class J2534Connection_ISO15765 : public J2534Connection { +public: + J2534Connection_ISO15765( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ); + + virtual long PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG * pMaskMsg, PASSTHRU_MSG * pPatternMsg, PASSTHRU_MSG * pFlowControlMsg, unsigned long * pFilterID); + + int get_matching_out_fc_filter_id(const std::string & msgdata, unsigned long flags, unsigned long flagmask); + + int get_matching_in_fc_filter_id(const J2534Frame& msg, unsigned long flagmask = CAN_29BIT_ID); + + virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg); + + virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& msg); + + virtual void processMessage(const J2534Frame& msg); + + virtual void setBaud(unsigned long baud); + + virtual void processIOCTLSetConfig(unsigned long Parameter, unsigned long Value); + + virtual unsigned long processIOCTLGetConfig(unsigned long Parameter); + + virtual unsigned long getMinMsgLen() { + return 4; + } + + virtual unsigned long getMaxMsgLen() { + return 4099; + }; + + virtual unsigned long getMaxMsgSingleFrameLen() { + return 11; + } + + virtual bool _is_29bit() { + return (this->Flags & CAN_29BIT_ID) == CAN_29BIT_ID; + } + + virtual bool isProtoCan() { + return TRUE; + } + +private: + std::array, 10> rxConversations; + unsigned int wftMax; +}; diff --git a/drivers/windows/pandaJ2534DLL/J2534Frame.h b/drivers/windows/pandaJ2534DLL/J2534Frame.h new file mode 100644 index 0000000..5c991c5 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Frame.h @@ -0,0 +1,48 @@ +#pragma once +#include "J2534_v0404.h" +#include "panda/panda.h" + +/*A move convenient container for J2534 Messages than the static buffer provided by default.*/ +class J2534Frame { +public: + J2534Frame(unsigned long ProtocolID, unsigned long RxStatus=0, unsigned long TxFlags=0, unsigned long Timestamp=0) : + ProtocolID(ProtocolID), RxStatus(RxStatus), TxFlags(TxFlags), Timestamp(Timestamp), ExtraDataIndex(0), Data("") { }; + + J2534Frame(const panda::PANDA_CAN_MSG& msg_in) { + ProtocolID = CAN; + ExtraDataIndex = 0; + Data.reserve(msg_in.len + 4); + Data += msg_in.addr >> 24; + Data += (msg_in.addr >> 16) & 0xFF; + Data += (msg_in.addr >> 8) & 0xFF; + Data += msg_in.addr & 0xFF; + Data += std::string((char*)&msg_in.dat, msg_in.len); + Timestamp = msg_in.recv_time; + RxStatus = (msg_in.addr_29b ? CAN_29BIT_ID : 0) | + (msg_in.is_receipt ? TX_MSG_TYPE : 0); + } + + J2534Frame(const PASSTHRU_MSG& msg) { + this->ProtocolID = msg.ProtocolID; + this->RxStatus = msg.RxStatus; + this->TxFlags = msg.TxFlags; + this->Timestamp = msg.Timestamp; + this->ExtraDataIndex = msg.ExtraDataIndex; + this->Data = std::string((const char*)msg.Data, msg.DataSize); + } + + J2534Frame() { + this->ProtocolID = 0; + this->RxStatus = 0; + this->TxFlags = 0; + this->Timestamp = 0; + this->ExtraDataIndex = 0; + } + + unsigned long ProtocolID; + unsigned long RxStatus; + unsigned long TxFlags; + unsigned long Timestamp; + unsigned long ExtraDataIndex; + std::string Data; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp new file mode 100644 index 0000000..2d19e1f --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp @@ -0,0 +1,104 @@ +#include "stdafx.h" +#include "J2534MessageFilter.h" +#include "J2534Frame.h" + +J2534MessageFilter::J2534MessageFilter( + J2534Connection *const conn, + unsigned int filtertype, + PASSTHRU_MSG *pMaskMsg, + PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg +) : filtertype(filtertype), flags(0), conn(conn) { + switch (filtertype) { + case PASS_FILTER: + case BLOCK_FILTER: + if (pMaskMsg == NULL || pPatternMsg == NULL) + throw ERR_NULL_PARAMETER; + if (pFlowControlMsg != NULL) + throw ERR_INVALID_FILTER_ID; + if (pMaskMsg->DataSize != pPatternMsg->DataSize) + throw ERR_INVALID_MSG; + break; + case FLOW_CONTROL_FILTER: + if (conn->getProtocol() != ISO15765) throw ERR_MSG_PROTOCOL_ID; //CHECK + if (pFlowControlMsg == NULL || pMaskMsg == NULL || pPatternMsg == NULL) + throw ERR_NULL_PARAMETER; + break; + default: + throw ERR_INVALID_MSG; + } + + if (!(conn->getMinMsgLen() < pMaskMsg->DataSize || pMaskMsg->DataSize < conn->getMaxMsgLen())) + throw ERR_INVALID_MSG; + if (conn->getProtocol() != pMaskMsg->ProtocolID) + throw ERR_MSG_PROTOCOL_ID; + this->maskMsg = std::string((char*)pMaskMsg->Data, pMaskMsg->DataSize); + + if (!(conn->getMinMsgLen() < pPatternMsg->DataSize || pPatternMsg->DataSize < conn->getMaxMsgLen())) + throw ERR_INVALID_MSG; + if (conn->getProtocol() != pPatternMsg->ProtocolID) + throw ERR_MSG_PROTOCOL_ID; + this->patternMsg = std::string((char*)pPatternMsg->Data, pPatternMsg->DataSize); + if (this->maskMsg.size() != this->patternMsg.size()) + throw ERR_INVALID_MSG; + + if (pFlowControlMsg) { + if (!(conn->getMinMsgLen() < pFlowControlMsg->DataSize || pFlowControlMsg->DataSize < conn->getMaxMsgLen())) + throw ERR_INVALID_MSG; + if (conn->getProtocol() != pFlowControlMsg->ProtocolID) + throw ERR_MSG_PROTOCOL_ID; + if (pMaskMsg->TxFlags != pPatternMsg->TxFlags || pMaskMsg->TxFlags != pFlowControlMsg->TxFlags) + throw ERR_INVALID_MSG; + if(pFlowControlMsg->TxFlags & ~(ISO15765_FRAME_PAD | CAN_29BIT_ID | ISO15765_ADDR_TYPE)) + throw ERR_INVALID_MSG; + if ((pFlowControlMsg->TxFlags & ISO15765_ADDR_TYPE) == ISO15765_ADDR_TYPE) { + if(pFlowControlMsg->DataSize != 5) + throw ERR_INVALID_MSG; + } else { + if (pFlowControlMsg->DataSize != 4) + throw ERR_INVALID_MSG; + } + this->flowCtrlMsg = std::string((char*)pFlowControlMsg->Data, pFlowControlMsg->DataSize); + if (this->flowCtrlMsg.size() != this->patternMsg.size()) + throw ERR_INVALID_MSG; + this->flags = pFlowControlMsg->TxFlags; + } +} + +bool J2534MessageFilter::operator ==(const J2534MessageFilter &b) const { + if (this->filtertype != b.filtertype) return FALSE; + if (this->maskMsg != b.maskMsg) return FALSE; + if (this->patternMsg != b.patternMsg) return FALSE; + if (this->flowCtrlMsg != b.flowCtrlMsg) return FALSE; + if (this->flags != b.flags) return FALSE; + return TRUE; +} + +FILTER_RESULT J2534MessageFilter::check(const J2534Frame& msg) { + bool matches = TRUE; + if (msg.Data.size() < this->maskMsg.size()) { + matches = FALSE; + } else { + for (int i = 0; i < this->maskMsg.size(); i++) { + if (this->patternMsg[i] != (msg.Data[i] & this->maskMsg[i])) { + matches = FALSE; + break; + } + } + } + + switch (this->filtertype) { + case PASS_FILTER: + return matches ? FILTER_RESULT_PASS : FILTER_RESULT_NEUTRAL; + case BLOCK_FILTER: + return matches ? FILTER_RESULT_BLOCK: FILTER_RESULT_NEUTRAL; + case FLOW_CONTROL_FILTER: + return matches ? FILTER_RESULT_MATCH : FILTER_RESULT_NOMATCH; + default: + throw std::out_of_range("Filtertype should not be able to be anything but PASS, BLOCK, or FLOW_CONTROL"); + } +} + +std::string J2534MessageFilter::get_flowctrl() { + return std::string(this->flowCtrlMsg); +} diff --git a/drivers/windows/pandaJ2534DLL/J2534MessageFilter.h b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.h new file mode 100644 index 0000000..c5e9a68 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.h @@ -0,0 +1,47 @@ +#pragma once +#include "J2534_v0404.h" +#include "J2534Connection.h" +#include "J2534Frame.h" + +typedef enum { + FILTER_RESULT_BLOCK, + FILTER_RESULT_NEUTRAL, + FILTER_RESULT_PASS, + FILTER_RESULT_NOMATCH = FILTER_RESULT_BLOCK, + FILTER_RESULT_MATCH = FILTER_RESULT_PASS, +} FILTER_RESULT; + +//Forward declare +class J2534Connection; + +/* Represents a J2534 Message Filter created by PassThruStartMsgFilter. + +J2534 uses filters to sort out messages in a simple and sane way. Except for +flow control filters. J2534 v04.04 uses filters to manage 'conversations' in +protocols that support flow control like ISO15765. The whole solution is a +hack, and J2534 v05.00 greatly simplifies this concept. But we are using +v04.04 so, here we are. +*/ +class J2534MessageFilter { +public: + J2534MessageFilter( + J2534Connection *const conn, + unsigned int filtertype, + PASSTHRU_MSG *pMaskMsg, + PASSTHRU_MSG *pPatternMsg, + PASSTHRU_MSG *pFlowControlMsg + ); + + bool J2534MessageFilter::operator ==(const J2534MessageFilter &b) const; + + FILTER_RESULT check(const J2534Frame& msg); + std::string get_flowctrl(); + + unsigned long flags; + J2534Connection *const conn; +private: + unsigned int filtertype; + std::string maskMsg; + std::string patternMsg; + std::string flowCtrlMsg; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/J2534_v0404.h b/drivers/windows/pandaJ2534DLL/J2534_v0404.h new file mode 100644 index 0000000..7cccf6b --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534_v0404.h @@ -0,0 +1,428 @@ +// +// 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: 5216 $ +// $Date: 2016-03-15 09:32:34 -0600 (Tue, 15 Mar 2016) $ +// + +#pragma once + +#ifdef PANDAJ2534DLL_EXPORTS +#define PANDAJ2534DLL_API extern "C" __declspec(dllexport) +#else +#define PANDAJ2534DLL_API +//__declspec(dllimport) +#endif + +// +// Platform-specific Defines: +// +// PTAPI: Define this yourself if you want a specific calling +// convention or other modifiers on the Pass-Thru API +// functions. Typically, on Windows, PTAPI will be defined +// as WINAPI, which enables the __stdcall convention. +// +#define PTAPI __stdcall //WINAPI + +// +// J2534-1 v04.04 ProtocolID Values +// +#define J1850VPW 0x01 +#define J1850PWM 0x02 +#define ISO9141 0x03 +#define ISO14230 0x04 +#define CAN 0x05 +#define ISO15765 0x06 +#define SCI_A_ENGINE 0x07 +#define SCI_A_TRANS 0x08 +#define SCI_B_ENGINE 0x09 +#define SCI_B_TRANS 0x0A + + +// +// J2534-2 ProtocolID Values +// +#define J1850VPW_PS 0x00008000 +#define J1850PWM_PS 0x00008001 +#define ISO9141_PS 0x00008002 +#define ISO14230_PS 0x00008003 +#define CAN_PS 0x00008004 +#define ISO15765_PS 0x00008005 +#define J2610_PS 0x00008006 +#define SW_ISO15765_PS 0x00008007 +#define SW_CAN_PS 0x00008008 +#define GM_UART_PS 0x00008009 +#define CAN_CH1 0x00009000 +#define CAN_CH2 (CAN_CH1 + 1) +#define CAN_CH128 (CAN_CH1 + 127) +#define J1850VPW_CH1 0x00009080 +#define J1850VPW_CH2 (J1850VPW_CH1 + 1) +#define J1850VPW_CH128 (J1850VPW_CH1 + 127) +#define J1850PWM_CH1 0x00009160 +#define J1850PWM_CH2 (J1850PWM_CH1 + 1) +#define J1850PWM_CH128 (J1850PWM_CH1 + 127) +#define ISO9141_CH1 0x00009240 +#define ISO9141_CH2 (ISO9141_CH1 + 1) +#define ISO9141_CH128 (ISO9141_CH1 + 127) +#define ISO14230_CH1 0x00009320 +#define ISO14230_CH2 (ISO14230_CH1 + 1) +#define ISO14230_CH128 (ISO14230_CH1 + 127) +#define ISO15765_CH1 0x00009400 +#define ISO15765_CH2 (ISO15765_CH1 + 1) +#define ISO15765_CH128 (ISO15765_CH1 + 127) +#define SW_CAN_CAN_CH1 0x00009480 +#define SW_CAN_CAN_CH2 (SW_CAN_CAN_CH1 + 1) +#define SW_CAN_CAN_CH128 (SW_CAN_CAN_CH1 + 127) +#define SW_CAN_ISO15765_CH1 0x00009560 +#define SW_CAN_ISO15765_CH2 (SW_CAN_ISO15765_CH1 + 1) +#define SW_CAN_ISO15765_CH128 (SW_CAN_ISO15765_CH1 + 127) +#define J2610_CH1 0x00009640 +#define J2610_CH2 (J2610_CH1 + 1) +#define J2610_CH128 (J2610_CH1 + 127) +#define ANALOG_IN_CH1 0x0000C000 +#define ANALOG_IN_CH2 0x0000C001 +#define ANALOG_IN_CH32 0x0000C01F + + +// +// J2534-1 v04.04 Error Values +// +#define STATUS_NOERROR 0x00 // Function call successful. +#define ERR_NOT_SUPPORTED 0x01 // Device cannot support requested functionality mandated in J2534. Device is not fully SAE J2534 compliant. +#define ERR_INVALID_CHANNEL_ID 0x02 // Invalid ChannelID value. +#define ERR_INVALID_PROTOCOL_ID 0x03 // Invalid or unsupported ProtocolID, or there is a resource conflict (i.e. trying to connect to multiple mutually exclusive protocols such as J1850PWM and J1850VPW, or CAN and SCI, etc.). +#define ERR_NULL_PARAMETER 0x04 // NULL pointer supplied where a valid pointer is required. +#define ERR_INVALID_IOCTL_VALUE 0x05 // Invalid value for Ioctl parameter. +#define ERR_INVALID_FLAGS 0x06 // Invalid flag values. +#define ERR_FAILED 0x07 // Undefined error, use PassThruGetLastError() for text description. +#define ERR_DEVICE_NOT_CONNECTED 0x08 // Unable to communicate with device. +#define ERR_TIMEOUT 0x09 // 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 . + // PassThruWriteMsgs() - Device could not write the specified number of messages. The actual number of messages sent on the vehicle network is placed in . +#define ERR_INVALID_MSG 0x0A // Invalid message structure pointed to by pMsg. +#define ERR_INVALID_TIME_INTERVAL 0x0B // Invalid TimeInterval value. +#define ERR_EXCEEDED_LIMIT 0x0C // Exceeded maximum number of message IDs or allocated space. +#define ERR_INVALID_MSG_ID 0x0D // Invalid MsgID value. +#define ERR_DEVICE_IN_USE 0x0E // Device is currently open. +#define ERR_INVALID_IOCTL_ID 0x0F // Invalid IoctlID value. +#define ERR_BUFFER_EMPTY 0x10 // Protocol message buffer empty, no messages available to read. +#define ERR_BUFFER_FULL 0x11 // Protocol message buffer full. All the messages specified may not have been transmitted. +#define ERR_BUFFER_OVERFLOW 0x12 // Indicates a buffer overflow occurred and messages were lost. +#define ERR_PIN_INVALID 0x13 // Invalid pin number, pin number already in use, or voltage already applied to a different pin. +#define ERR_CHANNEL_IN_USE 0x14 // Channel number is currently connected. +#define ERR_MSG_PROTOCOL_ID 0x15 // Protocol type in the message does not match the protocol associated with the Channel ID +#define ERR_INVALID_FILTER_ID 0x16 // Invalid Filter ID value. +#define ERR_NO_FLOW_CONTROL 0x17 // No flow control filter set or matched (for ProtocolID ISO15765 only). +#define ERR_NOT_UNIQUE 0x18 // A CAN ID in pPatternMsg or pFlowControlMsg matches either ID in an existing FLOW_CONTROL_FILTER +#define ERR_INVALID_BAUDRATE 0x19 // The desired baud rate cannot be achieved within the tolerance specified in SAE J2534-1 Section 6.5 +#define ERR_INVALID_DEVICE_ID 0x1A // Device ID invalid. + + +// +// J2534-1 v04.04 Connect Flags +// +#define CAN_29BIT_ID 0x0100 +#define ISO9141_NO_CHECKSUM 0x0200 +#define CAN_ID_BOTH 0x0800 +#define ISO9141_K_LINE_ONLY 0x1000 + + +// +// J2534-1 v04.04 Filter Type Values +// +#define PASS_FILTER 0x00000001 +#define BLOCK_FILTER 0x00000002 +#define FLOW_CONTROL_FILTER 0x00000003 + + +// +// J2534-1 v04.04 Programming Voltage Pin Numbers +// +#define AUXILIARY_OUTPUT_PIN 0 +#define SAE_J1962_CONNECTOR_PIN_6 6 +#define SAE_J1962_CONNECTOR_PIN_9 9 +#define SAE_J1962_CONNECTOR_PIN_11 11 +#define SAE_J1962_CONNECTOR_PIN_12 12 +#define SAE_J1962_CONNECTOR_PIN_13 13 +#define SAE_J1962_CONNECTOR_PIN_14 14 +#define SAE_J1962_CONNECTOR_PIN_15 15 // Short to ground only + + +// +// J2534-1 v04.04 Programming Voltage Values +// +#define SHORT_TO_GROUND 0xFFFFFFFE +#define VOLTAGE_OFF 0xFFFFFFFF + + +// +// J2534-1 v04.04 API Version Values +// +#define J2534_APIVER_FEBRUARY_2002 "02.02" +#define J2534_APIVER_NOVEMBER_2004 "04.04" + + +// +// J2534-1 v04.04 IOCTL ID Values +// +#define GET_CONFIG 0x01 // pInput = SCONFIG_LIST, pOutput = NULL +#define SET_CONFIG 0x02 // pInput = SCONFIG_LIST, pOutput = NULL +#define READ_VBATT 0x03 // pInput = NULL, pOutput = unsigned long +#define FIVE_BAUD_INIT 0x04 // pInput = SBYTE_ARRAY, pOutput = SBYTE_ARRAY +#define FAST_INIT 0x05 // pInput = PASSTHRU_MSG, pOutput = PASSTHRU_MSG +#define CLEAR_TX_BUFFER 0x07 // pInput = NULL, pOutput = NULL +#define CLEAR_RX_BUFFER 0x08 // pInput = NULL, pOutput = NULL +#define CLEAR_PERIODIC_MSGS 0x09 // pInput = NULL, pOutput = NULL +#define CLEAR_MSG_FILTERS 0x0A // pInput = NULL, pOutput = NULL +#define CLEAR_FUNCT_MSG_LOOKUP_TABLE 0x0B // pInput = NULL, pOutput = NULL +#define ADD_TO_FUNCT_MSG_LOOKUP_TABLE 0x0C // pInput = SBYTE_ARRAY, pOutput = NULL +#define DELETE_FROM_FUNCT_MSG_LOOKUP_TABLE 0x0D // pInput = SBYTE_ARRAY, pOutput = NULL +#define READ_PROG_VOLTAGE 0x0E // pInput = NULL, pOutput = unsigned long + + +// +// J2534-2 IOCTL ID Values +// +#define SW_CAN_HS 0x00008000 // pInput = NULL, pOutput = NULL +#define SW_CAN_NS 0x00008001 // pInput = NULL, pOutput = NULL +#define SET_POLL_RESPONSE 0x00008002 // pInput = SBYTE_ARRAY, pOutput = NULL +#define BECOME_MASTER 0x00008003 // pInput = unsigned char, pOutput = NULL + + +// +// J2534-1 v04.04 Configuration Parameter Values +// Default value is enclosed in square brackets "[" and "]" +// +#define DATA_RATE 0x01 // 5-500000 +#define LOOPBACK 0x03 // 0 (OFF), 1 (ON) [0] +#define NODE_ADDRESS 0x04 // J1850PWM: 0x00-0xFF +#define NETWORK_LINE 0x05 // J1850PWM: 0 (BUS_NORMAL), 1 (BUS_PLUS), 2 (BUS_MINUS) [0] +#define P1_MIN 0x06 // ISO9141 or ISO14230: Not used by interface +#define P1_MAX 0x07 // ISO9141 or ISO14230: 0x1-0xFFFF (.5 ms per bit) [40 (20ms)] +#define P2_MIN 0x08 // ISO9141 or ISO14230: Not used by interface +#define P2_MAX 0x09 // ISO9141 or ISO14230: Not used by interface +#define P3_MIN 0x0A // ISO9141 or ISO14230: 0x0-0xFFFF (.5 ms per bit) [110 (55ms)] +#define P3_MAX 0x0B // ISO9141 or ISO14230: Not used by interface +#define P4_MIN 0x0C // ISO9141 or ISO14230: 0x0-0xFFFF (.5 ms per bit) [10 (5ms)] +#define P4_MAX 0x0D // ISO9141 or ISO14230: Not used by interface +#define W0 0x19 // ISO9141: 0x0-0xFFFF (1 ms per bit) [300] +#define W1 0x0E // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [300] +#define W2 0x0F // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [20] +#define W3 0x10 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [20] +#define W4 0x11 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [50] +#define W5 0x12 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [300] +#define TIDLE 0x13 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [300] +#define TINIL 0x14 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [25] +#define TWUP 0x15 // ISO9141 or ISO14230: 0x0-0xFFFF (1 ms per bit) [50] +#define PARITY 0x16 // ISO9141 or ISO14230: 0 (NO_PARITY), 1 (ODD_PARITY), 2 (EVEN_PARITY) [0] +#define BIT_SAMPLE_POINT 0x17 // CAN: 0-100 (1% per bit) [80] +#define SYNC_JUMP_WIDTH 0x18 // CAN: 0-100 (1% per bit) [15] +#define T1_MAX 0x1A // SCI: 0x0-0xFFFF (1 ms per bit) [20] +#define T2_MAX 0x1B // SCI: 0x0-0xFFFF (1 ms per bit) [100] +#define T3_MAX 0x24 // SCI: 0x0-0xFFFF (1 ms per bit) [50] +#define T4_MAX 0x1C // SCI: 0x0-0xFFFF (1 ms per bit) [20] +#define T5_MAX 0x1D // SCI: 0x0-0xFFFF (1 ms per bit) [100] +#define ISO15765_BS 0x1E // ISO15765: 0x0-0xFF [0] +#define ISO15765_STMIN 0x1F // ISO15765: 0x0-0xFF [0] +#define ISO15765_BS_TX 0x22 // ISO15765: 0x0-0xFF,0xFFFF [0xFFFF] +#define ISO15765_STMIN_TX 0x23 // ISO15765: 0x0-0xFF,0xFFFF [0xFFFF] +#define DATA_BITS 0x20 // ISO9141 or ISO14230: 0 (8 data bits), 1 (7 data bits) [0] +#define FIVE_BAUD_MOD 0x21 // ISO9141 or ISO14230: 0 (ISO 9141-2/14230-4), 1 (Inv KB2), 2 (Inv Addr), 3 (ISO 9141) [0] +#define ISO15765_WFT_MAX 0x25 // ISO15765: 0x0-0xFF [0] + + +// +// J2534-2 Configuration Parameter Values +// Default value is enclosed in square brackets "[" and "]" +// +#define CAN_MIXED_FORMAT 0x00008000 // See #defines below. [0] +#define J1962_PINS 0x00008001 // 0xPPSS PP: 0x00-0x10 SS: 0x00-0x10 PP!=SS, except 0x0000. Exclude pins 4, 5, and 16. [0] +#define SW_CAN_HS_DATA_RATE 0x00008010 // SWCAN: 5-500000 [83333] +#define SW_CAN_SPEEDCHANGE_ENABLE 0x00008011 // SWCAN: 0 (DISABLE_SPDCHANGE), 1 (ENABLE_SPDCHANGE) [0] +#define SW_CAN_RES_SWITCH 0x00008012 // SWCAN: 0 (DISCONNECT_RESISTOR), 1 (CONNECT_RESISTOR), 2 (AUTO_ RESISTOR) [0] +#define ACTIVE_CHANNELS 0x00008020 // ANALOG: 0-0xFFFFFFFF +#define SAMPLE_RATE 0x00008021 // ANALOG: 0-0xFFFFFFFF [0] (high bit changes meaning from samples/sec to seconds/sample) +#define SAMPLES_PER_READING 0x00008022 // ANALOG: 1-0xFFFFFFFF [1] +#define READINGS_PER_MSG 0x00008023 // ANALOG: 1-0x00000408 (1 - 1032) [1] +#define AVERAGING_METHOD 0x00008024 // ANALOG: 0-0xFFFFFFFF [0] +#define SAMPLE_RESOLUTION 0x00008025 // ANALOG READ-ONLY: 0x1-0x20 (1 - 32) +#define INPUT_RANGE_LOW 0x00008026 // ANALOG READ-ONLY: 0x80000000-0x7FFFFFFF (-2147483648-2147483647) +#define INPUT_RANGE_HIGH 0x00008027 // ANALOG READ-ONLY: 0x80000000-0x7FFFFFFF (-2147483648-2147483647) + + +// +// J2534-2 Mixed-Mode/Format CAN Definitions +// +#define CAN_MIXED_FORMAT_OFF 0 // Messages will be treated as ISO 15765 ONLY. +#define CAN_MIXED_FORMAT_ON 1 // Messages will be treated as either ISO 15765 or an unformatted CAN frame. +#define CAN_MIXED_FORMAT_ALL_FRAMES 2 // Messages will be treated as ISO 15765, an unformatted CAN frame, or both. + + +// +// J2534-2 Analog Channel Averaging Method Definitions +// +#define SIMPLE_AVERAGE 0x00000000 // Simple arithmetic mean +#define MAX_LIMIT_AVERAGE 0x00000001 // Choose the biggest value +#define MIN_LIMIT_AVERAGE 0x00000002 // Choose the lowest value +#define MEDIAN_AVERAGE 0x00000003 // Choose arithmetic median + + +// +// J2534-1 v04.04 RxStatus Definitions +// +#define TX_MSG_TYPE 0x0001 +#define START_OF_MESSAGE 0x0002 +#define RX_BREAK 0x0004 +#define TX_INDICATION 0x0008 +#define ISO15765_PADDING_ERROR 0x0010 +#define ISO15765_ADDR_TYPE 0x0080 +//#define CAN_29BIT_ID 0x0100 // Defined above + + +// +// J2534-2 RxStatus Definitions +// +#define SW_CAN_HV_RX 0x00010000 // SWCAN Channels Only +#define SW_CAN_HS_RX 0x00020000 // SWCAN Channels Only +#define SW_CAN_NS_RX 0x00040000 // SWCAN Channels Only +#define OVERFLOW_ 0x00010000 // Analog Input Channels Only + + +// +// J2534-1 v04.04 TxFlags Definitions +// +#define ISO15765_FRAME_PAD 0x0040 +//#define ISO15765_ADDR_TYPE 0x0080 // Defined above +//#define CAN_29BIT_ID 0x0100 // Defined above +#define WAIT_P3_MIN_ONLY 0x0200 +#define SCI_MODE 0x400000 +#define SCI_TX_VOLTAGE 0x800000 + + +// +// J2534-2 TxFlags Definitions +// +#define SW_CAN_HV_TX 0x00000400 + + +// +// J2534-1 v04.04 Structure Definitions +// +typedef struct +{ + unsigned long Parameter; // Name of parameter + unsigned long Value; // Value of the parameter +} SCONFIG; + + +typedef struct +{ + unsigned long NumOfParams; // Number of SCONFIG elements + SCONFIG* ConfigPtr; // Array of SCONFIG +} SCONFIG_LIST; + + +typedef struct +{ + unsigned long NumOfBytes; // Number of bytes in the array + unsigned char* BytePtr; // Array of bytes +} SBYTE_ARRAY; + + +typedef struct +{ + unsigned long ProtocolID; + unsigned long RxStatus; + unsigned long TxFlags; + unsigned long Timestamp; + unsigned long DataSize; + unsigned long ExtraDataIndex; + unsigned char Data[4128]; +} PASSTHRU_MSG; + +// +// J2534-1 v04.04 Function Prototypes +// +PANDAJ2534DLL_API long PTAPI PassThruOpen(void *pName, unsigned long *pDeviceID); +PANDAJ2534DLL_API long PTAPI PassThruClose(unsigned long DeviceID); +PANDAJ2534DLL_API long PTAPI PassThruConnect(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID); +PANDAJ2534DLL_API long PTAPI PassThruDisconnect(unsigned long ChannelID); +PANDAJ2534DLL_API long PTAPI PassThruReadMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +PANDAJ2534DLL_API long PTAPI PassThruWriteMsgs(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +PANDAJ2534DLL_API long PTAPI PassThruStartPeriodicMsg(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval); +PANDAJ2534DLL_API long PTAPI PassThruStopPeriodicMsg(unsigned long ChannelID, unsigned long MsgID); +PANDAJ2534DLL_API long PTAPI PassThruStartMsgFilter(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID); +PANDAJ2534DLL_API long PTAPI PassThruStopMsgFilter(unsigned long ChannelID, unsigned long FilterID); +PANDAJ2534DLL_API long PTAPI PassThruSetProgrammingVoltage(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage); +PANDAJ2534DLL_API long PTAPI PassThruReadVersion(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion); +PANDAJ2534DLL_API long PTAPI PassThruGetLastError(char *pErrorDescription); +PANDAJ2534DLL_API long PTAPI PassThruIoctl(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput); + + +// +// J2534-1 v04.04 Function Typedefs +// These function typedefs allow simpler use of the J2534 API by +// allowing you to do things like this: +// PTCONNECT pPassThruConnectFunc = GetProcAddress(hModule, "PassThruConnect"); +// if (pPassThruConnectFunc == NULL) +// return FALSE; +// pPassThruConnectFunc(DeviceID, CAN, CAN_29BIT_ID, 500000, &ChannelID); +// +typedef long (PTAPI *PTOPEN)(void *pName, unsigned long *pDeviceID); +typedef long (PTAPI *PTCLOSE)(unsigned long DeviceID); +typedef long (PTAPI *PTCONNECT)(unsigned long DeviceID, unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate, unsigned long *pChannelID); +typedef long (PTAPI *PTDISCONNECT)(unsigned long ChannelID); +typedef long (PTAPI *PTREADMSGS)(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +typedef long (PTAPI *PTWRITEMSGS)(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout); +typedef long (PTAPI *PTSTARTPERIODICMSG)(unsigned long ChannelID, PASSTHRU_MSG *pMsg, unsigned long *pMsgID, unsigned long TimeInterval); +typedef long (PTAPI *PTSTOPPERIODICMSG)(unsigned long ChannelID, unsigned long MsgID); +typedef long (PTAPI *PTSTARTMSGFILTER)(unsigned long ChannelID, unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID); +typedef long (PTAPI *PTSTOPMSGFILTER)(unsigned long ChannelID, unsigned long FilterID); +typedef long (PTAPI *PTSETPROGRAMMINGVOLTAGE)(unsigned long DeviceID, unsigned long PinNumber, unsigned long Voltage); +typedef long (PTAPI *PTREADVERSION)(unsigned long DeviceID, char *pFirmwareVersion, char *pDllVersion, char *pApiVersion); +typedef long (PTAPI *PTGETLASTERROR)(char *pErrorDescription); +typedef long (PTAPI *PTIOCTL)(unsigned long ChannelID, unsigned long IoctlID, void *pInput, void *pOutput); diff --git a/drivers/windows/pandaJ2534DLL/J2534register_x64.reg b/drivers/windows/pandaJ2534DLL/J2534register_x64.reg new file mode 100644 index 0000000..120ab39 Binary files /dev/null and b/drivers/windows/pandaJ2534DLL/J2534register_x64.reg differ diff --git a/drivers/windows/pandaJ2534DLL/MessagePeriodic.cpp b/drivers/windows/pandaJ2534DLL/MessagePeriodic.cpp new file mode 100644 index 0000000..0c3416e --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessagePeriodic.cpp @@ -0,0 +1,30 @@ +#include "stdafx.h" +#include "MessagePeriodic.h" +#include "J2534Connection.h" + +MessagePeriodic::MessagePeriodic( + std::chrono::microseconds delay, + std::shared_ptr msg +) : Action(msg->connection, delay), msg(msg), runyet(FALSE), active(TRUE) { }; + +void MessagePeriodic::execute() { + if (!this->active) return; + if (this->runyet) { + if (msg->isFinished()) { + msg->reset(); + msg->execute(); + } + } else { + this->runyet = TRUE; + msg->execute(); + } + + if (auto conn_sp = this->connection.lock()) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + //Scheduling must be relative to now incase there was a long stall that + //would case it to be super far behind and try to catch up forever. + this->scheduleImmediateDelay(); + panda_dev_sp->insertActionIntoTaskList(shared_from_this()); + } + } +} diff --git a/drivers/windows/pandaJ2534DLL/MessagePeriodic.h b/drivers/windows/pandaJ2534DLL/MessagePeriodic.h new file mode 100644 index 0000000..4013256 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessagePeriodic.h @@ -0,0 +1,33 @@ +#pragma once +#include "Action.h" +#include "MessageTx.h" + +class J2534Connection; + +/* A message that is resent on a given period. Created with calls to PassThruStartPeriodicMessage. + +Instead of making each J2534 protocol implementation have to implement periodic message +functionality, this class takes a message to be sent, and passes along the execute call +to the message, then reschedules itself. +*/ +class MessagePeriodic : public Action, public std::enable_shared_from_this +{ +public: + MessagePeriodic( + std::chrono::microseconds delay, + std::shared_ptr msg + ); + + virtual void execute(); + + void cancel() { + this->active = FALSE; + } + +protected: + std::shared_ptr msg; + +private: + BOOL runyet; + BOOL active; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageRx.h b/drivers/windows/pandaJ2534DLL/MessageRx.h new file mode 100644 index 0000000..e22278d --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageRx.h @@ -0,0 +1,61 @@ +#pragma once + +class MessageRx +{ +public: + MessageRx( + unsigned long size, + std::string piece, + unsigned long rxFlags, + std::shared_ptr filter + ) : expected_size(size & 0xFFF), flags(rxFlags) { + msg.reserve(expected_size); + msg = piece; + next_part = 1; + }; + + bool rx_add_frame(uint8_t pci_byte, unsigned int max_packet_size, const std::string piece) { + if ((pci_byte & 0x0F) != this->next_part) { + //TODO: Maybe this should instantly fail the transaction. + return TRUE; + } + + this->next_part = (this->next_part + 1) % 0x10; + unsigned int payload_len = min(expected_size - msg.size(), max_packet_size); + if (piece.size() < payload_len) { + //A frame was received that could have held more data. + //No examples of this protocol show that happening, so + //it will be assumed that it is grounds to reset rx. + return FALSE; + } + msg += piece.substr(0, payload_len); + + return TRUE; + } + + unsigned int bytes_remaining() { + return this->expected_size - this->msg.size(); + } + + bool is_ready() { + return this->msg.size() == this->expected_size; + } + + bool flush_result(std::string& final_msg) { + if (this->msg.size() == this->expected_size) { + final_msg = this->msg; + return TRUE; + } + return FALSE; + } + + uint8_t getNextConsecutiveFrameId() { + return this->next_part++; + } + + std::weak_ptr filter; + unsigned long flags; + unsigned long expected_size; + std::string msg; + unsigned char next_part; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageTx.h b/drivers/windows/pandaJ2534DLL/MessageTx.h new file mode 100644 index 0000000..5315fa0 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx.h @@ -0,0 +1,25 @@ +#pragma once +#include "Action.h" +#include "J2534Frame.h" + +class J2534Connection; + +class MessageTx : public Action, public std::enable_shared_from_this +{ +public: + MessageTx( + std::weak_ptr connection_in, + PASSTHRU_MSG& to_send + ) : Action(connection_in), fullmsg(to_send) { }; + + virtual BOOL checkTxReceipt(J2534Frame frame) = 0; + + virtual BOOL isFinished() = 0; + + virtual BOOL txReady() = 0; + + virtual void reset() = 0; + +protected: + J2534Frame fullmsg; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/MessageTxTimeout.cpp b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.cpp new file mode 100644 index 0000000..2e21ba3 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.cpp @@ -0,0 +1,43 @@ +#include "stdafx.h" +#include "J2534Connection.h" +#include "MessageTxTimeout.h" + +MessageTxTimeoutable::MessageTxTimeoutable( + std::weak_ptr connection, + PASSTHRU_MSG& to_send +) : MessageTx(connection, to_send), recvCount(0) { }; + +void MessageTxTimeoutable::scheduleTimeout(std::chrono::microseconds timeoutus) { + if (auto conn_sp = this->connection.lock()) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + auto timeoutobj = std::make_shared(std::static_pointer_cast(shared_from_this()), timeoutus); + panda_dev_sp->scheduleAction(std::static_pointer_cast(timeoutobj), TRUE); + } + } +} + +void MessageTxTimeoutable::scheduleTimeout(unsigned long timeoutus) { + scheduleTimeout(std::chrono::microseconds(timeoutus)); +} + + + +MessageTxTimeout::MessageTxTimeout( + std::shared_ptr msg, + std::chrono::microseconds timeout +) : Action(msg->connection), msg(msg), lastRecvCount(msg->getRecvCount()) { + delay = timeout; +}; + +MessageTxTimeout::MessageTxTimeout( + std::shared_ptr msg, + unsigned long timeout +) : MessageTxTimeout(msg, std::chrono::microseconds(timeout * 1000)) { }; + +void MessageTxTimeout::execute() { + if (auto msg_sp = this->msg.lock()) { + if (msg_sp->getRecvCount() == this->lastRecvCount) { + msg_sp->onTimeout(); + } + } +} diff --git a/drivers/windows/pandaJ2534DLL/MessageTxTimeout.h b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.h new file mode 100644 index 0000000..a9c8784 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTxTimeout.h @@ -0,0 +1,52 @@ +#pragma once +#include "Action.h" +#include "MessageTx.h" + +class MessageTxTimeout; + +/* A special type of MessageTx for multipart messages that supports being canceled with a timeout.*/ +class MessageTxTimeoutable : public MessageTx +{ +public: + MessageTxTimeoutable( + std::weak_ptr connection, + PASSTHRU_MSG& to_send + ); + + unsigned long getRecvCount() { + return recvCount; + } + + virtual void onTimeout() = 0; + +protected: + unsigned long recvCount; + + void scheduleTimeout(std::chrono::microseconds timeoutus); + + void scheduleTimeout(unsigned long timeoutus); +}; + + +/* An Action that cancels MessageTxTimeoutableif the Timeout Actoin executes +before the MessageTxTimeoutableif renews its timeout. +*/ +class MessageTxTimeout : public Action +{ +public: + MessageTxTimeout( + std::shared_ptr msg, + std::chrono::microseconds timeout + ); + + MessageTxTimeout( + std::shared_ptr msg, + unsigned long timeout + ); + + virtual void execute(); + +private: + std::weak_ptr msg; + unsigned long lastRecvCount; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_CAN.cpp b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.cpp new file mode 100644 index 0000000..8217ce5 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.cpp @@ -0,0 +1,44 @@ +#include "stdafx.h" +#include "MessageTx_CAN.h" +#include "J2534Connection_CAN.h" + +MessageTx_CAN::MessageTx_CAN( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send +) : MessageTx(connection_in, to_send), sentyet(FALSE), txInFlight(FALSE) {}; + +void MessageTx_CAN::execute() { + uint32_t addr = ((uint8_t)fullmsg.Data[0]) << 24 | ((uint8_t)fullmsg.Data[1]) << 16 | + ((uint8_t)fullmsg.Data[2]) << 8 | ((uint8_t)fullmsg.Data[3]); + + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + auto payload = fullmsg.Data.substr(4); + if (panda_dev_sp->panda->can_send(addr, check_bmask(this->fullmsg.TxFlags, CAN_29BIT_ID), + (const uint8_t*)payload.c_str(), (uint8_t)payload.size(), panda::PANDA_CAN1) == FALSE) { + return; + } + this->txInFlight = TRUE; + this->sentyet = TRUE; + panda_dev_sp->txMsgsAwaitingEcho.push(shared_from_this()); + } + } +} + +//Returns TRUE if receipt is consumed by the msg, FALSE otherwise. +BOOL MessageTx_CAN::checkTxReceipt(J2534Frame frame) { + if (txReady()) return FALSE; + if (frame.Data == fullmsg.Data && ((this->fullmsg.TxFlags & CAN_29BIT_ID) == (frame.RxStatus & CAN_29BIT_ID))) { + txInFlight = FALSE; + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) + if (conn_sp->loopback) + conn_sp->addMsgToRxQueue(frame); + return TRUE; + } + return FALSE; +} + +void MessageTx_CAN::reset() { + sentyet = FALSE; + txInFlight = FALSE; +} diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_CAN.h b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.h new file mode 100644 index 0000000..afac75e --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_CAN.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include "MessageTx.h" + +class J2534Connection; + +class MessageTx_CAN : public MessageTx +{ +public: + MessageTx_CAN( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send + ); + + virtual void execute(); + + //Returns TRUE if receipt is consumed by the msg, FALSE otherwise. + virtual BOOL checkTxReceipt(J2534Frame frame); + + virtual BOOL isFinished() { + return !txInFlight && sentyet; + }; + + virtual BOOL txReady() { + return !sentyet; + }; + + virtual void reset(); + +private: + BOOL sentyet; + BOOL txInFlight; +}; diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp new file mode 100644 index 0000000..abcf3f6 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp @@ -0,0 +1,180 @@ +#include "stdafx.h" +#include "MessageTx_ISO15765.h" +#include "constants_ISO15765.h" + +//in microseconsa +#define TIMEOUT_FC 250000 //Flow Control +#define TIMEOUT_CF 250000 //Consecutive Frames + +MessageTx_ISO15765::MessageTx_ISO15765( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send, + std::shared_ptr filter +) : MessageTxTimeoutable(connection_in, to_send), filter(filter), frames_sent(0), +consumed_count(0), txInFlight(FALSE), sendAll(FALSE), block_size(0), numWaitFrames(0), didtimeout(FALSE), issuspended(FALSE){ + + CANid = ((uint8_t)fullmsg.Data[0]) << 24 | ((uint8_t)fullmsg.Data[1]) << 16 | + ((uint8_t)fullmsg.Data[2]) << 8 | ((uint8_t)fullmsg.Data[3]); + + payload = fullmsg.Data.substr(addressLength()); + + if (check_bmask(fullmsg.TxFlags, ISO15765_ADDR_TYPE)) + data_prefix = fullmsg.Data[4]; + + if (payload.size() <= (7 - data_prefix.size())) { + isMultipart = FALSE; + auto framepayload = data_prefix + std::string(1, (char)payload.size()) + payload; + if (check_bmask(this->fullmsg.TxFlags, ISO15765_FRAME_PAD)) + framepayload += std::string(8 - framepayload.size(), '\x00'); + framePayloads.push_back(framepayload); + } else { + isMultipart = TRUE; + unsigned long first_payload_len = 6 - data_prefix.size(); // 5 or 6 + std::string framepayload = data_prefix + + (char)(0x10 | ((payload.size() >> 8) & 0xF)) + + (char)(payload.size() & 0xFF) + + payload.substr(0, first_payload_len); + framePayloads.push_back(framepayload); + + unsigned int pktnum = 1; + uint8_t CFDatSize = 7 - data_prefix.size(); + while (TRUE) { + framepayload = data_prefix + (char)(0x20 | (pktnum % 0x10)) + + payload.substr(first_payload_len + (CFDatSize * (pktnum-1)), CFDatSize); + + if (check_bmask(this->fullmsg.TxFlags, ISO15765_FRAME_PAD)) + framepayload += std::string(8 - framepayload.size(), '\x00'); + framePayloads.push_back(framepayload); + if (first_payload_len + (CFDatSize * pktnum) >= payload.size()) break; + pktnum++; + } + + } +}; + +unsigned int MessageTx_ISO15765::addressLength() { + return check_bmask(fullmsg.TxFlags, ISO15765_ADDR_TYPE) ? 5 : 4; +} + +void MessageTx_ISO15765::execute() { + if (didtimeout || issuspended) return; + if (this->frames_sent >= this->framePayloads.size()) return; + if (block_size == 0 && !sendAll && this->frames_sent > 0) return; + if (block_size > 0 && !sendAll) block_size--; + + if (auto conn_sp = this->connection.lock()) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + auto& outFramePayload = this->framePayloads[this->frames_sent]; + if (panda_dev_sp->panda->can_send(this->CANid, check_bmask(this->fullmsg.TxFlags, CAN_29BIT_ID), + (const uint8_t*)outFramePayload.c_str(), (uint8_t)outFramePayload.size(), panda::PANDA_CAN1) == FALSE) { + return; + } + + this->txInFlight = TRUE; + this->frames_sent++; + panda_dev_sp->txMsgsAwaitingEcho.push(shared_from_this()); + } + } +} + +//Returns TRUE if receipt is consumed by the msg, FALSE otherwise. +BOOL MessageTx_ISO15765::checkTxReceipt(J2534Frame frame) { + if (!txInFlight) return FALSE; + if (frame.Data.size() >= addressLength() + 1 && (frame.Data[addressLength()] & 0xF0) == FRAME_FLOWCTRL) return FALSE; + + if (frame.Data == fullmsg.Data.substr(0, 4) + framePayloads[frames_sent - 1] && + ((this->fullmsg.TxFlags & CAN_29BIT_ID) == (frame.RxStatus & CAN_29BIT_ID))) { //Check receipt is expected + txInFlight = FALSE; //Received the expected receipt. Allow another msg to be sent. + + if (this->recvCount == 0 && this->framePayloads.size() > 1) + scheduleTimeout(TIMEOUT_FC); + + if (frames_sent == framePayloads.size()) { //Check message done + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) { + unsigned long flags = (filter == nullptr) ? fullmsg.TxFlags : this->filter->flags; + + J2534Frame outframe(ISO15765); + outframe.Timestamp = frame.Timestamp; + outframe.RxStatus = TX_INDICATION | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID)); + outframe.Data = frame.Data.substr(0, addressLength()); + conn_sp->addMsgToRxQueue(outframe); + + if (conn_sp->loopback) { + J2534Frame outframe(ISO15765); + outframe.Timestamp = frame.Timestamp; + outframe.RxStatus = TX_MSG_TYPE | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID)); + outframe.Data = this->fullmsg.Data; + conn_sp->addMsgToRxQueue(outframe); + } + + } //TODO what if fails + } else { + //Restart timeout if we are waiting for a flow control frame. + //FC frames are required when we are not sending all, the + //current block_size batch has not been sent, a FC message has + //already been received (differentiating from first frame), the + //message is not finished, and there is more than one frame in + //the message. + if (block_size == 0 && recvCount != 0 && !sendAll && !this->isFinished() && this->framePayloads.size() > 1) + scheduleTimeout(TIMEOUT_CF); + } + return TRUE; + } + return FALSE; +} + +BOOL MessageTx_ISO15765::isFinished() { + return this->frames_sent == this->framePayloads.size() && !txInFlight; +} + +BOOL MessageTx_ISO15765::txReady() { + return block_size > 0 || sendAll || this->frames_sent == 0; +} + +void MessageTx_ISO15765::reset() { + frames_sent = 0; + consumed_count = 0; + block_size = 0; + txInFlight = FALSE; + sendAll = FALSE; + numWaitFrames = 0; + didtimeout = FALSE; +} + +void MessageTx_ISO15765::onTimeout() { + didtimeout = TRUE; + if (auto conn_sp = std::static_pointer_cast(this->connection.lock())) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + panda_dev_sp->removeConnectionTopAction(conn_sp, shared_from_this()); + } + } +} + +void MessageTx_ISO15765::flowControlContinue(uint8_t block_size, std::chrono::microseconds separation_time) { + this->issuspended = FALSE; + this->block_size = block_size; + this->delay = separation_time; + this->sendAll = block_size == 0; + this->recvCount++; +} + +void MessageTx_ISO15765::flowControlWait(unsigned long N_WFTmax) { + this->issuspended = TRUE; + this->recvCount++; + this->numWaitFrames++; + this->sendAll = FALSE; + this->block_size = block_size; + this->delay = std::chrono::microseconds(0); + //Docs are vague on if 0 means NO WAITS ALLOWED or NO LIMIT TO WAITS. + //It is less likely to cause issue if NO LIMIT is assumed. + if (N_WFTmax > 0 && this->numWaitFrames > N_WFTmax) { + this->onTimeout(); //Trigger self destruction of message. + } else { + scheduleTimeout(TIMEOUT_FC); + } +} + +void MessageTx_ISO15765::flowControlAbort() { + this->recvCount++; //Invalidate future timeout actions. + this->onTimeout(); //Trigger self destruction of message. +} diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.h b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.h new file mode 100644 index 0000000..0113edb --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.h @@ -0,0 +1,54 @@ +#pragma once +#include "MessageTxTimeout.h" +#include "J2534Connection_ISO15765.h" + +class J2534Connection_ISO15765; + +/** +A specialized message type that can handle J2534 single and multi +frame (with flow control) writes. +*/ +class MessageTx_ISO15765 : public MessageTxTimeoutable +{ +public: + MessageTx_ISO15765( + std::shared_ptr connection, + PASSTHRU_MSG& to_send, + std::shared_ptr filter + ); + + unsigned int addressLength(); + + virtual void execute(); + + virtual BOOL checkTxReceipt(J2534Frame frame); + + virtual BOOL isFinished(); + + virtual BOOL txReady(); + + virtual void reset(); + + virtual void onTimeout(); + + //Functions for ISO15765 flow control + + void MessageTx_ISO15765::flowControlContinue(uint8_t block_size, std::chrono::microseconds separation_time); + void MessageTx_ISO15765::flowControlWait(unsigned long N_WFTmax); + void MessageTx_ISO15765::flowControlAbort(); + + std::shared_ptr filter; + unsigned long frames_sent; + unsigned long consumed_count; + uint8_t block_size; + unsigned long CANid; + std::string data_prefix; + std::string payload; + BOOL isMultipart; + std::vector framePayloads; + BOOL txInFlight; + BOOL sendAll; + unsigned int numWaitFrames; + BOOL didtimeout; + BOOL issuspended; +}; diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp new file mode 100644 index 0000000..64da8e2 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp @@ -0,0 +1,214 @@ +#include "stdafx.h" +#include "PandaJ2534Device.h" +#include "J2534Frame.h" + +PandaJ2534Device::PandaJ2534Device(std::unique_ptr new_panda) : txInProgress(FALSE) { + this->panda = std::move(new_panda); + + this->panda->set_esp_power(FALSE); + this->panda->set_safety_mode(panda::SAFETY_ALLOUTPUT); + this->panda->set_can_loopback(FALSE); + this->panda->set_alt_setting(1); + + DWORD canListenThreadID; + this->thread_kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); + this->can_thread_handle = CreateThread(NULL, 0, _can_recv_threadBootstrap, (LPVOID)this, 0, &canListenThreadID); + + DWORD flowControlSendThreadID; + this->flow_control_wakeup_event = CreateEvent(NULL, TRUE, FALSE, NULL); + this->flow_control_thread_handle = CreateThread(NULL, 0, _msg_tx_threadBootstrap, (LPVOID)this, 0, &flowControlSendThreadID); +}; + +PandaJ2534Device::~PandaJ2534Device() { + SetEvent(this->thread_kill_event); + DWORD res = WaitForSingleObject(this->can_thread_handle, INFINITE); + CloseHandle(this->can_thread_handle); + + res = WaitForSingleObject(this->flow_control_thread_handle, INFINITE); + CloseHandle(this->flow_control_thread_handle); + + CloseHandle(this->flow_control_wakeup_event); + CloseHandle(this->thread_kill_event); +} + +std::shared_ptr PandaJ2534Device::openByName(std::string sn) { + auto p = panda::Panda::openPanda(""); + if (p == nullptr) + return nullptr; + return std::unique_ptr(new PandaJ2534Device(std::move(p))); +} + +DWORD PandaJ2534Device::closeChannel(unsigned long ChannelID) { + if (this->connections.size() <= ChannelID) return ERR_INVALID_CHANNEL_ID; + if (this->connections[ChannelID] == nullptr) return ERR_INVALID_CHANNEL_ID; + this->connections[ChannelID] = nullptr; + return STATUS_NOERROR; +} + +DWORD PandaJ2534Device::addChannel(std::shared_ptr& conn, unsigned long* channel_id) { + int channel_index = -1; + for (unsigned int i = 0; i < this->connections.size(); i++) + if (this->connections[i] == nullptr) { + channel_index = i; + break; + } + + if (channel_index == -1) { + if (this->connections.size() == 0xFFFF) //channelid max 16 bits + return ERR_FAILED; //Too many channels + this->connections.push_back(nullptr); + channel_index = this->connections.size() - 1; + } + + this->connections[channel_index] = conn; + + *channel_id = channel_index; + return STATUS_NOERROR; +} + +DWORD PandaJ2534Device::can_recv_thread() { + DWORD err = TRUE; + while (err) { + std::vector msg_recv; + err = this->panda->can_recv_async(this->thread_kill_event, msg_recv); + for (auto msg_in : msg_recv) { + J2534Frame msg_out(msg_in); + + if (msg_in.is_receipt) { + synchronized(task_queue_mutex) { + if (txMsgsAwaitingEcho.size() > 0) { + auto msgtx = txMsgsAwaitingEcho.front(); + if (auto conn = msgtx->connection.lock()) { + if (conn->isProtoCan() && conn->getPort() == msg_in.bus) { + if (msgtx->checkTxReceipt(msg_out)) { + //Things to check: + // Frame not for this msg: Drop frame and alert. Error? + // Frame is for this msg, more tx frames required after a FC frame: Wait for FC frame to come and trigger next tx. + // Frame is for this msg, more tx frames required: Schedule next tx frame. + // Frame is for this msg, and is the final frame of the msg: Let conn process full msg, If another msg from this conn is available, register it. + txMsgsAwaitingEcho.pop(); //Remove the TX object and schedule record. + + if (msgtx->isFinished()) { + this->removeConnectionTopAction(conn, msgtx); + } else { + if (msgtx->txReady()) { //Not finished, ready to send next frame. + msgtx->schedule(msg_in.recv_time_point, TRUE); + this->insertActionIntoTaskList(msgtx); + } else { + //Not finished, but next frame not ready (maybe waiting for flow control). + //Do not schedule more messages from this connection. + //this->ConnTxSet.erase(conn); + //Removing this means new messages queued can kickstart the queue and overstep the current message. + } + } + } + } + } else { + //Connection has died. Clear out the tx entry from device records. + txMsgsAwaitingEcho.pop(); + this->ConnTxSet.erase(conn); //connection is already dead, no need to schedule future tx msgs. + } + } + } + } else { + for (auto& conn : this->connections) + if (conn->isProtoCan() && conn->getPort() == msg_in.bus) + conn->processMessage(msg_out); + } + } + } + + return 0; +} + +DWORD PandaJ2534Device::msg_tx_thread() { + const HANDLE subscriptions[] = { this->flow_control_wakeup_event, this->thread_kill_event }; + DWORD sleepDuration = INFINITE; + while (TRUE) { + DWORD res = WaitForMultipleObjects(2, subscriptions, FALSE, sleepDuration); + if (res == WAIT_OBJECT_0 + 1) return 0; + if (res != WAIT_OBJECT_0 && res != WAIT_TIMEOUT) { + printf("Got an unexpected wait result in flow_control_write_thread. Res: %d; GetLastError: %d\n. Terminating thread.", res, GetLastError()); + return 0; + } + ResetEvent(this->flow_control_wakeup_event); + + while (TRUE) { + synchronized(task_queue_mutex) { //implemented with for loop. Consumes breaks. + if (this->task_queue.size() == 0) { + sleepDuration = INFINITE; + goto break_flow_ctrl_loop; + } + if (std::chrono::steady_clock::now() >= this->task_queue.front()->expire) { + auto task = this->task_queue.front(); //Get the scheduled tx record. + this->task_queue.pop_front(); + task->execute(); + } else { //Ran out of things that need to be sent now. Sleep! + auto time_diff = std::chrono::duration_cast + (this->task_queue.front()->expire - std::chrono::steady_clock::now()); + sleepDuration = max(1, time_diff.count()); + goto break_flow_ctrl_loop; + } + } + } + break_flow_ctrl_loop: + continue; + } + return 0; +} + +//Place the Action in the task queue based on the Action's expiration time, +//then signal the thread that processes actions. +void PandaJ2534Device::insertActionIntoTaskList(std::shared_ptr action) { + synchronized(task_queue_mutex) { + auto iter = this->task_queue.begin(); + for (; iter != this->task_queue.end(); iter++) { + if (action->expire < (*iter)->expire) break; + } + this->task_queue.insert(iter, action); + } + SetEvent(this->flow_control_wakeup_event); +} + +void PandaJ2534Device::scheduleAction(std::shared_ptr msg, BOOL startdelayed) { + if(startdelayed) + msg->scheduleImmediateDelay(); + else + msg->scheduleImmediate(); + this->insertActionIntoTaskList(msg); +} + +void PandaJ2534Device::registerConnectionTx(std::shared_ptr conn) { + synchronized(connTXSet_mutex) { + auto ret = this->ConnTxSet.insert(conn); + if (ret.second == FALSE) return; //Conn already exists. + this->scheduleAction(conn->txbuff.front()); + } +} + +void PandaJ2534Device::unstallConnectionTx(std::shared_ptr conn) { + synchronized(connTXSet_mutex) { + auto ret = this->ConnTxSet.insert(conn); + if (ret.second == TRUE) return; //Conn already exists. + this->insertActionIntoTaskList(conn->txbuff.front()); + } +} + +void PandaJ2534Device::removeConnectionTopAction(std::shared_ptr conn, std::shared_ptr msg) { + synchronized(task_queue_mutex) { + if (conn->txbuff.size() == 0) + return; + if (conn->txbuff.front() != msg) + return; + conn->txbuff.pop(); //Remove the top TX message from the connection tx queue. + + //Remove the connection from the active connection list if no more messages are scheduled with this connection. + if (conn->txbuff.size() == 0) { + //Update records showing the connection no longer has a tx record scheduled. + this->ConnTxSet.erase(conn); + } else { + //Add the next scheduled tx from this conn + this->scheduleAction(conn->txbuff.front()); + } + } +} diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h new file mode 100644 index 0000000..3e5880c --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h @@ -0,0 +1,77 @@ +#pragma once +#include +#include +#include +#include +#include +#include "J2534_v0404.h" +#include "panda/panda.h" +#include "synchronize.h" +#include "Action.h" +#include "MessageTx.h" +#include "J2534Connection.h" + +class J2534Connection; +class Action; +class MessageTx; + +/** +Class representing a physical panda adapter. Instances are created by +PassThruOpen in the J2534 API. A Device can create one or more +J2534Connections. +*/ +class PandaJ2534Device { +public: + PandaJ2534Device(std::unique_ptr new_panda); + + ~PandaJ2534Device(); + + static std::shared_ptr openByName(std::string sn); + + DWORD closeChannel(unsigned long ChannelID); + DWORD addChannel(std::shared_ptr& conn, unsigned long* channel_id); + + std::unique_ptr panda; + std::vector> connections; + + //Place the Action in the task queue based on the Action's expiration time, + //then signal the thread that processes actions. + void insertActionIntoTaskList(std::shared_ptr action); + + void scheduleAction(std::shared_ptr msg, BOOL startdelayed=FALSE); + + void registerConnectionTx(std::shared_ptr conn); + + //Resume sending messages from the provided Connection's TX queue. + void unstallConnectionTx(std::shared_ptr conn); + + //Cleans up several queues after a message completes, is canceled, or otherwise goes away. + void removeConnectionTopAction(std::shared_ptr conn, std::shared_ptr msg); + + //Messages that have been sent on the wire will be echoed by the panda when + //transmission is complete. This tracks what is still waiting to hear an echo. + std::queue> txMsgsAwaitingEcho; + +private: + HANDLE thread_kill_event; + + HANDLE can_thread_handle; + static DWORD WINAPI _can_recv_threadBootstrap(LPVOID This) { + return ((PandaJ2534Device*)This)->can_recv_thread(); + } + DWORD can_recv_thread(); + + HANDLE flow_control_wakeup_event; + HANDLE flow_control_thread_handle; + static DWORD WINAPI _msg_tx_threadBootstrap(LPVOID This) { + return ((PandaJ2534Device*)This)->msg_tx_thread(); + } + DWORD msg_tx_thread(); + std::list> task_queue; + Mutex task_queue_mutex; + + std::queue> ConnTxQueue; + std::set> ConnTxSet; + Mutex connTXSet_mutex; + BOOL txInProgress; +}; diff --git a/drivers/windows/pandaJ2534DLL/Timer.cpp b/drivers/windows/pandaJ2534DLL/Timer.cpp new file mode 100644 index 0000000..2f20f88 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/Timer.cpp @@ -0,0 +1,17 @@ +#include "stdafx.h" +#include "Timer.h" + + +Timer::Timer() +{ + start = std::chrono::time_point_cast(clock::now()); +} + +// gets the time elapsed from construction. +unsigned long long /*milliseconds*/ Timer::getTimePassed(){ + // get the new time + auto end = std::chrono::time_point_cast(clock::now()); + + // return the difference of the times + return (end - start).count(); +} \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/Timer.h b/drivers/windows/pandaJ2534DLL/Timer.h new file mode 100644 index 0000000..d4888fc --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/Timer.h @@ -0,0 +1,18 @@ +#pragma once +#include + +//Copied from https://stackoverflow.com/a/31488113 + +class Timer +{ + using clock = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point < clock, std::chrono::milliseconds >; +public: + Timer(); + + // gets the time elapsed from construction. + unsigned long long /*milliseconds*/ getTimePassed(); + +private: + time_point_type start; +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/constants_ISO15765.h b/drivers/windows/pandaJ2534DLL/constants_ISO15765.h new file mode 100644 index 0000000..86928f1 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/constants_ISO15765.h @@ -0,0 +1,20 @@ +#pragma once + +#define msg_is_extaddr(msg) check_bmask(msg->TxFlags, ISO15765_ADDR_TYPE) +#define msg_is_padded(msg) check_bmask(msg->TxFlags, ISO15765_FRAME_PAD) + +#define FRAME_SINGLE 0x00 +#define FRAME_FIRST 0x10 +#define FRAME_CONSEC 0x20 +#define FRAME_FLOWCTRL 0x30 + +#define FLOWCTRL_CONTINUE 0 +#define FLOWCTRL_WAIT 1 +#define FLOWCTRL_ABORT 2 + +#define msg_get_type(msg, addrlen) ((msg).Data[addrlen] & 0xF0) + +#define is_single(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_SINGLE) +#define is_first(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_FIRST) +#define is_consecutive(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_CONSEC) +#define is_flowctrl(msg, addrlen) (msg_get_type(msg, addrlen) == FRAME_FLOWCTRL) \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/dllmain.cpp b/drivers/windows/pandaJ2534DLL/dllmain.cpp new file mode 100644 index 0000000..d4122e0 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/dllmain.cpp @@ -0,0 +1,22 @@ +// dllmain.cpp : Defines the entry point for the DLL application. +#include "dllmain.h" + +HMODULE thisdll; + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + thisdll = hModule; + + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} diff --git a/drivers/windows/pandaJ2534DLL/dllmain.h b/drivers/windows/pandaJ2534DLL/dllmain.h new file mode 100644 index 0000000..f49819e --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/dllmain.h @@ -0,0 +1,4 @@ +#pragma once +#include "stdafx.h" + +extern HMODULE thisdll; diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp new file mode 100644 index 0000000..2e706f5 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp @@ -0,0 +1,433 @@ +// 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> 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 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 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(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(panda, ProtocolID, Flags, BaudRate); + break; + case CAN: + case CAN_PS: + //case SW_CAN_PS: + conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); + break; + case ISO15765: + case ISO15765_PS: + conn = std::make_shared(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 . + // PassThruWriteMsgs() - Device could not write the specified number of messages. The actual number of messages sent on the vehicle network is placed in . + 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); +} diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.rc b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.rc new file mode 100644 index 0000000..e359044 Binary files /dev/null and b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.rc differ diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj new file mode 100644 index 0000000..065fa69 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj @@ -0,0 +1,152 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {A2BB18A5-F26B-48D6-BBB5-B83D64473C77} + Win32Proj + pandaJ2534DLL + 8.1 + + + + DynamicLibrary + true + v140 + Unicode + + + DynamicLibrary + false + v140 + true + Unicode + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + pandaJ2534_0404_32 + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + pandaJ2534_0404_32 + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;_USRDLL;PANDAJ2534DLL_EXPORTS;%(PreprocessorDefinitions) + true + $(SolutionDir); + + + Windows + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);version.lib;$(OutDir)panda.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;_USRDLL;PANDAJ2534DLL_EXPORTS;%(PreprocessorDefinitions) + true + $(SolutionDir); + + + Windows + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);version.lib;$(OutDir)panda.lib + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + false + + + + + + + + + + + + + + + Create + Create + + + + + + {5528aefb-638d-49af-b9d4-965154e7d531} + + + + + + + + + + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters new file mode 100644 index 0000000..57f7cef --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters @@ -0,0 +1,155 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {a4cd0bce-0a2a-43d9-9c9f-b21a3b607e90} + + + {a85ee263-380d-4d37-b167-6629cfd5177f} + + + {010a0176-a146-4d3a-824a-fd683904774d} + + + {71c9502a-ee59-4d5e-873f-c9cc792e7c76} + + + {4fd3183a-c457-430c-b762-f767a5788bca} + + + {53cd179e-22d8-43e2-bc61-516d3861fae6} + + + {08d548b5-4d0b-4ce4-85e6-5ff3fc987758} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\J2534_CAN + + + Header Files\J2534_ISO15765 + + + Header Files\depends + + + Header Files\depends + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files + + + Header Files\J2534_ISO15765 + + + Header Files + + + Header Files\J2534_ISO15765 + + + Header Files\J2534_CAN + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\J2534_CAN + + + Source Files\J2534_ISO15765 + + + Source Files\boilerplate + + + Source Files\boilerplate + + + Source Files\J2534_ISO15765 + + + Source Files + + + Source Files\J2534_CAN + + + Source Files + + + + + Resource Files + + + + + + \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/resource.h b/drivers/windows/pandaJ2534DLL/resource.h new file mode 100644 index 0000000..771e7b8 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by pandaJ2534DLL.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/drivers/windows/pandaJ2534DLL/stdafx.cpp b/drivers/windows/pandaJ2534DLL/stdafx.cpp new file mode 100644 index 0000000..c27db9e --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// pandaJ2534DLL.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/pandaJ2534DLL/stdafx.h b/drivers/windows/pandaJ2534DLL/stdafx.h new file mode 100644 index 0000000..bd4a4b6 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/stdafx.h @@ -0,0 +1,14 @@ +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + +#include +#include +#include +#include +#include +#include \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/synchronize.h b/drivers/windows/pandaJ2534DLL/synchronize.h new file mode 100644 index 0000000..446dfc1 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/synchronize.h @@ -0,0 +1,56 @@ +#pragma once +#define WIN32_LEAN_AND_MEAN +#include + +//Inspired/directly copied from https://www.codeproject.com/Articles/12362/A-quot-synchronized-quot-statement-for-C-like-in-J +//Enables easier synchronization +class Mutex { +public: + Mutex() { + InitializeCriticalSectionAndSpinCount(&critSection, 0x00000400); + //InitializeCriticalSection(&critSection); + } + + ~Mutex() { + DeleteCriticalSection(&critSection); + } + + void lock() { + EnterCriticalSection(&critSection); + } + + void unlock() { + LeaveCriticalSection(&critSection); + } + +private: + CRITICAL_SECTION critSection; +}; + +//Synchronization Controller Object +class Lock { +public: + Lock(Mutex &m) : mutex(m), locked(TRUE) { + m.lock(); + } + + ~Lock() { + mutex.unlock(); + } + + operator bool() const { + return locked; + } + + void setUnlock() { + locked = FALSE; + } + +private: + Mutex& mutex; + bool locked; +}; + +//A useful shorthand for locking and unlocking a mutex over a scope. +//CAUTION, implemented with a for loop, so break/continue are consumed. +#define synchronized(M) for(Lock M##_lock = M; M##_lock; M##_lock.setUnlock()) diff --git a/drivers/windows/pandaJ2534DLL/targetver.h b/drivers/windows/pandaJ2534DLL/targetver.h new file mode 100644 index 0000000..1bf4ee6 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/targetver.h @@ -0,0 +1,13 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include + +#define WINVER _WIN32_WINNT_WIN7 +#define _WIN32_WINNT _WIN32_WINNT_WIN7 + +#include diff --git a/drivers/windows/panda_install.nsi b/drivers/windows/panda_install.nsi new file mode 100644 index 0000000..81ecc3c --- /dev/null +++ b/drivers/windows/panda_install.nsi @@ -0,0 +1,212 @@ +!define J2534_Reg_Path "Software\PassThruSupport.04.04\comma.ai - panda" +!define Install_Name "panda J2534 Drivers" + +;NOTE! The panda software requires a VC runtime to be installed in order to work. +;This installer must be bundled with the appropriate runtime installer, and have +;the installation registry key set so the installer can tell if the runtime is +;already installed. Copy vscruntimeinfo.nsh.sample to vscruntimeinfo.nsh and edit +;it for your version of Visual Studio. +!include "redist\vscruntimeinfo.nsh" + +;-------------------------------- +;Include Modern UI +!include "MUI2.nsh" +!include "x64.nsh" + +!define MUI_ICON "panda.ico" +;NSIS is ignoring the unicon unless it is the same as the normal icon +;!define MUI_UNICON "panda_remove.ico" + +;Properly display all languages (Installer will not work on Windows 95, 98 or ME!) +Unicode true + +# Set the installer display name +Name "panda Driver" + +# set the name of the installer +Outfile "panda install.exe" + +; The default installation directory +InstallDir $PROGRAMFILES\comma.ai\panda + +; Request application privileges for UAC +RequestExecutionLevel admin + +; Registry key to check for directory (so if you install again, it will +; overwrite the old one automatically) +InstallDirRegKey HKLM "SOFTWARE\${Install_Name}" "Install_Dir" + +;-------------------------------- +; Pages +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "..\..\LICENSE" +!insertmacro MUI_PAGE_COMPONENTS +!insertmacro MUI_PAGE_DIRECTORY +!insertmacro MUI_PAGE_INSTFILES + +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +!insertmacro MUI_LANGUAGE "English" ;first language is the default language + +; ------------------------------------------------------------------------------------------------- +; Additional info (will appear in the "details" tab of the properties window for the installer) + +VIAddVersionKey /LANG=${LANG_ENGLISH} "ProductName" "panda OBD-II adapter" +VIAddVersionKey /LANG=${LANG_ENGLISH} "Comments" "" +VIAddVersionKey /LANG=${LANG_ENGLISH} "CompanyName" "comma.ai" +VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalTrademarks" "Application released under the MIT license" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "LegalCopyright" "© ${PRODUCT_NAME} Team" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "FileDescription" "Jessy Exum" +;VIAddVersionKey /LANG=${LANG_ENGLISH} "FileVersion" "${PRODUCT_VERSION}" +VIProductVersion "1.0.0.0" + +;-------------------------------- +; Install Sections +Section "panda driver (required)" + + SectionIn RO + + SetOutPath "$INSTDIR" + + + ;If the visual studio version this project is compiled with changes, this section + ;must be revisited. The registry key must be changed, and the VS redistributable + ;binary must be updated to the VS version used. + ClearErrors + ReadRegStr $0 HKLM ${VCRuntimeRegKey} "Version" + ${If} ${Errors} + DetailPrint "Installing Visual Studio C Runtime..." + File "${VCRuntimeSetupPath}\${VCRuntimeSetupFile}" + ExecWait '"$INSTDIR\${VCRuntimeSetupFile}" /passive /norestart' + ${Else} + DetailPrint "Visual Studio C Runtime already installed." + ${EndIf} + + ;Remove the now unnecessary runtime installer. + Delete "$INSTDIR\${VCRuntimeSetupFile}" + + ;Do the rest of the install + SetOutPath "$INSTDIR\driver" + + ; The inf file works for both 32 and 64 bit. + File "Debug_x86\panda Driver Package\panda.inf" + File "Debug_x86\panda Driver Package\panda.cat" + ${DisableX64FSRedirection} + nsExec::ExecToLog '"$SYSDIR\PnPutil.exe" /a "$INSTDIR\driver\panda.inf"' + ${EnableX64FSRedirection} + + SetOutPath $SYSDIR + + File Release_x86\panda.dll + + ${If} ${RunningX64} + ${DisableX64FSRedirection} + ;Note that the x64 VS redistributable is not installed to prevent bloat. + ;If you are the rare person who uses the 64 bit raw panda driver, please + ;install the correct x64 VS runtime manually. + File Release_x64\panda.dll + ${EnableX64FSRedirection} + ${EndIf} + + ; Write the installation path into the registry + WriteRegStr HKLM "SOFTWARE\panda J2534 Drivers" "Install_Dir" "$INSTDIR" + + ; Write the uninstall keys for Windows + ;WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "DisplayVersion" "" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "DisplayIcon" '"$SYSDIR\panda.dll",0' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "DisplayName" "panda J2534 Drivers" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "Publisher" "comma.ai" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "UninstallString" '"$INSTDIR\uninstall.exe"' + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "URLInfoAbout" "https://github.com/commaai/panda/" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" "NoRepair" 1 + + SetOutPath $INSTDIR + WriteUninstaller "uninstall.exe" + +SectionEnd + +Section "panda devel lib/header" + + SetOutPath "$INSTDIR\devel" + File panda\panda.h + + SetOutPath "$INSTDIR\devel\x86" + File Release_x86\panda.lib + + SetOutPath "$INSTDIR\devel\x64" + File Release_x64\panda.lib + +SectionEnd + +Section "J2534 Driver" + + SetOutPath $INSTDIR + + File Release_x86\pandaJ2534_0404_32.dll + + SetRegView 32 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "CAN" 00000001 + WriteRegStr HKLM "${J2534_Reg_Path}" "FunctionLibrary" "$INSTDIR\pandaJ2534_0404_32.dll" + WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO15765" 00000001 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "J1850VPW" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_A_ENGINE" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_A_TRANS" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_B_ENGINE" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_B_TRANS" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "J1850PWM" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO9141" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO14230" 00000001 + WriteRegStr HKLM "${J2534_Reg_Path}" "Name" "panda" + WriteRegStr HKLM "${J2534_Reg_Path}" "Vendor" "comma.ai" + WriteRegStr HKLM "${J2534_Reg_Path}" "ConfigApplication" "" + DetailPrint "Registered J2534 Driver" + +SectionEnd + +;-------------------------------- +; Uninstaller +Section "Uninstall" + + ; Removing the inf file for winusb is not easy to do. + ; The best solution I can find is parsing the output + ; of the pnputil.exe /e command to find the oem#.inf + ; file that lists comma.ai as the provider. Not sure + ; if Microsoft wants these inf files to be removed. + ; Consider https://blog.sverrirs.com/2015/12/creating-windows-installer-and.html + ; These lines just remove the inf backups. + Delete "$INSTDIR\driver\panda.inf" + Delete "$INSTDIR\driver\panda.cat" + RMDir "$INSTDIR\driver" + + ; Remove WinUSB driver library + Delete $SYSDIR\panda.dll + ${If} ${RunningX64} + ${DisableX64FSRedirection} + Delete $SYSDIR\panda.dll + ${EnableX64FSRedirection} + ${EndIf} + + ; Remove devel files + Delete "$INSTDIR\devel\x86\panda.lib" + RMDir "$INSTDIR\devel\x86" + Delete "$INSTDIR\devel\x64\panda.lib" + RMDir "$INSTDIR\devel\x64" + Delete "$INSTDIR\devel\panda.h" + RMDir "$INSTDIR\devel" + + ; Remove registry keys + DeleteRegKey HKLM "${J2534_Reg_Path}" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${Install_Name}" + DeleteRegKey HKLM "SOFTWARE\${Install_Name}" + + ; Remove files and uninstaller + Delete "$INSTDIR\uninstall.exe" + Delete "$INSTDIR\pandaJ2534_0404_32.dll" + + ; Remove directories used + RMDir "$INSTDIR" + RMDir "$PROGRAMFILES\comma.ai" + +SectionEnd diff --git a/drivers/windows/panda_playground/ReadMe.txt b/drivers/windows/panda_playground/ReadMe.txt new file mode 100644 index 0000000..37dba5d --- /dev/null +++ b/drivers/windows/panda_playground/ReadMe.txt @@ -0,0 +1,40 @@ +======================================================================== + CONSOLE APPLICATION : panda_playground Project Overview +======================================================================== + +AppWizard has created this panda_playground application for you. + +This file contains a summary of what you will find in each of the files that +make up your panda_playground application. + + +panda_playground.vcxproj + This is the main project file for VC++ projects generated using an Application Wizard. + It contains information about the version of Visual C++ that generated the file, and + information about the platforms, configurations, and project features selected with the + Application Wizard. + +panda_playground.vcxproj.filters + This is the filters file for VC++ projects generated using an Application Wizard. + It contains information about the association between the files in your project + and the filters. This association is used in the IDE to show grouping of files with + similar extensions under a specific node (for e.g. ".cpp" files are associated with the + "Source Files" filter). + +panda_playground.cpp + This is the main application source file. + +///////////////////////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named panda_playground.pch and a precompiled types file named StdAfx.obj. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" comments to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/drivers/windows/panda_playground/panda_playground.cpp b/drivers/windows/panda_playground/panda_playground.cpp new file mode 100644 index 0000000..0f51924 --- /dev/null +++ b/drivers/windows/panda_playground/panda_playground.cpp @@ -0,0 +1,86 @@ +// panda_playground.cpp : Defines the entry point for the console application. +// + +#include "stdafx.h" +#include "pandaJ2534DLL Test\Loader4.h" +#include "ECUsim DLL\ECUsim.h" +#include + + +int _tmain(int Argc, _TCHAR *Argv) { + UNREFERENCED_PARAMETER(Argc); + UNREFERENCED_PARAMETER(Argv); + + ECUsim sim("", 500000); + + //if (LoadJ2534Dll("C:\\WINDOWS\\SysWOW64\\op20pt32.dll") != 0) { + if (LoadJ2534Dll("pandaJ2534.dll") != 0) { + auto err = GetLastError(); + return 1; + } + unsigned long did, cid, fid; + PassThruOpen("", &did); + PassThruConnect(did, ISO15765, CAN_29BIT_ID, 500000, &cid); + + PASSTHRU_MSG mask, pattern, flow; + + memcpy(mask.Data, "\xff\xff\xff\xff", 4); + mask.DataSize = 4; + mask.ProtocolID = ISO15765; + mask.TxFlags = CAN_29BIT_ID; + mask.ExtraDataIndex = 0; + mask.RxStatus = 0; + + ////////////////////////18//DA//F1//EF + memcpy(pattern.Data, "\x18\xda\xf1\xef", 4); + pattern.DataSize = 4; + pattern.ProtocolID = ISO15765; + pattern.TxFlags = CAN_29BIT_ID; + pattern.ExtraDataIndex = 0; + pattern.RxStatus = 0; + + memcpy(flow.Data, "\x18\xda\xef\xf1", 4); + flow.DataSize = 4; + flow.ProtocolID = ISO15765; + flow.TxFlags = CAN_29BIT_ID; + flow.ExtraDataIndex = 0; + flow.RxStatus = 0; + + auto res = PassThruStartMsgFilter(cid, FLOW_CONTROL_FILTER, &mask, &pattern, &flow, &fid); + if (res != STATUS_NOERROR) + return 1; + + SCONFIG_LIST list; + SCONFIG config; + config.Parameter = LOOPBACK; + config.Value = 0; + list.ConfigPtr = &config; + list.NumOfParams = 1; + + res = PassThruIoctl(cid, SET_CONFIG, &list, NULL); + if (res != STATUS_NOERROR) + return 1; + + PASSTHRU_MSG outmsg; + memcpy(outmsg.Data, "\x18\xda\xef\xf1""\xAA\xBB\xCC\xDD\xEE\xFF\x11\x22\x33\x44", 4 + 10); + outmsg.DataSize = 4 + 10; + outmsg.ProtocolID = ISO15765; + outmsg.TxFlags = CAN_29BIT_ID; + outmsg.ExtraDataIndex = 0; + outmsg.RxStatus = 0; + + unsigned long msgoutcount = 1; + + res = PassThruWriteMsgs(cid, &outmsg, &msgoutcount, 0); + if (res != STATUS_NOERROR) + return 1; + + PASSTHRU_MSG inmsg[8]; + unsigned long msgincount = 8; + + res = PassThruReadMsgs(cid, inmsg, &msgincount, 1000); + if (res != STATUS_NOERROR) + return 1; + + return 0; +} diff --git a/drivers/windows/panda_playground/panda_playground.vcxproj b/drivers/windows/panda_playground/panda_playground.vcxproj new file mode 100644 index 0000000..0063beb --- /dev/null +++ b/drivers/windows/panda_playground/panda_playground.vcxproj @@ -0,0 +1,191 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {691DB635-C272-4B98-897E-0505B970DCA9} + Win32Proj + panda_playground + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + $(ProjectName)2 + + + true + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + $(ProjectName) + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + false + $(SolutionDir)$(Configuration)_$(PlatformShortName)\ + + + + Use + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + Use + Level3 + Disabled + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + %(AdditionalIncludeDirectories);$(SolutionDir) + + + Console + true + true + true + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies);$(OutDir)panda.lib;$(OutDir)ecusim.lib + + + + + + + + + + + + + + + + Create + Create + Create + Create + + + + + {96e0e646-ee76-444d-9a77-a0cd7f781deb} + + + {a2bb18a5-f26b-48d6-bbb5-b83d64473c77} + + + {5528aefb-638d-49af-b9d4-965154e7d531} + + + + + + \ No newline at end of file diff --git a/drivers/windows/panda_playground/panda_playground.vcxproj.filters b/drivers/windows/panda_playground/panda_playground.vcxproj.filters new file mode 100644 index 0000000..b84fc23 --- /dev/null +++ b/drivers/windows/panda_playground/panda_playground.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/drivers/windows/panda_playground/stdafx.cpp b/drivers/windows/panda_playground/stdafx.cpp new file mode 100644 index 0000000..fefa8d7 --- /dev/null +++ b/drivers/windows/panda_playground/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// panda_playground.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/drivers/windows/panda_playground/stdafx.h b/drivers/windows/panda_playground/stdafx.h new file mode 100644 index 0000000..f22759b --- /dev/null +++ b/drivers/windows/panda_playground/stdafx.h @@ -0,0 +1,17 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include + +#include +#include +#include +#include diff --git a/drivers/windows/panda_playground/targetver.h b/drivers/windows/panda_playground/targetver.h new file mode 100644 index 0000000..87c0086 --- /dev/null +++ b/drivers/windows/panda_playground/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include diff --git a/drivers/windows/panda_remove.ico b/drivers/windows/panda_remove.ico new file mode 100644 index 0000000..74602e0 Binary files /dev/null and b/drivers/windows/panda_remove.ico differ diff --git a/drivers/windows/redist/.gitignore b/drivers/windows/redist/.gitignore new file mode 100644 index 0000000..90d431b --- /dev/null +++ b/drivers/windows/redist/.gitignore @@ -0,0 +1,2 @@ +*.exe +vscruntimeinfo.nsh diff --git a/drivers/windows/redist/README.md b/drivers/windows/redist/README.md new file mode 100644 index 0000000..69565f1 --- /dev/null +++ b/drivers/windows/redist/README.md @@ -0,0 +1,7 @@ +When building the installer, please put the relevant vc_redist.x86.exe file into this folder. +Make sure that the uninstall registry key is correct in the panda_install.nsi file. + +Here is a list of the VC runtime downloads: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads +An list of the registry keys has been maintained here: https://stackoverflow.com/a/34209692/627525 + +Copy vscruntimeinfo.nsh.sample to vscruntimeinfo.nsh and edit it for your version of Visual Studio. \ No newline at end of file diff --git a/drivers/windows/redist/vscruntimeinfo.nsh.sample b/drivers/windows/redist/vscruntimeinfo.nsh.sample new file mode 100644 index 0000000..3e74ab1 --- /dev/null +++ b/drivers/windows/redist/vscruntimeinfo.nsh.sample @@ -0,0 +1,13 @@ +;NOTE! The panda software requires a VC runtime to be installed in order to work. +;This installer must be bundled with the appropriate runtime installer, and have +;the installation registry key set so the installer can tell if the runtime is +;already installed. + +;Here is a list of the VC runtime downloads: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads +;An list of the registry keys has been maintained here: https://stackoverflow.com/a/34209692/627525 + + +;Microsoft Visual C++ 2015 Redistributable (x86) - 14.0.24123 +!define VCRuntimeRegKey "SOFTWARE\Classes\Installer\Dependencies\{206898cc-4b41-4d98-ac28-9f9ae57f91fe}" +!define VCRuntimeSetupPath "redist\" +!define VCRuntimeSetupFile "vc_redist.x86.exe" \ No newline at end of file diff --git a/drivers/windows/test certs/commaaiCertStore.pvk b/drivers/windows/test certs/commaaiCertStore.pvk new file mode 100644 index 0000000..6db0a78 Binary files /dev/null and b/drivers/windows/test certs/commaaiCertStore.pvk differ diff --git a/drivers/windows/test certs/commaaicert.cer b/drivers/windows/test certs/commaaicert.cer new file mode 100644 index 0000000..8d0a38a Binary files /dev/null and b/drivers/windows/test certs/commaaicert.cer differ