├── .github └── workflows │ ├── build-msvc.yml │ ├── build-w-opt-can-arg.yml │ └── cmake.yml ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── CONTRIBUTORS.md ├── Doxyfile ├── FUNDING.yml ├── LICENSE ├── Makefile ├── README.md ├── isotp.c ├── isotp.h ├── isotp_config.h ├── isotp_defines.h ├── isotp_user.h ├── library.json └── vars.mk /.github/workflows/build-msvc.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/windows-msvc-cmake.yml 2 | name: Windows MSVC Build 3 | 4 | on: 5 | push: 6 | branches: [ "master" ] 7 | pull_request: 8 | branches: [ "master" ] 9 | 10 | jobs: 11 | build: 12 | name: Build with MSVC via CMake 13 | runs-on: windows-latest 14 | 15 | steps: 16 | - name: Check out repository 17 | uses: actions/checkout@v3 18 | 19 | - name: Configure CMake 20 | run: | 21 | cmake -S . -B build -G "Visual Studio 17 2022" -A x64 -DCMAKE_BUILD_TYPE=Release 22 | 23 | - name: Build (Release) 24 | run: cmake --build build --config Release -- /m 25 | 26 | - name: Run unit tests 27 | if: always() 28 | run: | 29 | cd build 30 | ctest --output-on-failure --timeout 120 31 | -------------------------------------------------------------------------------- /.github/workflows/build-w-opt-can-arg.yml: -------------------------------------------------------------------------------- 1 | name: "CMake w/ changes from #36" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Checkout Submodules 24 | run: git submodule update --init --recursive 25 | 26 | - name: Configure CMake 27 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 28 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 29 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -Disotp_ENABLE_CAN_SEND_ARG=ON 30 | 31 | - name: Build 32 | # Build your program with the given configuration 33 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 34 | 35 | - name: Test 36 | working-directory: ${{github.workspace}}/build 37 | # Execute tests defined by the CMake configuration. 38 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 39 | run: ctest -C ${{env.BUILD_TYPE}} 40 | 41 | -------------------------------------------------------------------------------- /.github/workflows/cmake.yml: -------------------------------------------------------------------------------- 1 | name: CMake 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | 9 | env: 10 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 11 | BUILD_TYPE: Release 12 | 13 | jobs: 14 | build: 15 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 16 | # You can convert this to a matrix build if you need cross-platform coverage. 17 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - name: Checkout Submodules 24 | run: git submodule update --init --recursive 25 | 26 | - name: Configure CMake 27 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 28 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 29 | run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} 30 | 31 | - name: Build 32 | # Build your program with the given configuration 33 | run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 34 | 35 | - name: Test 36 | working-directory: ${{github.workspace}}/build 37 | # Execute tests defined by the CMake configuration. 38 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 39 | run: ctest -C ${{env.BUILD_TYPE}} 40 | 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### 2 | # .GITIGNORE FILE 3 | # This file contains instructions for filesystem entries git should ignore when 4 | # doing things to this repository. 5 | ### 6 | 7 | ### 8 | # Ignore Artifacts 9 | ### 10 | *.o 11 | bin/ 12 | build/ 13 | 14 | ### 15 | # Ignore Backups 16 | ### 17 | *~ 18 | 19 | html/ 20 | /.vs 21 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "submodules/doxygen-awesome-css"] 2 | path = submodules/doxygen-awesome-css 3 | url = https://github.com/jothepro/doxygen-awesome-css.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c++ 2 | compiler: 3 | - gcc 4 | - g++ 5 | - clang 6 | script: make travis 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | ### 4 | # Project definition 5 | ### 6 | project(isotp LANGUAGES C VERSION 1.5.0 DESCRIPTION "A platform-agnostic ISOTP implementation in C for embedded devices.") 7 | 8 | option(isotpc_USE_INCLUDE_DIR "Copy header files to separate include directory in current binary dir for better separation of header files to combat potential naming conflicts." OFF) 9 | option(isotpc_STATIC_LIBRARY "Compile libisotpc as a static library, instead of a shared library." OFF) 10 | option(isotpc_STATIC_LIBRARY_PIC "Make use of position independent code (PIC), when compiling as a static library (enabled automatically when building shared libraries)." OFF) 11 | option(isotpc_PAD_CAN_FRAMES "Pad CAN frames to their full size." ON) 12 | set(isotpc_CAN_FRAME_PAD_VALUE "0xAA" CACHE STRING "Padding byte value to be used in CAN frames if enabled") 13 | option(isotpc_ENABLE_CAN_SEND_ARG "Adds an extra argument to isotp_user_send_can to better support multiple CAN interfaces." OFF) 14 | 15 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 16 | 17 | if (isotpc_STATIC_LIBRARY OR MSVC) 18 | add_library(isotp STATIC ${CMAKE_CURRENT_SOURCE_DIR}/isotp.c) 19 | else() 20 | add_library(isotp SHARED ${CMAKE_CURRENT_SOURCE_DIR}/isotp.c) 21 | endif() 22 | 23 | ### 24 | # Strict building policies 25 | ### 26 | if (MSVC) 27 | target_compile_options( 28 | isotp PRIVATE 29 | /W4 30 | ) 31 | else() 32 | target_compile_options( 33 | isotp PRIVATE 34 | -Werror 35 | -Wall 36 | -Wpedantic 37 | -Wextra 38 | -Wno-unknown-pragmas # ignore unknown pragmas, such as #pragma region 39 | ) 40 | endif() 41 | 42 | ### 43 | # Enable position independent code (PIC) if required 44 | ### 45 | if (NOT isotpc_STATIC_LIBRARY OR isotpc_STATIC_LIBRARY_PIC) 46 | # Building a shared library or a static library with PIC enabled 47 | target_compile_options(isotp PRIVATE -fPIC) 48 | endif() 49 | 50 | ### 51 | # Provide padding configuration 52 | ### 53 | if (isotpc_PAD_CAN_FRAMES) 54 | target_compile_definitions(isotp PRIVATE -DISO_TP_FRAME_PADDING -DISO_TP_FRAME_PADDING_VALUE=${isotpc_CAN_FRAME_PAD_VALUE}) 55 | endif() 56 | 57 | ### 58 | # Include additional arg in isotp_user_send_can if required 59 | ### 60 | if (isotpc_ENABLE_CAN_SEND_ARG) 61 | target_compile_options(isotp PRIVATE -DISO_TP_USER_SEND_CAN_ARG) 62 | endif() 63 | 64 | ### 65 | # Check for debug builds 66 | ### 67 | if (CMAKE_BUILD_TYPE STREQUAL "Debug") 68 | target_compile_options(isotp PRIVATE -O0 -g) # don't optimise and add debugging symbols 69 | else() 70 | target_compile_options(isotp PRIVATE -O2) # optimise code 71 | target_link_libraries(isotp PRIVATE -s) # strip the library 72 | endif() 73 | 74 | if (isotpc_USE_INCLUDE_DIR) 75 | file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/isotp_c) 76 | 77 | file(GLOB HEADERS FOLLOW_SYMLINKS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) 78 | file(COPY ${HEADERS} DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/include/isotp_c/) 79 | 80 | target_include_directories(isotp PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/include) 81 | 82 | else() 83 | target_include_directories(isotp PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 84 | endif() 85 | 86 | ### 87 | # Add library aliases 88 | ### 89 | add_library(isotpc ALIAS isotp) 90 | add_library(isotp_c ALIAS isotp) 91 | add_library(simon_cahill::isotp ALIAS isotp) 92 | add_library(simon_cahill::isotpc ALIAS isotp) 93 | add_library(simon_cahill::isotp_c ALIAS isotp) 94 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | | Name and Profile | Contribution | PR | 2 | |-----------------------------------------------------|------------------------------------------------------------------------------------------------|--------| 3 | | [Li Shen](https://github.com/lishen2) | Original Author | | 4 | | [Simon Cahill](https://github.com/SimonCahill) | CMake support, documentation improvements, code simplification, fork owner | | 5 | | [Brandon Ros](https://github.com/brandonros) | Migrated timestamps from milliseconds to microseconds. | #12 | 6 | | [Nick James Kirkby](https://github.com/driftregion) | Fixed include path warnings on Win64 | #13 | 7 | | [Mark A Clark](https://github.com/maclark88) | Fixed documentation inconsistencies | #18 | 8 | | [speedy-h](https://github.com/speedy-h) | Fixed compile- and link-time options | #23 | 9 | | [Phil Greenland](https://github.com/pgreenland) | Added padding support in ISOTP messages | #24 | 10 | | [Phil Greenland](https://github.com/pgreenland) | Added support for message retries if the remote controller was busy | #26 | 11 | | [Phil Greenland](https://github.com/pgreenland) | Fixed PIC being generated for static binaries | #27 | 12 | | [speedy-h](https://github.com/speedy-h) | Fixed compiler error when compiling with `-Wextra` caused by unused function params. | #32 | 13 | | [kazetsukaimiko](https://github.com/kazetsukaimiko) | Added support for PlatformIO, a platform for distributing libraries for a variety of systems. | #35 | 14 | | [driftregion](https://github.com/driftregion) | Added support for optional CAN arguments for adapter-specific information. | #36 | 15 | | [andyneubacher](https://github.com/AndyNeubacher) | Added support for larger frame sizes (ISO15765-2:2016). | #46 | 16 | | [mws262](https://github.com/mws262) | Fixed snprintf format string for embedded devices (replaced %d w/ %u) | #47 | 17 | 18 | Thank you everyone for contributing to this library and improving it! 19 | Have you contributed and I've forgotten to mention you? Please let me know and I'll add you here! 20 | -------------------------------------------------------------------------------- /Doxyfile: -------------------------------------------------------------------------------- 1 | # Doxyfile 1.9.1 2 | 3 | #--------------------------------------------------------------------------- 4 | # Project related configuration options 5 | #--------------------------------------------------------------------------- 6 | DOXYFILE_ENCODING = UTF-8 7 | PROJECT_NAME = "ISOTP-C" 8 | PROJECT_NUMBER = 9 | PROJECT_BRIEF = "A platform-agnostic ISOTP implementation in C." 10 | PROJECT_LOGO = 11 | OUTPUT_DIRECTORY = 12 | CREATE_SUBDIRS = YES 13 | ALLOW_UNICODE_NAMES = YES 14 | OUTPUT_LANGUAGE = English 15 | OUTPUT_TEXT_DIRECTION = None 16 | BRIEF_MEMBER_DESC = YES 17 | REPEAT_BRIEF = YES 18 | ABBREVIATE_BRIEF = "The $name class" \ 19 | "The $name widget" \ 20 | "The $name file" \ 21 | is \ 22 | provides \ 23 | specifies \ 24 | contains \ 25 | represents \ 26 | a \ 27 | an \ 28 | the 29 | ALWAYS_DETAILED_SEC = NO 30 | INLINE_INHERITED_MEMB = NO 31 | FULL_PATH_NAMES = YES 32 | STRIP_FROM_PATH = 33 | STRIP_FROM_INC_PATH = 34 | SHORT_NAMES = NO 35 | JAVADOC_AUTOBRIEF = NO 36 | JAVADOC_BANNER = NO 37 | QT_AUTOBRIEF = NO 38 | MULTILINE_CPP_IS_BRIEF = NO 39 | PYTHON_DOCSTRING = YES 40 | INHERIT_DOCS = YES 41 | SEPARATE_MEMBER_PAGES = NO 42 | TAB_SIZE = 4 43 | ALIASES = 44 | OPTIMIZE_OUTPUT_FOR_C = YES 45 | OPTIMIZE_OUTPUT_JAVA = NO 46 | OPTIMIZE_FOR_FORTRAN = NO 47 | OPTIMIZE_OUTPUT_VHDL = NO 48 | OPTIMIZE_OUTPUT_SLICE = NO 49 | EXTENSION_MAPPING = 50 | MARKDOWN_SUPPORT = YES 51 | TOC_INCLUDE_HEADINGS = 5 52 | AUTOLINK_SUPPORT = YES 53 | BUILTIN_STL_SUPPORT = NO 54 | CPP_CLI_SUPPORT = YES 55 | SIP_SUPPORT = NO 56 | IDL_PROPERTY_SUPPORT = YES 57 | DISTRIBUTE_GROUP_DOC = NO 58 | GROUP_NESTED_COMPOUNDS = NO 59 | SUBGROUPING = YES 60 | INLINE_GROUPED_CLASSES = NO 61 | INLINE_SIMPLE_STRUCTS = NO 62 | TYPEDEF_HIDES_STRUCT = NO 63 | LOOKUP_CACHE_SIZE = 0 64 | NUM_PROC_THREADS = 1 65 | #--------------------------------------------------------------------------- 66 | # Build related configuration options 67 | #--------------------------------------------------------------------------- 68 | EXTRACT_ALL = YES 69 | EXTRACT_PRIVATE = YES 70 | EXTRACT_PRIV_VIRTUAL = YES 71 | EXTRACT_PACKAGE = YES 72 | EXTRACT_STATIC = YES 73 | EXTRACT_LOCAL_CLASSES = YES 74 | EXTRACT_LOCAL_METHODS = YES 75 | EXTRACT_ANON_NSPACES = YES 76 | RESOLVE_UNNAMED_PARAMS = YES 77 | HIDE_UNDOC_MEMBERS = NO 78 | HIDE_UNDOC_CLASSES = NO 79 | HIDE_FRIEND_COMPOUNDS = NO 80 | HIDE_IN_BODY_DOCS = NO 81 | INTERNAL_DOCS = NO 82 | CASE_SENSE_NAMES = YES 83 | HIDE_SCOPE_NAMES = NO 84 | HIDE_COMPOUND_REFERENCE= NO 85 | SHOW_INCLUDE_FILES = YES 86 | SHOW_GROUPED_MEMB_INC = NO 87 | FORCE_LOCAL_INCLUDES = NO 88 | INLINE_INFO = YES 89 | SORT_MEMBER_DOCS = YES 90 | SORT_BRIEF_DOCS = NO 91 | SORT_MEMBERS_CTORS_1ST = NO 92 | SORT_GROUP_NAMES = NO 93 | SORT_BY_SCOPE_NAME = NO 94 | STRICT_PROTO_MATCHING = NO 95 | GENERATE_TODOLIST = YES 96 | GENERATE_TESTLIST = YES 97 | GENERATE_BUGLIST = YES 98 | GENERATE_DEPRECATEDLIST= YES 99 | ENABLED_SECTIONS = 100 | MAX_INITIALIZER_LINES = 30 101 | SHOW_USED_FILES = YES 102 | SHOW_FILES = YES 103 | SHOW_NAMESPACES = YES 104 | FILE_VERSION_FILTER = 105 | LAYOUT_FILE = 106 | CITE_BIB_FILES = 107 | #--------------------------------------------------------------------------- 108 | # Configuration options related to warning and progress messages 109 | #--------------------------------------------------------------------------- 110 | QUIET = NO 111 | WARNINGS = YES 112 | WARN_IF_UNDOCUMENTED = YES 113 | WARN_IF_DOC_ERROR = YES 114 | WARN_NO_PARAMDOC = NO 115 | WARN_AS_ERROR = NO 116 | WARN_FORMAT = "$file:$line: $text" 117 | WARN_LOGFILE = 118 | #--------------------------------------------------------------------------- 119 | # Configuration options related to the input files 120 | #--------------------------------------------------------------------------- 121 | INPUT = README.md CONTRIBUTORS.md isotp_config.h isotp_defines.h isotp_user.h isotp.c isotp.h 122 | INPUT_ENCODING = UTF-8 123 | FILE_PATTERNS = *.c \ 124 | *.cc \ 125 | *.cxx \ 126 | *.cpp \ 127 | *.c++ \ 128 | *.java \ 129 | *.ii \ 130 | *.ixx \ 131 | *.ipp \ 132 | *.i++ \ 133 | *.inl \ 134 | *.idl \ 135 | *.ddl \ 136 | *.odl \ 137 | *.h \ 138 | *.hh \ 139 | *.hxx \ 140 | *.hpp \ 141 | *.h++ \ 142 | *.cs \ 143 | *.d \ 144 | *.php \ 145 | *.php4 \ 146 | *.php5 \ 147 | *.phtml \ 148 | *.inc \ 149 | *.m \ 150 | *.markdown \ 151 | *.md \ 152 | *.mm \ 153 | *.dox \ 154 | *.py \ 155 | *.pyw \ 156 | *.f90 \ 157 | *.f95 \ 158 | *.f03 \ 159 | *.f08 \ 160 | *.f18 \ 161 | *.f \ 162 | *.for \ 163 | *.vhd \ 164 | *.vhdl \ 165 | *.ucf \ 166 | *.qsf \ 167 | *.ice 168 | RECURSIVE = NO 169 | EXCLUDE = 170 | EXCLUDE_SYMLINKS = NO 171 | EXCLUDE_PATTERNS = 172 | EXCLUDE_SYMBOLS = 173 | EXAMPLE_PATH = 174 | EXAMPLE_PATTERNS = * 175 | EXAMPLE_RECURSIVE = NO 176 | IMAGE_PATH = 177 | INPUT_FILTER = 178 | FILTER_PATTERNS = 179 | FILTER_SOURCE_FILES = NO 180 | FILTER_SOURCE_PATTERNS = 181 | USE_MDFILE_AS_MAINPAGE = README.md 182 | #--------------------------------------------------------------------------- 183 | # Configuration options related to source browsing 184 | #--------------------------------------------------------------------------- 185 | SOURCE_BROWSER = YES 186 | INLINE_SOURCES = YES 187 | STRIP_CODE_COMMENTS = YES 188 | REFERENCED_BY_RELATION = YES 189 | REFERENCES_RELATION = YES 190 | REFERENCES_LINK_SOURCE = YES 191 | SOURCE_TOOLTIPS = YES 192 | USE_HTAGS = NO 193 | VERBATIM_HEADERS = YES 194 | CLANG_ASSISTED_PARSING = YES 195 | CLANG_ADD_INC_PATHS = YES 196 | CLANG_OPTIONS = 197 | CLANG_DATABASE_PATH = 198 | #--------------------------------------------------------------------------- 199 | # Configuration options related to the alphabetical class index 200 | #--------------------------------------------------------------------------- 201 | ALPHABETICAL_INDEX = YES 202 | IGNORE_PREFIX = 203 | #--------------------------------------------------------------------------- 204 | # Configuration options related to the HTML output 205 | #--------------------------------------------------------------------------- 206 | GENERATE_HTML = YES 207 | FULL_SIDEBAR = NO 208 | HTML_OUTPUT = html 209 | HTML_FILE_EXTENSION = .html 210 | HTML_HEADER = 211 | HTML_FOOTER = 212 | HTML_STYLESHEET = 213 | HTML_EXTRA_STYLESHEET = submodules/doxygen-awesome-css/doxygen-awesome.css \ 214 | submodules/doxygen-awesome-css/doxygen-awesome-sidebar-only.css 215 | HTML_EXTRA_FILES = 216 | HTML_COLORSTYLE_HUE = 220 217 | HTML_COLORSTYLE_SAT = 100 218 | HTML_COLORSTYLE_GAMMA = 80 219 | HTML_TIMESTAMP = NO 220 | HTML_DYNAMIC_MENUS = YES 221 | HTML_DYNAMIC_SECTIONS = NO 222 | HTML_INDEX_NUM_ENTRIES = 100 223 | GENERATE_DOCSET = NO 224 | DOCSET_FEEDNAME = "Doxygen generated docs" 225 | DOCSET_BUNDLE_ID = eu.simonc.isotpc 226 | DOCSET_PUBLISHER_ID = eu.simonc.isotpc 227 | DOCSET_PUBLISHER_NAME = Simon Cahill 228 | GENERATE_HTMLHELP = NO 229 | CHM_FILE = 230 | HHC_LOCATION = 231 | GENERATE_CHI = NO 232 | CHM_INDEX_ENCODING = 233 | BINARY_TOC = NO 234 | TOC_EXPAND = NO 235 | GENERATE_QHP = NO 236 | QCH_FILE = 237 | QHP_NAMESPACE = org.doxygen.Project 238 | QHP_VIRTUAL_FOLDER = doc 239 | QHP_CUST_FILTER_NAME = 240 | QHP_CUST_FILTER_ATTRS = 241 | QHP_SECT_FILTER_ATTRS = 242 | QHG_LOCATION = 243 | GENERATE_ECLIPSEHELP = NO 244 | ECLIPSE_DOC_ID = org.doxygen.Project 245 | DISABLE_INDEX = NO 246 | GENERATE_TREEVIEW = YES 247 | ENUM_VALUES_PER_LINE = 4 248 | TREEVIEW_WIDTH = 250 249 | EXT_LINKS_IN_WINDOW = NO 250 | HTML_FORMULA_FORMAT = png 251 | FORMULA_FONTSIZE = 10 252 | FORMULA_TRANSPARENT = YES 253 | FORMULA_MACROFILE = 254 | USE_MATHJAX = NO 255 | MATHJAX_FORMAT = HTML-CSS 256 | MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 257 | MATHJAX_EXTENSIONS = 258 | MATHJAX_CODEFILE = 259 | SEARCHENGINE = YES 260 | SERVER_BASED_SEARCH = NO 261 | EXTERNAL_SEARCH = NO 262 | SEARCHENGINE_URL = 263 | SEARCHDATA_FILE = searchdata.xml 264 | EXTERNAL_SEARCH_ID = 265 | EXTRA_SEARCH_MAPPINGS = 266 | #--------------------------------------------------------------------------- 267 | # Configuration options related to the LaTeX output 268 | #--------------------------------------------------------------------------- 269 | GENERATE_LATEX = NO 270 | LATEX_OUTPUT = latex 271 | LATEX_CMD_NAME = 272 | MAKEINDEX_CMD_NAME = makeindex 273 | LATEX_MAKEINDEX_CMD = makeindex 274 | COMPACT_LATEX = NO 275 | PAPER_TYPE = a4 276 | EXTRA_PACKAGES = 277 | LATEX_HEADER = 278 | LATEX_FOOTER = 279 | LATEX_EXTRA_STYLESHEET = 280 | LATEX_EXTRA_FILES = 281 | PDF_HYPERLINKS = YES 282 | USE_PDFLATEX = YES 283 | LATEX_BATCHMODE = NO 284 | LATEX_HIDE_INDICES = NO 285 | LATEX_SOURCE_CODE = NO 286 | LATEX_BIB_STYLE = plain 287 | LATEX_TIMESTAMP = NO 288 | LATEX_EMOJI_DIRECTORY = 289 | #--------------------------------------------------------------------------- 290 | # Configuration options related to the RTF output 291 | #--------------------------------------------------------------------------- 292 | GENERATE_RTF = NO 293 | RTF_OUTPUT = rtf 294 | COMPACT_RTF = NO 295 | RTF_HYPERLINKS = NO 296 | RTF_STYLESHEET_FILE = 297 | RTF_EXTENSIONS_FILE = 298 | RTF_SOURCE_CODE = NO 299 | #--------------------------------------------------------------------------- 300 | # Configuration options related to the man page output 301 | #--------------------------------------------------------------------------- 302 | GENERATE_MAN = NO 303 | MAN_OUTPUT = man 304 | MAN_EXTENSION = .3 305 | MAN_SUBDIR = 306 | MAN_LINKS = NO 307 | #--------------------------------------------------------------------------- 308 | # Configuration options related to the XML output 309 | #--------------------------------------------------------------------------- 310 | GENERATE_XML = NO 311 | XML_OUTPUT = xml 312 | XML_PROGRAMLISTING = YES 313 | XML_NS_MEMB_FILE_SCOPE = NO 314 | #--------------------------------------------------------------------------- 315 | # Configuration options related to the DOCBOOK output 316 | #--------------------------------------------------------------------------- 317 | GENERATE_DOCBOOK = NO 318 | DOCBOOK_OUTPUT = docbook 319 | DOCBOOK_PROGRAMLISTING = NO 320 | #--------------------------------------------------------------------------- 321 | # Configuration options for the AutoGen Definitions output 322 | #--------------------------------------------------------------------------- 323 | GENERATE_AUTOGEN_DEF = NO 324 | #--------------------------------------------------------------------------- 325 | # Configuration options related to the Perl module output 326 | #--------------------------------------------------------------------------- 327 | GENERATE_PERLMOD = NO 328 | PERLMOD_LATEX = NO 329 | PERLMOD_PRETTY = YES 330 | PERLMOD_MAKEVAR_PREFIX = 331 | #--------------------------------------------------------------------------- 332 | # Configuration options related to the preprocessor 333 | #--------------------------------------------------------------------------- 334 | ENABLE_PREPROCESSING = YES 335 | MACRO_EXPANSION = NO 336 | EXPAND_ONLY_PREDEF = NO 337 | SEARCH_INCLUDES = YES 338 | INCLUDE_PATH = 339 | INCLUDE_FILE_PATTERNS = 340 | PREDEFINED = 341 | EXPAND_AS_DEFINED = 342 | SKIP_FUNCTION_MACROS = YES 343 | #--------------------------------------------------------------------------- 344 | # Configuration options related to external references 345 | #--------------------------------------------------------------------------- 346 | TAGFILES = 347 | GENERATE_TAGFILE = 348 | ALLEXTERNALS = NO 349 | EXTERNAL_GROUPS = YES 350 | EXTERNAL_PAGES = YES 351 | #--------------------------------------------------------------------------- 352 | # Configuration options related to the dot tool 353 | #--------------------------------------------------------------------------- 354 | CLASS_DIAGRAMS = YES 355 | DIA_PATH = 356 | HIDE_UNDOC_RELATIONS = YES 357 | HAVE_DOT = YES 358 | DOT_NUM_THREADS = 0 359 | DOT_FONTNAME = Helvetica 360 | DOT_FONTSIZE = 10 361 | DOT_FONTPATH = 362 | CLASS_GRAPH = YES 363 | COLLABORATION_GRAPH = YES 364 | GROUP_GRAPHS = YES 365 | UML_LOOK = NO 366 | UML_LIMIT_NUM_FIELDS = 10 367 | DOT_UML_DETAILS = NO 368 | DOT_WRAP_THRESHOLD = 17 369 | TEMPLATE_RELATIONS = NO 370 | INCLUDE_GRAPH = YES 371 | INCLUDED_BY_GRAPH = YES 372 | CALL_GRAPH = NO 373 | CALLER_GRAPH = NO 374 | GRAPHICAL_HIERARCHY = YES 375 | DIRECTORY_GRAPH = YES 376 | DOT_IMAGE_FORMAT = png 377 | INTERACTIVE_SVG = NO 378 | DOT_PATH = 379 | DOTFILE_DIRS = 380 | MSCFILE_DIRS = 381 | DIAFILE_DIRS = 382 | PLANTUML_JAR_PATH = 383 | PLANTUML_CFG_FILE = 384 | PLANTUML_INCLUDE_PATH = 385 | DOT_GRAPH_MAX_NODES = 50 386 | MAX_DOT_GRAPH_DEPTH = 0 387 | DOT_TRANSPARENT = NO 388 | DOT_MULTI_TARGETS = NO 389 | GENERATE_LEGEND = YES 390 | DOT_CLEANUP = YES 391 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: SimonCahill 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2024 Li Shen & Co-Operators 4 | Copyright (c) 2024 Simon Cahill & Contributors. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include vars.mk 2 | 3 | CFLAGS := -Wall -g -ggdb $(STD) 4 | LDFLAGS := -shared 5 | BIN := ./bin 6 | 7 | .PHONY: all clean fPIC no_opt $(BIN)/$(LIB_NAME) $(BIN)/$(LIB_NAME).$(MAJOR_VER) $(BIN)/$(LIB_NAME).$(MAJOR_VER).$(MINOR_VER).$(REVISION) travis 8 | 9 | ### 10 | # BEGIN TARGETS 11 | ### 12 | 13 | ### 14 | # Builds all library artifacts 15 | ### 16 | all: $(BIN)/$(LIB_NAME) 17 | @printf "########## BUILT $^ ##########\n\n\n" 18 | 19 | fPIC: CFLAGS += "-fPIC" 20 | 21 | travis: fPIC all 22 | 23 | ### 24 | # Builds all targets w/o optimisations enabled 25 | ### 26 | no_opt: CFLAGS += -g -O0 all 27 | 28 | ### 29 | # Removes all build artifacts 30 | ### 31 | clean: 32 | -rm -f *.o $(BIN)/$(LIB_NAME)* 33 | 34 | ### 35 | # Builds all library artifacts, including all symlinks. 36 | ### 37 | $(BIN)/$(LIB_NAME): $(BIN)/$(LIB_NAME).$(MAJOR_VER) 38 | -ln -s $^ $@ 39 | @printf "Linked $^ --> $@...\n" 40 | 41 | ### 42 | # Builds the shared object along with one symlink 43 | ### 44 | $(BIN)/$(LIB_NAME).$(MAJOR_VER): $(BIN)/$(LIB_NAME).$(MAJOR_VER).$(MINOR_VER).$(REVISION) 45 | -ln -s $^ $@ 46 | @printf "Linked $^ --> $@...\n" 47 | 48 | $(BIN)/$(LIB_NAME).$(MAJOR_VER).$(MINOR_VER).$(REVISION): libisotp.o 49 | if [ ! -d $(BIN) ]; then mkdir $(BIN); fi; 50 | ${COMP} $^ -o $@ ${LDFLAGS} 51 | 52 | ### 53 | # Compiles the isotp.c TU to an object file. 54 | ### 55 | libisotp.o: isotp.c 56 | ${COMP} -c $^ -o $@ ${CFLAGS} -DISO_TP_FRAME_PADDING 57 | 58 | install: all 59 | @printf "Installing $(LIB_NAME) to $(INSTALL_DIR)...\n" 60 | cp $(BIN)/$(LIB_NAME)* $(INSTALL_DIR) 61 | @printf "Library was installed...\n" 62 | 63 | ### 64 | # END TARGETS 65 | ### 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ISO-TP (ISO 15765-2) Support Library in C 2 | ================================ 3 | 4 | **This project is inspired by [openxc isotp-c](https://github.com/openxc/isotp-c), but the code has been completely re-written.** 5 | 6 | This is a platform agnostic C library that implements the [ISO 15765-2](https://en.wikipedia.org/wiki/ISO_15765-2) (also known as ISO-TP) protocol, which runs over a CAN bus. Quoting Wikipedia: 7 | 8 | >ISO 15765-2, or ISO-TP, is an international standard for sending data packets over a CAN-Bus. 9 | >The protocol allows for the transport of messages that exceed the eight byte maximum payload of CAN frames. 10 | >ISO-TP segments longer messages into multiple frames, adding metadata that allows the interpretation of individual frames and reassembly 11 | >into a complete message packet by the recipient. It can carry up to 4095 bytes of payload per message packet. 12 | 13 | This library doesn't assume anything about the source of the ISO-TP messages or the underlying interface to CAN. It uses dependency injection to give you complete control. 14 | 15 | **The current version supports [ISO-15765-2](https://en.wikipedia.org/wiki/ISO_15765-2) single and multiple frame transmition, and works in Full-duplex mode.** 16 | 17 | ## Builds 18 | 19 | [![CMake](https://github.com/SimonCahill/isotp-c/actions/workflows/cmake.yml/badge.svg)](https://github.com/SimonCahill/isotp-c/actions/workflows/cmake.yml) 20 | [![CMake w/ changes from #36](https://github.com/SimonCahill/isotp-c/actions/workflows/build-w-opt-can-arg.yml/badge.svg)](https://github.com/SimonCahill/isotp-c/actions/workflows/build-w-opt-can-arg.yml) 21 | [![Windows MSVC Build](https://github.com/SimonCahill/isotp-c/actions/workflows/build-msvc.yml/badge.svg)](https://github.com/SimonCahill/isotp-c/actions/workflows/build-msvc.yml) 22 | 23 | ## Contributors 24 | 25 | It's at this point where I'd like to point out all the fantastic contributions made to this fork by the amazing people using it! 26 | [List of contributors](https://github.com/SimonCahill/isotp-c/blob/master/CONTRIBUTORS.md) 27 | 28 | Thank you all! 29 | 30 | ## Building ISOTP-C 31 | 32 | This library may be built using either straight Makefiles, or using CMake. 33 | 34 | ### make 35 | To build this library using Make, simply call: 36 | 37 | ```bash 38 | $ make all 39 | ``` 40 | 41 | ### CMake 42 | 43 | The CMake build system allows for more flexibility at generation and build time, so it is recommended you use this for building this library. 44 | Of course, if your project does not use CMake, you don't *have* to use it. 45 | If your projects use a different build system, you are more than welcome to include it in this repository. 46 | 47 | The Makefile generator for isotpc will automatically detect whether or not your build system is using the `Debug` or `Release` build type and will adjust compiler parameters accordingly. 48 | 49 | #### Debug Build 50 | If your project is configured to build as `Debug`, then the library will be compiled with **no** optimisations and **with** debug symbols. 51 | `-DCMAKE_BUILD_TYPE=Debug` 52 | 53 | #### Release Build 54 | If your project is configured to build as `Release`, then the library code will be **optimised** using `-O2` and will be **stripped**. 55 | `-DCMAKE_BUILD_TYPE=Release` 56 | 57 | #### External Include Directories 58 | It is generally considered good practice to segregate header files from each other, depending on the project. For this reason, you may opt in to this behaviour for this library. 59 | 60 | If you pass `-Disotpc_USE_INCLUDE_DIR=ON` on the command-line, or you set `set(isotpc_USE_INCLUDE_DIR ON CACHE BOOL "Use external include dir for isotp-c")` in your CMakeLists.txt, then a separate `include/` directory will 61 | be added to the project. 62 | This happens at generation time, and the CMake project will automatically reference `${CMAKE_CURRENT_BINARY_DIR}/include` as the include directory for the project. This will be propagated to your projects, too. 63 | 64 | In your code: 65 | 66 | ```c 67 | // if -Disotpc_USE_INCLUDE_DIR=ON 68 | #include 69 | 70 | // else 71 | #include 72 | ``` 73 | 74 | #### Static Library 75 | In some cases, it is required that a static library be used instead of a shared library. 76 | isotp-c supports this also, via options. 77 | 78 | Either pass `-Disotpc_STATIC_LIBRARY=ON` via command-line or `set(isotpc_STATIC_LIBRARY ON CACHE BOOL "Enable static library for isotp-c")` in your CMakeLists.txt and the library will be built as a static library (`*.a|*.lib`) for your project to include. 79 | 80 | #### Use of multiple CAN interfaces 81 | For applications requiring multiple CAN interfaces, it is necessary to specify the interface in `isotp_user_send_can`. 82 | 83 | In this case the config option `-DISO_TP_USER_SEND_CAN_ARG` may be enabled. The library may then be used as follows: 84 | 85 | ```c 86 | // Objects representing two CAN interfaces: a and b. 87 | CAN_IFACE_t can_a, can_b; 88 | 89 | void init() { 90 | // Two IsoTpLinks assumed to be bound to different CAN interfaces. 91 | IsoTpLink link_a, link_b; 92 | 93 | isotp_init_link(&link_a, ...); 94 | isotp_init_link(&link_b, ...); 95 | 96 | // After link initialization, the relevant CAN interface may be 97 | // attached to the link. 98 | link_a.user_send_can_arg = &can_a; 99 | link_a.user_send_can_arg = &can_b; 100 | } 101 | 102 | int isotp_user_send_can( 103 | const uint32_t arbitration_id, 104 | const uint8_t *data, 105 | const uint8_t size, 106 | void *user_send_can_arg) 107 | { 108 | // It is then available for use inside isotp_user_send_can 109 | int err = CAN_SEND((CAN_IFACE_t *)(user_send_can_arg), arbitration_id, data, size); 110 | if (err) { 111 | return ISOTP_RET_ERROR; 112 | } else { 113 | return ISOTP_RET_OK; 114 | } 115 | } 116 | 117 | ``` 118 | 119 | 120 | #### Inclusion in your CMake project 121 | ```cmake 122 | ### 123 | # Set your desired options 124 | ### 125 | set(isotpc_USE_INCLUDE_DIR ON CACHE BOOL "Use external include directory for isotp-c") # optional 126 | set(isotpc_STATIC_LIBRARY ON CACHE BOOL "Build isotp-c as a static library instead of shared") # optional 127 | 128 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/path/to/isotp-c) # add to current project 129 | 130 | target_link_libraries( 131 | mytarget 132 | 133 | # ... other libs 134 | simon_cahill::isotp_c 135 | ) 136 | ``` 137 | 138 | 139 | ## Usage 140 | 141 | First, create some [shim](https://en.wikipedia.org/wiki/Shim_(computing)) functions to let this library use your lower level system: 142 | 143 | ```C 144 | /* required, this must send a single CAN message with the given arbitration 145 | * ID (i.e. the CAN message ID) and data. The size will never be more than 8 146 | * bytes. Should return ISOTP_RET_OK if frame sent successfully. 147 | * May return ISOTP_RET_NOSPACE if the frame could not be sent but may be 148 | * retried later. Should return ISOTP_RET_ERROR in case frame could not be sent. 149 | */ 150 | int isotp_user_send_can(const uint32_t arbitration_id, 151 | const uint8_t* data, const uint8_t size) { 152 | // ... 153 | } 154 | 155 | /* required, return system tick, unit is micro-second */ 156 | uint32_t isotp_user_get_us(void) { 157 | // ... 158 | } 159 | 160 | /* optional, provide to receive debugging log messages */ 161 | void isotp_user_debug(const char* message, ...) { 162 | // ... 163 | } 164 | ``` 165 | 166 | ### API 167 | 168 | You can use isotp-c in the following way: 169 | 170 | ```C 171 | /* Alloc IsoTpLink statically in RAM */ 172 | static IsoTpLink g_link; 173 | 174 | /* Alloc send and receive buffer statically in RAM */ 175 | static uint8_t g_isotpRecvBuf[ISOTP_BUFSIZE]; 176 | static uint8_t g_isotpSendBuf[ISOTP_BUFSIZE]; 177 | 178 | int main(void) { 179 | /* Initialize CAN and other peripherals */ 180 | 181 | /* Initialize link, 0x7TT is the CAN ID you send with */ 182 | isotp_init_link(&g_link, 0x7TT, 183 | g_isotpSendBuf, sizeof(g_isotpSendBuf), 184 | g_isotpRecvBuf, sizeof(g_isotpRecvBuf)); 185 | 186 | while(1) { 187 | 188 | /* If receive any interested can message, call isotp_on_can_message to handle message */ 189 | ret = can_receive(&id, &data, &len); 190 | 191 | /* 0x7RR is CAN ID you want to receive */ 192 | if (RET_OK == ret && 0x7RR == id) { 193 | isotp_on_can_message(&g_link, data, len); 194 | } 195 | 196 | /* Poll link to handle multiple frame transmition */ 197 | isotp_poll(&g_link); 198 | 199 | /* You can receive message with isotp_receive. 200 | payload is upper layer message buffer, usually UDS; 201 | payload_size is payload buffer size; 202 | out_size is the actuall read size; 203 | */ 204 | ret = isotp_receive(&g_link, payload, payload_size, &out_size); 205 | if (ISOTP_RET_OK == ret) { 206 | /* Handle received message */ 207 | } 208 | 209 | /* And send message with isotp_send */ 210 | ret = isotp_send(&g_link, payload, payload_size); 211 | if (ISOTP_RET_OK == ret) { 212 | /* Send ok */ 213 | } else { 214 | /* An error occured */ 215 | } 216 | 217 | /* In case you want to send data w/ functional addressing, use isotp_send_with_id */ 218 | ret = isotp_send_with_id(&g_link, 0x7df, payload, payload_size); 219 | if (ISOTP_RET_OK == ret) { 220 | /* Send ok */ 221 | } else { 222 | /* Error occur */ 223 | } 224 | } 225 | 226 | return; 227 | } 228 | ``` 229 | 230 | You can call isotp_poll as frequently as you want, as it internally uses isotp_user_get_ms to measure timeout occurences. 231 | If you need handle functional addressing, you must use two separate links, one for each. 232 | 233 | ```C 234 | /* Alloc IsoTpLink statically in RAM */ 235 | static IsoTpLink g_phylink; 236 | static IsoTpLink g_funclink; 237 | 238 | /* Allocate send and receive buffer statically in RAM */ 239 | static uint8_t g_isotpPhyRecvBuf[512]; 240 | static uint8_t g_isotpPhySendBuf[512]; 241 | /* currently functional addressing is not supported with multi-frame messages */ 242 | static uint8_t g_isotpFuncRecvBuf[8]; 243 | static uint8_t g_isotpFuncSendBuf[8]; 244 | 245 | int main(void) { 246 | /* Initialize CAN and other peripherals */ 247 | 248 | /* Initialize link, 0x7TT is the CAN ID you send with */ 249 | isotp_init_link(&g_phylink, 0x7TT, 250 | g_isotpPhySendBuf, sizeof(g_isotpPhySendBuf), 251 | g_isotpPhyRecvBuf, sizeof(g_isotpPhyRecvBuf)); 252 | isotp_init_link(&g_funclink, 0x7TT, 253 | g_isotpFuncSendBuf, sizeof(g_isotpFuncSendBuf), 254 | g_isotpFuncRecvBuf, sizeof(g_isotpFuncRecvBuf)); 255 | 256 | while(1) { 257 | 258 | /* If any CAN messages are received, which are of interest, call isotp_on_can_message to handle the message */ 259 | ret = can_receive(&id, &data, &len); 260 | 261 | /* 0x7RR is CAN ID you want to receive */ 262 | if (RET_OK == ret) { 263 | if (0x7RR == id) { 264 | isotp_on_can_message(&g_phylink, data, len); 265 | } else if (0x7df == id) { 266 | isotp_on_can_message(&g_funclink, data, len); 267 | } 268 | } 269 | 270 | /* Poll link to handle multiple frame transmition */ 271 | isotp_poll(&g_phylink); 272 | isotp_poll(&g_funclink); 273 | 274 | /* You can receive message with isotp_receive. 275 | payload is upper layer message buffer, usually UDS; 276 | payload_size is payload buffer size; 277 | out_size is the actuall read size; 278 | */ 279 | ret = isotp_receive(&g_phylink, payload, payload_size, &out_size); 280 | if (ISOTP_RET_OK == ret) { 281 | /* Handle physical addressing message */ 282 | } 283 | 284 | ret = isotp_receive(&g_funclink, payload, payload_size, &out_size); 285 | if (ISOTP_RET_OK == ret) { 286 | /* Handle functional addressing message */ 287 | } 288 | 289 | /* And send message with isotp_send */ 290 | ret = isotp_send(&g_phylink, payload, payload_size); 291 | if (ISOTP_RET_OK == ret) { 292 | /* Send ok */ 293 | } else { 294 | /* An error occured */ 295 | } 296 | } 297 | 298 | return; 299 | } 300 | ``` 301 | 302 | ## Authors 303 | 304 | Please view [Contributors](#contributors) to see a list of all contributors. 305 | 306 | ## License 307 | 308 | Licensed under the MIT license. 309 | -------------------------------------------------------------------------------- /isotp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "assert.h" 3 | #include "isotp.h" 4 | 5 | /////////////////////////////////////////////////////// 6 | /// STATIC FUNCTIONS /// 7 | /////////////////////////////////////////////////////// 8 | 9 | /* st_min to microsecond */ 10 | static uint8_t isotp_us_to_st_min(uint32_t us) { 11 | if (us <= 127000) { 12 | if (us >= 100 && us <= 900) { 13 | return (uint8_t)(0xF0 + (us / 100)); 14 | } else { 15 | return (uint8_t)(us / 1000u); 16 | } 17 | } 18 | 19 | return 0; 20 | } 21 | 22 | /* st_min to usec */ 23 | static uint32_t isotp_st_min_to_us(uint8_t st_min) { 24 | if (st_min <= 0x7F) { 25 | return st_min * 1000; 26 | } else if (st_min >= 0xF1 && st_min <= 0xF9) { 27 | return (st_min - 0xF0) * 100; 28 | } 29 | return 0; 30 | } 31 | 32 | static int isotp_send_flow_control(const IsoTpLink* link, uint8_t flow_status, uint8_t block_size, uint32_t st_min_us) { 33 | 34 | IsoTpCanMessage message; 35 | int ret; 36 | uint8_t size = 0; 37 | 38 | /* setup message */ 39 | message.as.flow_control.type = ISOTP_PCI_TYPE_FLOW_CONTROL_FRAME; 40 | message.as.flow_control.FS = flow_status; 41 | message.as.flow_control.BS = block_size; 42 | message.as.flow_control.STmin = isotp_us_to_st_min(st_min_us); 43 | 44 | /* send message */ 45 | #ifdef ISO_TP_FRAME_PADDING 46 | (void) memset(message.as.flow_control.reserve, ISO_TP_FRAME_PADDING_VALUE, sizeof(message.as.flow_control.reserve)); 47 | size = sizeof(message); 48 | #else 49 | size = 3; 50 | #endif 51 | 52 | ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, size 53 | #if defined (ISO_TP_USER_SEND_CAN_ARG) 54 | ,link->user_send_can_arg 55 | #endif 56 | ); 57 | 58 | return ret; 59 | } 60 | 61 | static int isotp_send_single_frame(const IsoTpLink* link, uint32_t id) { 62 | (void) id; // Prevent unused variable warning 63 | 64 | IsoTpCanMessage message; 65 | int ret; 66 | uint8_t size = 0; 67 | 68 | /* multi frame message length must greater than 7 */ 69 | assert(link->send_size <= 7); 70 | 71 | /* setup message */ 72 | message.as.single_frame.type = ISOTP_PCI_TYPE_SINGLE; 73 | message.as.single_frame.SF_DL = (uint8_t) link->send_size; 74 | (void) memcpy(message.as.single_frame.data, link->send_buffer, link->send_size); 75 | 76 | /* send message */ 77 | #ifdef ISO_TP_FRAME_PADDING 78 | (void) memset(message.as.single_frame.data + link->send_size, ISO_TP_FRAME_PADDING_VALUE, sizeof(message.as.single_frame.data) - link->send_size); 79 | size = sizeof(message); 80 | #else 81 | size = link->send_size + (uint8_t)1; 82 | #endif 83 | 84 | ret = isotp_user_send_can( 85 | link->send_arbitration_id, message.as.data_array.ptr, size 86 | #if defined (ISO_TP_USER_SEND_CAN_ARG) 87 | ,link->user_send_can_arg 88 | #endif 89 | ); 90 | 91 | return ret; 92 | } 93 | 94 | static int isotp_send_first_frame(IsoTpLink* link, uint32_t id) { 95 | 96 | IsoTpCanMessage message = {0}; 97 | int ret = 0; 98 | 99 | /* multi frame message length must greater than 7 */ 100 | assert(link->send_size > 7); 101 | 102 | if (link->send_size <= 4095) { 103 | /* setup 'short' message */ 104 | message.as.first_frame_short.type = ISOTP_PCI_TYPE_FIRST_FRAME; 105 | message.as.first_frame_short.FF_DL_low = (uint8_t)link->send_size; 106 | message.as.first_frame_short.FF_DL_high = (uint8_t)(0x0F & (link->send_size >> 8)); 107 | (void)memcpy(message.as.first_frame_short.data, link->send_buffer, sizeof(message.as.first_frame_short.data)); 108 | 109 | /* send 'short' message */ 110 | ret = isotp_user_send_can( 111 | id, message.as.data_array.ptr, sizeof(message) 112 | #if defined (ISO_TP_USER_SEND_CAN_ARG) 113 | ,link->user_send_can_arg 114 | #endif 115 | ); 116 | 117 | if (ISOTP_RET_OK == ret) { 118 | link->send_offset += sizeof(message.as.first_frame_short.data); 119 | } 120 | } else { // ISO15765-2:2016 121 | /* setup 'long' message */ 122 | message.as.first_frame_long.set_to_zero_high = 0; 123 | message.as.first_frame_long.set_to_zero_low = 0; 124 | message.as.first_frame_long.type = ISOTP_PCI_TYPE_FIRST_FRAME; 125 | message.as.first_frame_long.FF_DL = LE32TOH(link->send_size); 126 | (void)memcpy(message.as.first_frame_long.data, link->send_buffer, sizeof(message.as.first_frame_long.data)); 127 | 128 | /* send 'long' message */ 129 | ret = isotp_user_send_can( 130 | id, message.as.data_array.ptr, sizeof(message) 131 | #if defined (ISO_TP_USER_SEND_CAN_ARG) 132 | ,link->user_send_can_arg 133 | #endif 134 | ); 135 | 136 | if (ISOTP_RET_OK == ret) { 137 | link->send_offset += sizeof(message.as.first_frame_long.data); 138 | } 139 | } 140 | 141 | link->send_sn = 1; 142 | 143 | return ret; 144 | } 145 | 146 | static int isotp_send_consecutive_frame(IsoTpLink* link) { 147 | 148 | IsoTpCanMessage message; 149 | uint32_t data_length; 150 | int ret; 151 | uint8_t size = 0; 152 | 153 | /* multi frame message length must greater than 7 */ 154 | assert(link->send_size > 7); 155 | 156 | /* setup message */ 157 | message.as.consecutive_frame.type = ISOTP_PCI_TYPE_CONSECUTIVE_FRAME; 158 | message.as.consecutive_frame.SN = link->send_sn; 159 | data_length = link->send_size - link->send_offset; 160 | if (data_length > sizeof(message.as.consecutive_frame.data)) { 161 | data_length = sizeof(message.as.consecutive_frame.data); 162 | } 163 | (void) memcpy(message.as.consecutive_frame.data, link->send_buffer + link->send_offset, data_length); 164 | 165 | /* send message */ 166 | #ifdef ISO_TP_FRAME_PADDING 167 | (void) memset(message.as.consecutive_frame.data + data_length, ISO_TP_FRAME_PADDING_VALUE, sizeof(message.as.consecutive_frame.data) - data_length); 168 | size = sizeof(message); 169 | #else 170 | size = data_length + 1; 171 | #endif 172 | 173 | ret = isotp_user_send_can( 174 | link->send_arbitration_id, 175 | message.as.data_array.ptr, size 176 | #if defined (ISO_TP_USER_SEND_CAN_ARG) 177 | ,link->user_send_can_arg 178 | #endif 179 | ); 180 | 181 | if (ISOTP_RET_OK == ret) { 182 | link->send_offset += data_length; 183 | if (++(link->send_sn) > 0x0F) { 184 | link->send_sn = 0; 185 | } 186 | } 187 | 188 | return ret; 189 | } 190 | 191 | static int isotp_receive_single_frame(IsoTpLink* link, const IsoTpCanMessage* message, uint8_t len) { 192 | /* check data length */ 193 | if ((0 == message->as.single_frame.SF_DL) || (message->as.single_frame.SF_DL > (len - 1))) { 194 | isotp_user_debug("Single-frame length too small."); 195 | return ISOTP_RET_LENGTH; 196 | } 197 | 198 | /* copying data */ 199 | (void) memcpy(link->receive_buffer, message->as.single_frame.data, message->as.single_frame.SF_DL); 200 | link->receive_size = message->as.single_frame.SF_DL; 201 | 202 | return ISOTP_RET_OK; 203 | } 204 | 205 | static int isotp_receive_first_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) { 206 | uint8_t is_long_packet = 0; 207 | uint32_t payload_length; 208 | 209 | if (8 != len) { 210 | isotp_user_debug("First frame should be 8 bytes in length."); 211 | return ISOTP_RET_LENGTH; 212 | } 213 | 214 | /* check data length */ 215 | payload_length = message->as.first_frame_short.FF_DL_high; 216 | payload_length = (payload_length << 8) + message->as.first_frame_short.FF_DL_low; 217 | 218 | /* if length is ZERO we get a long message > 4095bytes of payload */ 219 | if (payload_length == 0) { 220 | is_long_packet = 1; 221 | payload_length = LE32TOH(message->as.first_frame_long.FF_DL); 222 | } 223 | 224 | /* should not use multiple frame transmition */ 225 | if (payload_length <= 7) { 226 | isotp_user_debug("Should not use multiple frame transmission."); 227 | return ISOTP_RET_LENGTH; 228 | } 229 | 230 | if (payload_length > link->receive_buf_size) { 231 | isotp_user_debug("Multi-frame response too large for receiving buffer."); 232 | return ISOTP_RET_OVERFLOW; 233 | } 234 | 235 | /* copying data */ 236 | if (is_long_packet) { 237 | (void)memcpy(link->receive_buffer, message->as.first_frame_long.data, sizeof(message->as.first_frame_long.data)); 238 | link->receive_offset = sizeof(message->as.first_frame_long.data); 239 | } 240 | else { 241 | (void)memcpy(link->receive_buffer, message->as.first_frame_short.data, sizeof(message->as.first_frame_short.data)); 242 | link->receive_offset = sizeof(message->as.first_frame_short.data); 243 | } 244 | 245 | link->receive_size = payload_length; 246 | link->receive_sn = 1; 247 | 248 | return ISOTP_RET_OK; 249 | } 250 | 251 | static int isotp_receive_consecutive_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) { 252 | uint32_t remaining_bytes; 253 | 254 | /* check sn */ 255 | if (link->receive_sn != message->as.consecutive_frame.SN) { 256 | return ISOTP_RET_WRONG_SN; 257 | } 258 | 259 | /* check data length */ 260 | remaining_bytes = link->receive_size - link->receive_offset; 261 | if (remaining_bytes > sizeof(message->as.consecutive_frame.data)) { 262 | remaining_bytes = sizeof(message->as.consecutive_frame.data); 263 | } 264 | if (remaining_bytes > (uint32_t)(len - 1)) { 265 | isotp_user_debug("Consecutive frame too short."); 266 | return ISOTP_RET_LENGTH; 267 | } 268 | 269 | /* copying data */ 270 | (void) memcpy(link->receive_buffer + link->receive_offset, message->as.consecutive_frame.data, remaining_bytes); 271 | 272 | link->receive_offset += remaining_bytes; 273 | if (++(link->receive_sn) > 0x0F) { 274 | link->receive_sn = 0; 275 | } 276 | 277 | return ISOTP_RET_OK; 278 | } 279 | 280 | static int isotp_receive_flow_control_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) { 281 | /* unused args */ 282 | (void) link; 283 | (void) message; 284 | 285 | /* check message length */ 286 | if (len < 3) { 287 | isotp_user_debug("Flow control frame too short."); 288 | return ISOTP_RET_LENGTH; 289 | } 290 | 291 | return ISOTP_RET_OK; 292 | } 293 | 294 | /////////////////////////////////////////////////////// 295 | /// PUBLIC FUNCTIONS /// 296 | /////////////////////////////////////////////////////// 297 | 298 | int isotp_send(IsoTpLink *link, const uint8_t payload[], uint32_t size) { 299 | return isotp_send_with_id(link, link->send_arbitration_id, payload, size); 300 | } 301 | 302 | int isotp_send_with_id(IsoTpLink *link, uint32_t id, const uint8_t payload[], uint32_t size) { 303 | int ret; 304 | 305 | if (link == 0x0) { 306 | isotp_user_debug("Link is null!"); 307 | return ISOTP_RET_ERROR; 308 | } 309 | 310 | if (size > link->send_buf_size) { 311 | isotp_user_debug("Message size too large. Increase ISO_TP_MAX_MESSAGE_SIZE to set a larger buffer\n"); 312 | 313 | char message[ISOTP_MAX_ERROR_MSG_SIZE] = {0}; 314 | int32_t writtenChars = snprintf(&message[0], ISOTP_MAX_ERROR_MSG_SIZE, "Attempted to send %u bytes; max size is %u!\n", (unsigned int)size, (unsigned int)link->send_buf_size); 315 | 316 | assert(writtenChars <= ISOTP_MAX_ERROR_MSG_SIZE); 317 | (void) writtenChars; 318 | 319 | isotp_user_debug(message); 320 | return ISOTP_RET_OVERFLOW; 321 | } 322 | 323 | if (ISOTP_SEND_STATUS_INPROGRESS == link->send_status) { 324 | isotp_user_debug("Abort previous message, transmission in progress.\n"); 325 | return ISOTP_RET_INPROGRESS; 326 | } 327 | 328 | /* copy into local buffer */ 329 | link->send_size = size; 330 | link->send_offset = 0; 331 | (void) memcpy(link->send_buffer, payload, size); 332 | 333 | if (link->send_size < 8) { 334 | /* send single frame */ 335 | ret = isotp_send_single_frame(link, id); 336 | } else { 337 | /* send multi-frame */ 338 | ret = isotp_send_first_frame(link, id); 339 | 340 | /* init multi-frame control flags */ 341 | if (ISOTP_RET_OK == ret) { 342 | link->send_bs_remain = 0; 343 | link->send_st_min_us = 0; 344 | link->send_wtf_count = 0; 345 | link->send_timer_st = isotp_user_get_us(); 346 | link->send_timer_bs = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; 347 | link->send_protocol_result = ISOTP_PROTOCOL_RESULT_OK; 348 | link->send_status = ISOTP_SEND_STATUS_INPROGRESS; 349 | } 350 | } 351 | 352 | return ret; 353 | } 354 | 355 | void isotp_on_can_message(IsoTpLink* link, const uint8_t* data, uint8_t len) { 356 | IsoTpCanMessage message; 357 | int ret; 358 | 359 | if (len < 2 || len > 8) { 360 | return; 361 | } 362 | 363 | memcpy(message.as.data_array.ptr, data, len); 364 | memset(message.as.data_array.ptr + len, 0, sizeof(message.as.data_array.ptr) - len); 365 | 366 | switch (message.as.common.type) { 367 | case ISOTP_PCI_TYPE_SINGLE: { 368 | /* update protocol result */ 369 | if (ISOTP_RECEIVE_STATUS_INPROGRESS == link->receive_status) { 370 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_UNEXP_PDU; 371 | } else { 372 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_OK; 373 | } 374 | 375 | /* handle message */ 376 | ret = isotp_receive_single_frame(link, &message, len); 377 | 378 | if (ISOTP_RET_OK == ret) { 379 | /* change status */ 380 | link->receive_status = ISOTP_RECEIVE_STATUS_FULL; 381 | } 382 | break; 383 | } 384 | case ISOTP_PCI_TYPE_FIRST_FRAME: { 385 | /* update protocol result */ 386 | if (ISOTP_RECEIVE_STATUS_INPROGRESS == link->receive_status) { 387 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_UNEXP_PDU; 388 | } else { 389 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_OK; 390 | } 391 | 392 | /* handle message */ 393 | ret = isotp_receive_first_frame(link, &message, len); 394 | 395 | /* if overflow happened */ 396 | if (ISOTP_RET_OVERFLOW == ret) { 397 | /* update protocol result */ 398 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_BUFFER_OVFLW; 399 | /* change status */ 400 | link->receive_status = ISOTP_RECEIVE_STATUS_IDLE; 401 | /* send error message */ 402 | isotp_send_flow_control(link, PCI_FLOW_STATUS_OVERFLOW, 0, 0); 403 | break; 404 | } 405 | 406 | /* if receive successful */ 407 | if (ISOTP_RET_OK == ret) { 408 | /* change status */ 409 | link->receive_status = ISOTP_RECEIVE_STATUS_INPROGRESS; 410 | /* send fc frame */ 411 | link->receive_bs_count = ISO_TP_DEFAULT_BLOCK_SIZE; 412 | isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE, link->receive_bs_count, ISO_TP_DEFAULT_ST_MIN_US); 413 | /* refresh timer cs */ 414 | link->receive_timer_cr = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; 415 | } 416 | 417 | break; 418 | } 419 | case ISOTP_PCI_TYPE_CONSECUTIVE_FRAME: { 420 | /* check if in receiving status */ 421 | if (ISOTP_RECEIVE_STATUS_INPROGRESS != link->receive_status) { 422 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_UNEXP_PDU; 423 | break; 424 | } 425 | 426 | /* handle message */ 427 | ret = isotp_receive_consecutive_frame(link, &message, len); 428 | 429 | /* if wrong sn */ 430 | if (ISOTP_RET_WRONG_SN == ret) { 431 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_WRONG_SN; 432 | link->receive_status = ISOTP_RECEIVE_STATUS_IDLE; 433 | break; 434 | } 435 | 436 | /* if success */ 437 | if (ISOTP_RET_OK == ret) { 438 | /* refresh timer cs */ 439 | link->receive_timer_cr = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; 440 | 441 | /* receive finished */ 442 | if (link->receive_offset >= link->receive_size) { 443 | link->receive_status = ISOTP_RECEIVE_STATUS_FULL; 444 | } else { 445 | /* send fc when bs reaches limit */ 446 | if (0 == --link->receive_bs_count) { 447 | link->receive_bs_count = ISO_TP_DEFAULT_BLOCK_SIZE; 448 | isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE, link->receive_bs_count, ISO_TP_DEFAULT_ST_MIN_US); 449 | } 450 | } 451 | } 452 | 453 | break; 454 | } 455 | case ISOTP_PCI_TYPE_FLOW_CONTROL_FRAME: 456 | /* handle fc frame only when sending in progress */ 457 | if (ISOTP_SEND_STATUS_INPROGRESS != link->send_status) { 458 | break; 459 | } 460 | 461 | /* handle message */ 462 | ret = isotp_receive_flow_control_frame(link, &message, len); 463 | 464 | if (ISOTP_RET_OK == ret) { 465 | /* refresh bs timer */ 466 | link->send_timer_bs = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; 467 | 468 | /* overflow */ 469 | if (PCI_FLOW_STATUS_OVERFLOW == message.as.flow_control.FS) { 470 | link->send_protocol_result = ISOTP_PROTOCOL_RESULT_BUFFER_OVFLW; 471 | link->send_status = ISOTP_SEND_STATUS_ERROR; 472 | } 473 | 474 | /* wait */ 475 | else if (PCI_FLOW_STATUS_WAIT == message.as.flow_control.FS) { 476 | link->send_wtf_count += 1; 477 | /* wait exceed allowed count */ 478 | if (link->send_wtf_count > ISO_TP_MAX_WFT_NUMBER) { 479 | link->send_protocol_result = ISOTP_PROTOCOL_RESULT_WFT_OVRN; 480 | link->send_status = ISOTP_SEND_STATUS_ERROR; 481 | } 482 | } 483 | 484 | /* permit send */ 485 | else if (PCI_FLOW_STATUS_CONTINUE == message.as.flow_control.FS) { 486 | if (0 == message.as.flow_control.BS) { 487 | link->send_bs_remain = ISOTP_INVALID_BS; 488 | } else { 489 | link->send_bs_remain = message.as.flow_control.BS; 490 | } 491 | uint32_t message_st_min_us = isotp_st_min_to_us(message.as.flow_control.STmin); 492 | link->send_st_min_us = message_st_min_us > ISO_TP_DEFAULT_ST_MIN_US ? message_st_min_us : ISO_TP_DEFAULT_ST_MIN_US; // prefer as much st_min as possible for stability? 493 | link->send_wtf_count = 0; 494 | } 495 | } 496 | break; 497 | default: 498 | break; 499 | }; 500 | 501 | return; 502 | } 503 | 504 | int isotp_receive(IsoTpLink *link, uint8_t *payload, const uint32_t payload_size, uint32_t *out_size) { 505 | uint32_t copylen; 506 | 507 | if (ISOTP_RECEIVE_STATUS_FULL != link->receive_status) { 508 | return ISOTP_RET_NO_DATA; 509 | } 510 | 511 | copylen = link->receive_size; 512 | if (copylen > payload_size) { 513 | copylen = payload_size; 514 | } 515 | 516 | memcpy(payload, link->receive_buffer, copylen); 517 | *out_size = copylen; 518 | 519 | link->receive_status = ISOTP_RECEIVE_STATUS_IDLE; 520 | 521 | return ISOTP_RET_OK; 522 | } 523 | 524 | void isotp_init_link(IsoTpLink *link, uint32_t sendid, uint8_t *sendbuf, uint32_t sendbufsize, uint8_t *recvbuf, uint32_t recvbufsize) { 525 | memset(link, 0, sizeof(*link)); 526 | link->receive_status = ISOTP_RECEIVE_STATUS_IDLE; 527 | link->send_status = ISOTP_SEND_STATUS_IDLE; 528 | link->send_arbitration_id = sendid; 529 | link->send_buffer = sendbuf; 530 | link->send_buf_size = sendbufsize; 531 | link->receive_buffer = recvbuf; 532 | link->receive_buf_size = recvbufsize; 533 | 534 | return; 535 | } 536 | 537 | void isotp_poll(IsoTpLink *link) { 538 | int ret = 0; 539 | 540 | /* only polling when operation in progress */ 541 | if (ISOTP_SEND_STATUS_INPROGRESS == link->send_status) { 542 | 543 | /* continue send data */ 544 | if (/* send data if bs_remain is invalid or bs_remain large than zero */ 545 | (ISOTP_INVALID_BS == link->send_bs_remain || link->send_bs_remain > 0) && 546 | /* and if st_min is zero or go beyond interval time */ 547 | (0 == link->send_st_min_us || IsoTpTimeAfter(isotp_user_get_us(), link->send_timer_st)) 548 | ) { 549 | 550 | ret = isotp_send_consecutive_frame(link); 551 | if (ISOTP_RET_OK == ret) { 552 | if (ISOTP_INVALID_BS != link->send_bs_remain) { 553 | link->send_bs_remain -= 1; 554 | } 555 | link->send_timer_bs = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; 556 | link->send_timer_st = isotp_user_get_us() + link->send_st_min_us; 557 | 558 | /* check if send finish */ 559 | if (link->send_offset >= link->send_size) { 560 | link->send_status = ISOTP_SEND_STATUS_IDLE; 561 | } 562 | } else if (ISOTP_RET_NOSPACE == ret) { 563 | /* shim reported that it isn't able to send a frame at present, retry on next call */ 564 | } else { 565 | link->send_status = ISOTP_SEND_STATUS_ERROR; 566 | } 567 | } 568 | 569 | /* check timeout */ 570 | if (IsoTpTimeAfter(isotp_user_get_us(), link->send_timer_bs)) { 571 | link->send_protocol_result = ISOTP_PROTOCOL_RESULT_TIMEOUT_BS; 572 | link->send_status = ISOTP_SEND_STATUS_ERROR; 573 | } 574 | } 575 | 576 | /* only polling when operation in progress */ 577 | if (ISOTP_RECEIVE_STATUS_INPROGRESS == link->receive_status) { 578 | 579 | /* check timeout */ 580 | if ((link->receive_timer_cr > 0) && IsoTpTimeAfter(isotp_user_get_us(), link->receive_timer_cr)) { 581 | link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_TIMEOUT_CR; 582 | link->receive_status = ISOTP_RECEIVE_STATUS_IDLE; 583 | } 584 | } 585 | 586 | return; 587 | } 588 | -------------------------------------------------------------------------------- /isotp.h: -------------------------------------------------------------------------------- 1 | #ifndef ISOTPC_H 2 | #define ISOTPC_H 3 | 4 | #include 5 | #include 6 | 7 | #ifdef __cplusplus 8 | #include 9 | 10 | extern "C" { 11 | #endif 12 | 13 | #include "isotp_defines.h" 14 | #include "isotp_config.h" 15 | #include "isotp_user.h" 16 | 17 | /** 18 | * @brief Struct containing the data for linking an application to a CAN instance. 19 | * The data stored in this struct is used internally and may be used by software programs 20 | * using this library. 21 | */ 22 | typedef struct IsoTpLink { 23 | /* sender paramters */ 24 | uint32_t send_arbitration_id; /* used to reply consecutive frame */ 25 | /* message buffer */ 26 | uint8_t* send_buffer; 27 | uint32_t send_buf_size; 28 | uint32_t send_size; 29 | uint32_t send_offset; 30 | /* multi-frame flags */ 31 | uint8_t send_sn; 32 | uint32_t send_bs_remain; /* Remaining block size */ 33 | uint32_t send_st_min_us; /* Separation Time between consecutive frames */ 34 | uint8_t send_wtf_count; /* Maximum number of FC.Wait frame transmissions */ 35 | uint32_t send_timer_st; /* Last time send consecutive frame */ 36 | uint32_t send_timer_bs; /* Time until reception of the next FlowControl N_PDU 37 | start at sending FF, CF, receive FC 38 | end at receive FC */ 39 | int send_protocol_result; 40 | uint8_t send_status; 41 | /* receiver paramters */ 42 | uint32_t receive_arbitration_id; 43 | /* message buffer */ 44 | uint8_t* receive_buffer; 45 | uint32_t receive_buf_size; 46 | uint32_t receive_size; 47 | uint32_t receive_offset; 48 | /* multi-frame control */ 49 | uint8_t receive_sn; 50 | uint8_t receive_bs_count; /* Maximum number of FC.Wait frame transmissions */ 51 | uint32_t receive_timer_cr; /* Time until transmission of the next ConsecutiveFrame N_PDU 52 | start at sending FC, receive CF 53 | end at receive FC */ 54 | int receive_protocol_result; 55 | uint8_t receive_status; 56 | 57 | #if defined(ISO_TP_USER_SEND_CAN_ARG) 58 | void* user_send_can_arg; 59 | #endif 60 | } IsoTpLink; 61 | 62 | /** 63 | * @brief Initialises the ISO-TP library. 64 | * 65 | * @param link The @code IsoTpLink @endcode instance used for transceiving data. 66 | * @param sendid The ID used to send data to other CAN nodes. 67 | * @param sendbuf A pointer to an area in memory which can be used as a buffer for data to be sent. 68 | * @param sendbufsize The size of the buffer area. 69 | * @param recvbuf A pointer to an area in memory which can be used as a buffer for data to be received. 70 | * @param recvbufsize The size of the buffer area. 71 | */ 72 | void isotp_init_link(IsoTpLink *link, uint32_t sendid, 73 | uint8_t *sendbuf, uint32_t sendbufsize, 74 | uint8_t *recvbuf, uint32_t recvbufsize); 75 | 76 | /** 77 | * @brief Polling function; call this function periodically to handle timeouts, send consecutive frames, etc. 78 | * 79 | * @param link The @code IsoTpLink @endcode instance used. 80 | */ 81 | void isotp_poll(IsoTpLink *link); 82 | 83 | /** 84 | * @brief Handles incoming CAN messages. 85 | * Determines whether an incoming message is a valid ISO-TP frame or not and handles it accordingly. 86 | * 87 | * @param link The @code IsoTpLink @endcode instance used for transceiving data. 88 | * @param data The data received via CAN. 89 | * @param len The length of the data received. 90 | */ 91 | void isotp_on_can_message(IsoTpLink *link, const uint8_t *data, uint8_t len); 92 | 93 | /** 94 | * @brief Sends ISO-TP frames via CAN, using the ID set in the initialising function. 95 | * 96 | * Single-frame messages will be sent immediately when calling this function. 97 | * Multi-frame messages will be sent consecutively when calling isotp_poll. 98 | * 99 | * @param link The @code IsoTpLink @endcode instance used for transceiving data. 100 | * @param payload The payload to be sent. (Up to 4095 bytes). 101 | * @param size The size of the payload to be sent. 102 | * 103 | * @return Possible return values: 104 | * - @code ISOTP_RET_OVERFLOW @endcode 105 | * - @code ISOTP_RET_INPROGRESS @endcode 106 | * - @code ISOTP_RET_OK @endcode 107 | * - The return value of the user shim function isotp_user_send_can(). 108 | */ 109 | int isotp_send(IsoTpLink *link, const uint8_t payload[], uint32_t size); 110 | 111 | /** 112 | * @brief See @link isotp_send @endlink, with the exception that this function is used only for functional addressing. 113 | */ 114 | int isotp_send_with_id(IsoTpLink *link, uint32_t id, const uint8_t payload[], uint32_t size); 115 | 116 | /** 117 | * @brief Receives and parses the received data and copies the parsed data in to the internal buffer. 118 | * @param link The @link IsoTpLink @endlink instance used to transceive data. 119 | * @param payload A pointer to an area in memory where the raw data is copied from. 120 | * @param payload_size The size of the received (raw) CAN data. 121 | * @param out_size A reference to a variable which will contain the size of the actual (parsed) data. 122 | * 123 | * @return Possible return values: 124 | * - @link ISOTP_RET_OK @endlink 125 | * - @link ISOTP_RET_NO_DATA @endlink 126 | */ 127 | int isotp_receive(IsoTpLink *link, uint8_t *payload, const uint32_t payload_size, uint32_t *out_size); 128 | 129 | #ifdef __cplusplus 130 | } 131 | #endif 132 | 133 | #endif // ISOTPC_H 134 | 135 | -------------------------------------------------------------------------------- /isotp_config.h: -------------------------------------------------------------------------------- 1 | #ifndef ISOTPC_CONFIG_H 2 | #define ISOTPC_CONFIG_H 3 | 4 | /* Max number of messages the receiver can receive at one time, this value 5 | * is affected by can driver queue length 6 | */ 7 | #define ISO_TP_DEFAULT_BLOCK_SIZE 8 8 | 9 | /* The STmin parameter value specifies the minimum time gap allowed between 10 | * the transmission of consecutive frame network protocol data units 11 | */ 12 | #define ISO_TP_DEFAULT_ST_MIN_US 0 13 | 14 | /* This parameter indicate how many FC N_PDU WTs can be transmitted by the 15 | * receiver in a row. 16 | */ 17 | #define ISO_TP_MAX_WFT_NUMBER 1 18 | 19 | /* Private: The default timeout to use when waiting for a response during a 20 | * multi-frame send or receive. 21 | */ 22 | #define ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US 100000 23 | 24 | /* Private: Determines if by default, padding is added to ISO-TP message frames. 25 | */ 26 | //#define ISO_TP_FRAME_PADDING 27 | 28 | /* Private: Value to use when padding frames if enabled by ISO_TP_FRAME_PADDING 29 | */ 30 | #ifndef ISO_TP_FRAME_PADDING_VALUE 31 | #define ISO_TP_FRAME_PADDING_VALUE 0xAA 32 | #endif 33 | 34 | /* Private: Determines if by default, an additional argument is present in the 35 | * definition of isotp_user_send_can. 36 | */ 37 | //#define ISO_TP_USER_SEND_CAN_ARG 38 | 39 | #endif // ISOTPC_CONFIG_H 40 | -------------------------------------------------------------------------------- /isotp_defines.h: -------------------------------------------------------------------------------- 1 | #ifndef ISOTPC_USER_DEFINITIONS_H 2 | #define ISOTPC_USER_DEFINITIONS_H 3 | 4 | #include 5 | 6 | /************************************************************** 7 | * compiler specific defines 8 | *************************************************************/ 9 | #ifdef __GNUC__ 10 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 11 | #define ISOTP_BYTE_ORDER_LITTLE_ENDIAN 12 | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ 13 | #else 14 | #error "unsupported byte ordering" 15 | #endif 16 | 17 | #define ISOTP_PACKED_STRUCT(content) typedef struct __attribute__((packed)) content 18 | #endif 19 | 20 | /************************************************************** 21 | * OS specific defines 22 | *************************************************************/ 23 | #ifdef _WIN32 24 | #define ISOTP_PACKED_STRUCT(content) __pragma(pack(push, 1)) typedef struct content __pragma(pack(pop)) 25 | 26 | #define snprintf _snprintf 27 | 28 | #include 29 | #define ISOTP_BYTE_ORDER_LITTLE_ENDIAN 30 | #define __builtin_bswap8 _byteswap_uint8 31 | #define __builtin_bswap16 _byteswap_uint16 32 | #define __builtin_bswap32 _byteswap_uint32 33 | #define __builtin_bswap64 _byteswap_uint64 34 | #endif 35 | 36 | #define LE32TOH(le) ((uint32_t)(((le) << 24) | (((le) & 0x0000FF00) << 8) | (((le) & 0x00FF0000) >> 8) | ((le) >> 24))) 37 | 38 | /************************************************************** 39 | * internal used defines 40 | *************************************************************/ 41 | #define ISOTP_RET_OK 0 42 | #define ISOTP_RET_ERROR -1 43 | #define ISOTP_RET_INPROGRESS -2 44 | #define ISOTP_RET_OVERFLOW -3 45 | #define ISOTP_RET_WRONG_SN -4 46 | #define ISOTP_RET_NO_DATA -5 47 | #define ISOTP_RET_TIMEOUT -6 48 | #define ISOTP_RET_LENGTH -7 49 | #define ISOTP_RET_NOSPACE -8 50 | 51 | /* return logic true if 'a' is after 'b' */ 52 | #define IsoTpTimeAfter(a,b) ((int32_t)((int32_t)(b) - (int32_t)(a)) < 0) 53 | 54 | /* invalid bs */ 55 | #define ISOTP_INVALID_BS 0xFFFF 56 | 57 | /* Define the maximum amount of characters allowed in an error message. This fixes code which would otherwise break on Microsoft's dumb platform. */ 58 | #define ISOTP_MAX_ERROR_MSG_SIZE 128 59 | 60 | /* ISOTP sender status */ 61 | typedef enum { 62 | ISOTP_SEND_STATUS_IDLE, 63 | ISOTP_SEND_STATUS_INPROGRESS, 64 | ISOTP_SEND_STATUS_ERROR, 65 | } IsoTpSendStatusTypes; 66 | 67 | /* ISOTP receiver status */ 68 | typedef enum { 69 | ISOTP_RECEIVE_STATUS_IDLE, 70 | ISOTP_RECEIVE_STATUS_INPROGRESS, 71 | ISOTP_RECEIVE_STATUS_FULL, 72 | } IsoTpReceiveStatusTypes; 73 | 74 | /* can fram defination */ 75 | #if defined(ISOTP_BYTE_ORDER_LITTLE_ENDIAN) 76 | typedef struct { 77 | uint8_t reserve_1:4; 78 | uint8_t type:4; 79 | uint8_t reserve_2[7]; 80 | } IsoTpPciType; 81 | 82 | typedef struct { 83 | uint8_t SF_DL:4; 84 | uint8_t type:4; 85 | uint8_t data[7]; 86 | } IsoTpSingleFrame; 87 | 88 | typedef struct { 89 | uint8_t FF_DL_high:4; 90 | uint8_t type:4; 91 | uint8_t FF_DL_low; 92 | uint8_t data[6]; 93 | } IsoTpFirstFrameShort; 94 | 95 | ISOTP_PACKED_STRUCT({ 96 | uint8_t set_to_zero_high : 4; 97 | uint8_t type : 4; 98 | uint8_t set_to_zero_low; 99 | uint32_t FF_DL; 100 | uint8_t data[2]; 101 | } IsoTpFirstFrameLong); 102 | 103 | typedef struct { 104 | uint8_t SN:4; 105 | uint8_t type:4; 106 | uint8_t data[7]; 107 | } IsoTpConsecutiveFrame; 108 | 109 | typedef struct { 110 | uint8_t FS:4; 111 | uint8_t type:4; 112 | uint8_t BS; 113 | uint8_t STmin; 114 | uint8_t reserve[5]; 115 | } IsoTpFlowControl; 116 | 117 | #else 118 | 119 | typedef struct { 120 | uint8_t type:4; 121 | uint8_t reserve_1:4; 122 | uint8_t reserve_2[7]; 123 | } IsoTpPciType; 124 | 125 | /* 126 | * single frame 127 | * +-------------------------+-----+ 128 | * | byte #0 | ... | 129 | * +-------------------------+-----+ 130 | * | nibble #0 | nibble #1 | ... | 131 | * +-------------+-----------+ ... + 132 | * | PCIType = 0 | SF_DL | ... | 133 | * +-------------+-----------+-----+ 134 | */ 135 | typedef struct { 136 | uint8_t type:4; 137 | uint8_t SF_DL:4; 138 | uint8_t data[7]; 139 | } IsoTpSingleFrame; 140 | 141 | /* 142 | * first frame short 143 | * +-------------------------+-----------------------+-----+ 144 | * | byte #0 | byte #1 | ... | 145 | * +-------------------------+-----------+-----------+-----+ 146 | * | nibble #0 | nibble #1 | nibble #2 | nibble #3 | ... | 147 | * +-------------+-----------+-----------+-----------+-----+ 148 | * | PCIType = 1 | FF_DL | ... | 149 | * +-------------+-----------+-----------------------+-----+ 150 | */ 151 | typedef struct { 152 | uint8_t FF_DL_high:4; 153 | uint8_t type:4; 154 | uint8_t FF_DL_low; 155 | uint8_t data[6]; 156 | } IsoTpFirstFrameShort; 157 | 158 | /* 159 | * first frame long 160 | * +-------------------------+-----------------------+---------+---------+---------+---------+ 161 | * | byte #0 | byte #1 | byte #2 | byte #3 | byte #4 | byte #5 | 162 | * +-------------------------+-----------+-----------+---------+---------+---------+---------+ 163 | * | nibble #0 | nibble #1 | nibble #2 | nibble #3 | ... | 164 | * +-------------+-----------+-----------+-----------+---------------------------------------+ 165 | * | PCIType = 1 | unused=0 | escape sequence = 0 | FF_DL | 166 | * +-------------+-----------+-----------------------+---------------------------------------+ 167 | */ 168 | ISOTP_PACKED_STRUCT({ 169 | uint8_t set_to_zero_high:4; 170 | uint8_t type:4; 171 | uint8_t set_to_zero_low; 172 | uint32_t FF_DL; 173 | uint8_t data[2]; 174 | } IsoTpFirstFrameLong); 175 | 176 | /* 177 | * consecutive frame 178 | * +-------------------------+-----+ 179 | * | byte #0 | ... | 180 | * +-------------------------+-----+ 181 | * | nibble #0 | nibble #1 | ... | 182 | * +-------------+-----------+ ... + 183 | * | PCIType = 0 | SN | ... | 184 | * +-------------+-----------+-----+ 185 | */ 186 | typedef struct { 187 | uint8_t type:4; 188 | uint8_t SN:4; 189 | uint8_t data[7]; 190 | } IsoTpConsecutiveFrame; 191 | 192 | /* 193 | * flow control frame 194 | * +-------------------------+-----------------------+-----------------------+-----+ 195 | * | byte #0 | byte #1 | byte #2 | ... | 196 | * +-------------------------+-----------+-----------+-----------+-----------+-----+ 197 | * | nibble #0 | nibble #1 | nibble #2 | nibble #3 | nibble #4 | nibble #5 | ... | 198 | * +-------------+-----------+-----------+-----------+-----------+-----------+-----+ 199 | * | PCIType = 1 | FS | BS | STmin | ... | 200 | * +-------------+-----------+-----------------------+-----------------------+-----+ 201 | */ 202 | typedef struct { 203 | uint8_t type:4; 204 | uint8_t FS:4; 205 | uint8_t BS; 206 | uint8_t STmin; 207 | uint8_t reserve[5]; 208 | } IsoTpFlowControl; 209 | 210 | #endif 211 | 212 | typedef struct { 213 | uint8_t ptr[8]; 214 | } IsoTpDataArray; 215 | 216 | typedef struct { 217 | union { 218 | IsoTpPciType common; 219 | IsoTpSingleFrame single_frame; 220 | IsoTpFirstFrameShort first_frame_short; 221 | IsoTpFirstFrameLong first_frame_long; 222 | IsoTpConsecutiveFrame consecutive_frame; 223 | IsoTpFlowControl flow_control; 224 | IsoTpDataArray data_array; 225 | } as; 226 | } IsoTpCanMessage; 227 | 228 | /************************************************************** 229 | * protocol specific defines 230 | *************************************************************/ 231 | 232 | /* Private: Protocol Control Information (PCI) types, for identifying each frame of an ISO-TP message. 233 | */ 234 | typedef enum { 235 | ISOTP_PCI_TYPE_SINGLE = 0x0, 236 | ISOTP_PCI_TYPE_FIRST_FRAME = 0x1, 237 | TSOTP_PCI_TYPE_CONSECUTIVE_FRAME = 0x2, 238 | ISOTP_PCI_TYPE_FLOW_CONTROL_FRAME = 0x3, 239 | 240 | ISOTP_PCI_TYPE_CONSECUTIVE_FRAME = 0x2, // Typo fix; but keep broken value for backwards-compat. 241 | } IsoTpProtocolControlInformation; 242 | 243 | /* Private: Protocol Control Information (PCI) flow control identifiers. 244 | */ 245 | typedef enum { 246 | PCI_FLOW_STATUS_CONTINUE = 0x0, 247 | PCI_FLOW_STATUS_WAIT = 0x1, 248 | PCI_FLOW_STATUS_OVERFLOW = 0x2 249 | } IsoTpFlowStatus; 250 | 251 | /* Private: network layer resault code. 252 | */ 253 | #define ISOTP_PROTOCOL_RESULT_OK 0 254 | #define ISOTP_PROTOCOL_RESULT_TIMEOUT_A -1 255 | #define ISOTP_PROTOCOL_RESULT_TIMEOUT_BS -2 256 | #define ISOTP_PROTOCOL_RESULT_TIMEOUT_CR -3 257 | #define ISOTP_PROTOCOL_RESULT_WRONG_SN -4 258 | #define ISOTP_PROTOCOL_RESULT_INVALID_FS -5 259 | #define ISOTP_PROTOCOL_RESULT_UNEXP_PDU -6 260 | #define ISOTP_PROTOCOL_RESULT_WFT_OVRN -7 261 | #define ISOTP_PROTOCOL_RESULT_BUFFER_OVFLW -8 262 | #define ISOTP_PROTOCOL_RESULT_ERROR -9 263 | 264 | #endif // ISOTPC_USER_DEFINITIONS_H -------------------------------------------------------------------------------- /isotp_user.h: -------------------------------------------------------------------------------- 1 | #ifndef ISOTPC_USER_H 2 | #define ISOTPC_USER_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** @brief user implemented, print debug message */ 11 | void isotp_user_debug(const char* message, ...); 12 | 13 | /** 14 | * @brief user implemented, send can message. should return ISOTP_RET_OK when success. 15 | * 16 | * @return may return ISOTP_RET_NOSPACE if the CAN transfer should be retried later 17 | * or ISOTP_RET_ERROR if transmission couldn't be completed 18 | */ 19 | int isotp_user_send_can(const uint32_t arbitration_id, 20 | const uint8_t* data, const uint8_t size 21 | #if ISO_TP_USER_SEND_CAN_ARG 22 | ,void *arg 23 | #endif 24 | ); 25 | 26 | /** 27 | * @brief user implemented, gets the amount of time passed since the last call in microseconds 28 | */ 29 | uint32_t isotp_user_get_us(void); 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif // ISOTPC_USER_H 36 | 37 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "isotp-c", 3 | "version": "v1.1.2", 4 | "description": "ISO 15765-2 Support Library in C", 5 | "keywords": "c,windows,linux,c-plus-plus,arm,multi-platform,microcontroller,embedded,mips,can,x86,ppc,powerpc,uds,embedded-c,controller-area-network,iso-tp,unified-diagnostics-services,iso-15765-2,15765-2", 6 | "authors": { 7 | "name": "SimonCahill", 8 | "url": "https://github.com/SimonCahill/isotp-c", 9 | "maintainer": true 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git@github.com:SimonCahill/isotp-c.git" 14 | }, 15 | "frameworks": "*", 16 | "platforms": "*", 17 | "headers": "isotp.h", 18 | "build": { 19 | "libArchive": false 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /vars.mk: -------------------------------------------------------------------------------- 1 | ### 2 | # CROSS variable 3 | # Use for cross-compilation. 4 | # Uncommenting and setting this variable to the prefix of 5 | # your cross compiler will allow you to cross compile this library. 6 | ### 7 | #CROSS=powerpc-linux-gnu 8 | 9 | ### 10 | # LANG variable 11 | # Set this value according to the language 12 | # you wish to compile against. 13 | # Possible (legal) values: 14 | # - C [c] 15 | # - C++ [c++] 16 | ### 17 | LANG := "C++" 18 | 19 | ### 20 | # STD variables. 21 | # Do NOT set the STD variable. 22 | # Instead, set the C/C++ STD variables 23 | # according to the standard you wish to use. 24 | ### 25 | CSTD := "gnu99" 26 | CPPSTD := "c++0x" 27 | 28 | ### 29 | # OUTPUT_NAME variable. 30 | # This variable contains the name of the output file (the .so). 31 | ### 32 | LIB_NAME := "libisotp.so" 33 | MAJOR_VER := "1" 34 | MINOR_VER := "5" 35 | REVISION := "0" 36 | OUTPUT_NAME := $(LIB_NAME).$(MAJOR_VER).$(MINOR_VER).$(REVISION) 37 | 38 | ### 39 | # INSTALL_DIR variable. 40 | # Set this variable to the location where the lib should be installed. 41 | ### 42 | INSTALL_DIR := /usr/lib 43 | 44 | ### 45 | # Compute compiler and language standard to use 46 | # This section determines which compiler to use. 47 | ### 48 | ifeq ($(LANG), "C++") 49 | STD := "-std=$(CPPSTD)" 50 | ifeq ($(strip $(CROSS)),) 51 | COMP := g++ 52 | else 53 | COMP := $(CROSS)g++ 54 | endif 55 | endif 56 | ifeq ($(LANG), "C") 57 | STD := "-std=$(CSTD)" 58 | ifeq ($(strip $(CROSS)),) 59 | COMP := gcc 60 | else 61 | COMP := $(CROSS)gcc 62 | endif 63 | endif 64 | --------------------------------------------------------------------------------