├── .clang-format ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── cmake.yml ├── .gitignore ├── .travis.yml ├── AUTHORS ├── ChangeLog.txt ├── README.md ├── TODO ├── bin ├── mingw │ └── setup_mingw.bat ├── posix │ ├── setup_posix.sh │ └── setup_posix_fuzz_afl.sh └── win32 │ └── setup_windows.bat ├── data ├── OpENerPC.stc └── opener_sample_app.eds ├── fuzz ├── imgs │ └── fuzz.png ├── inputs │ ├── cip_req_forward_open │ ├── cip_req_list_identity_cip │ ├── enip_req_list_identity │ └── enip_req_register_session └── scripts │ └── send_testcase.py ├── license.txt ├── sonar-project.properties ├── source ├── .gitignore ├── CMakeLists.txt ├── buildsupport │ ├── CodeCoverage.cmake │ ├── MINGW │ │ └── OpENer_PLATFORM_INCLUDES.cmake │ ├── OpENer.cmake │ ├── OpENer_CIP_Object_generator.cmake │ ├── OpENer_Tests.cmake │ ├── OpENer_function_checks.cmake │ ├── POSIX │ │ └── OpENer_PLATFORM_INCLUDES.cmake │ ├── Toolchain │ │ └── Toolchain-EABI-ARM-Generic.cmake │ └── WIN32 │ │ └── OpENer_PLATFORM_INCLUDES.cmake ├── doc │ ├── STM32 │ │ ├── LwIP_HTTP_Server_Netconn_RTOS_OpENer.patch │ │ ├── OpENer STM32 Paths and Symbols.xml │ │ └── OpENer STM32 Port.pdf │ ├── coding_rules │ │ ├── opener_coding_rules.pdf │ │ └── src │ │ │ └── opener_coding_rules.tex │ └── opener.bib ├── opener.doxyfile.in ├── src │ ├── .gitignore │ ├── CMakeLists.txt │ ├── cip │ │ ├── CMakeLists.txt │ │ ├── appcontype.c │ │ ├── appcontype.h │ │ ├── cipassembly.c │ │ ├── cipassembly.h │ │ ├── cipclass3connection.c │ │ ├── cipclass3connection.h │ │ ├── cipcommon.c │ │ ├── cipcommon.h │ │ ├── cipconnectionmanager.c │ │ ├── cipconnectionmanager.h │ │ ├── cipconnectionobject.c │ │ ├── cipconnectionobject.h │ │ ├── cipdlr.c │ │ ├── cipdlr.h │ │ ├── cipelectronickey.c │ │ ├── cipelectronickey.h │ │ ├── cipepath.c │ │ ├── cipepath.h │ │ ├── ciperror.h │ │ ├── cipethernetlink.c │ │ ├── cipethernetlink.h │ │ ├── cipidentity.c │ │ ├── cipidentity.h │ │ ├── cipioconnection.c │ │ ├── cipioconnection.h │ │ ├── cipmessagerouter.c │ │ ├── cipmessagerouter.h │ │ ├── cipqos.c │ │ ├── cipqos.h │ │ ├── cipstring.c │ │ ├── cipstring.h │ │ ├── cipstringi.c │ │ ├── cipstringi.h │ │ ├── ciptcpipinterface.c │ │ ├── ciptcpipinterface.h │ │ ├── ciptypes.c │ │ └── ciptypes.h │ ├── cip_objects │ │ └── CMakeLists.txt │ ├── enet_encap │ │ ├── CMakeLists.txt │ │ ├── cpf.c │ │ ├── cpf.h │ │ ├── encap.c │ │ ├── encap.h │ │ ├── endianconv.c │ │ └── endianconv.h │ ├── opener_api.h │ ├── ports │ │ ├── CMakeLists.txt │ │ ├── MINGW │ │ │ ├── CMakeLists.txt │ │ │ ├── main.c │ │ │ ├── networkconfig.c │ │ │ ├── networkconfig.h │ │ │ ├── networkhandler.c │ │ │ ├── opener_error.c │ │ │ ├── platform_network_includes.h │ │ │ └── sample_application │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── ethlinkcbs.c │ │ │ │ ├── ethlinkcbs.h │ │ │ │ ├── opener_user_conf.h │ │ │ │ └── sampleapplication.c │ │ ├── POSIX │ │ │ ├── CMakeLists.txt │ │ │ ├── main.c │ │ │ ├── networkconfig.c │ │ │ ├── networkconfig.h │ │ │ ├── networkhandler.c │ │ │ ├── opener_error.c │ │ │ ├── platform_network_includes.h │ │ │ └── sample_application │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── ethlinkcbs.c │ │ │ │ ├── ethlinkcbs.h │ │ │ │ ├── opener_user_conf.h │ │ │ │ └── sampleapplication.c │ │ ├── STM32 │ │ │ ├── networkconfig.c │ │ │ ├── networkconfig.h │ │ │ ├── networkhandler.c │ │ │ ├── opener.c │ │ │ ├── opener.h │ │ │ ├── opener_error.c │ │ │ ├── platform_network_includes.h │ │ │ └── sample_application │ │ │ │ ├── ethlinkcbs.c │ │ │ │ ├── ethlinkcbs.h │ │ │ │ ├── opener_user_conf.h │ │ │ │ └── sampleapplication.c │ │ ├── WIN32 │ │ │ ├── CMakeLists.txt │ │ │ ├── main.c │ │ │ ├── networkconfig.c │ │ │ ├── networkconfig.h │ │ │ ├── networkhandler.c │ │ │ ├── opener_error.c │ │ │ ├── platform_network_includes.h │ │ │ └── sample_application │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── ethlinkcbs.c │ │ │ │ ├── ethlinkcbs.h │ │ │ │ ├── opener_user_conf.h │ │ │ │ └── sampleapplication.c │ │ ├── devicedata.h.in │ │ ├── generic_networkhandler.c │ │ ├── generic_networkhandler.h │ │ ├── networkhandler.h │ │ ├── nvdata │ │ │ ├── CMakeLists.txt │ │ │ ├── conffile.c │ │ │ ├── conffile.h │ │ │ ├── nvdata.c │ │ │ ├── nvdata.h │ │ │ ├── nvqos.c │ │ │ ├── nvqos.h │ │ │ ├── nvtcpip.c │ │ │ └── nvtcpip.h │ │ ├── opener_error.h │ │ ├── socket_timer.c │ │ └── socket_timer.h │ ├── trace.h │ ├── typedefs.h │ └── utils │ │ ├── CMakeLists.txt │ │ ├── doublylinkedlist.c │ │ ├── doublylinkedlist.h │ │ ├── enipmessage.c │ │ ├── enipmessage.h │ │ ├── random.c │ │ ├── random.h │ │ ├── xorshiftrandom.c │ │ └── xorshiftrandom.h └── tests │ ├── CMakeLists.txt │ ├── OpENerTests.cpp │ ├── OpENerTests.h │ ├── callback_mock.cpp │ ├── check_assert.h │ ├── cip │ ├── CMakeLists.txt │ ├── cipcommontests.cpp │ ├── cipconnectionmanagertest.cpp │ ├── cipconnectionobjecttest.cpp │ ├── cipelectronickeyformattest.cpp │ ├── cipelectronickeytest.cpp │ ├── cipepathtest.cpp │ └── cipstringtests.cpp │ ├── enet_encap │ ├── CMakeLists.txt │ ├── encaptest.cpp │ └── endianconvtest.cpp │ ├── ports │ ├── CMakeLists.txt │ └── socket_timer_tests.cpp │ ├── test_assert.h │ └── utils │ ├── CMakeLists.txt │ ├── doublylinkedlistTests.cpp │ ├── randomTests.cpp │ └── xorshiftrandomtests.cpp ├── travis_scripts ├── compileGcovResults.sh ├── generateDocumentationAndDeploy.sh ├── installCppUTestDependency.sh ├── linuxAfterSuccessScript.sh ├── linuxBeforeScript.sh ├── linuxScript.sh ├── windowsMinGWScript.sh └── windowsScript.sh └── uncrustify.cfg /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | ConstructorInitializerIndentWidth: 4 6 | AlignEscapedNewlinesLeft: true 7 | AlignTrailingComments: true 8 | AllowAllParametersOfDeclarationOnNextLine: true 9 | AllowShortBlocksOnASingleLine: false 10 | AllowShortIfStatementsOnASingleLine: true 11 | AllowShortLoopsOnASingleLine: true 12 | AllowShortFunctionsOnASingleLine: All 13 | AlwaysBreakTemplateDeclarations: true 14 | AlwaysBreakBeforeMultilineStrings: true 15 | BreakBeforeBinaryOperators: false 16 | BreakBeforeTernaryOperators: true 17 | BreakConstructorInitializersBeforeComma: false 18 | BinPackParameters: true 19 | ColumnLimit: 80 20 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 21 | DerivePointerAlignment: true 22 | ExperimentalAutoDetectBinPacking: false 23 | IndentCaseLabels: true 24 | IndentWrappedFunctionNames: false 25 | IndentFunctionDeclarationAfterType: false 26 | MaxEmptyLinesToKeep: 1 27 | KeepEmptyLinesAtTheStartOfBlocks: false 28 | NamespaceIndentation: None 29 | ObjCSpaceAfterProperty: false 30 | ObjCSpaceBeforeProtocolList: false 31 | PenaltyBreakBeforeFirstCallParameter: 1 32 | PenaltyBreakComment: 300 33 | PenaltyBreakString: 1000 34 | PenaltyBreakFirstLessLess: 120 35 | PenaltyExcessCharacter: 1000000 36 | PenaltyReturnTypeOnItsOwnLine: 200 37 | PointerAlignment: Left 38 | SpacesBeforeTrailingComments: 2 39 | Cpp11BracedListStyle: true 40 | Standard: Auto 41 | IndentWidth: 2 42 | TabWidth: 8 43 | UseTab: Never 44 | BreakBeforeBraces: Attach 45 | SpacesInParentheses: false 46 | SpacesInAngles: false 47 | SpaceInEmptyParentheses: false 48 | SpacesInCStyleCastParentheses: false 49 | SpacesInContainerLiterals: true 50 | SpaceBeforeAssignmentOperators: true 51 | ContinuationIndentWidth: 4 52 | CommentPragmas: '^ IWYU pragma:' 53 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 54 | SpaceBeforeParens: ControlStatements 55 | DisableFormat: false 56 | ... 57 | 58 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Code formatter settings 2 | *.c filter=uncrustify 3 | *.cpp filter=uncrustify 4 | *.h filter=uncrustify 5 | *.hpp filter=uncrustify 6 | 7 | # Set the default behavior, in case people don't have core.autocrlf set. 8 | * text=auto 9 | 10 | # Explicitly declare text files you want to always be normalized and converted 11 | # to native line endings on checkout. 12 | *.c text 13 | *.h text 14 | 15 | # Declare files that will always have CRLF line endings on checkout. 16 | *.sln text eol=crlf 17 | *.stc text eol=crlf 18 | *.eds text eol=crlf 19 | 20 | # Denote all files that are truly binary and should not be modified. 21 | *.png binary 22 | *.jpg binary 23 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | -------------------------------------------------------------------------------- /.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 | BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed 13 | 14 | jobs: 15 | build: 16 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 17 | # You can convert this to a matrix build if you need cross-platform coverage. 18 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 19 | name: Build 20 | runs-on: ubuntu-latest 21 | env: 22 | BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed 23 | 24 | steps: 25 | - uses: actions/checkout@v4 26 | with: 27 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 28 | 29 | - name: Set up Python 3.8 for gcovr 30 | uses: actions/setup-python@v5 31 | with: 32 | python-version: 3.8 33 | 34 | - name: install gcovr 5.0 35 | run: | 36 | pip install gcovr==5.0 # 5.1 is not supported 37 | 38 | - name: Install libcap, lcov, and CppUTest 39 | run: sudo apt-get install -y libcap-dev lcov cpputest 40 | 41 | - name: Install sonar-scanner and build-wrapper 42 | uses: SonarSource/sonarcloud-github-c-cpp@v3 43 | 44 | - name: Configure CMake 45 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 46 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 47 | run: cmake -S ${{github.workspace}}/source -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DOpENer_PLATFORM:STRING="POSIX" -DBUILD_SHARED_LIBS:BOOL=OFF -DOpENer_TRACES:BOOL=OFF -DOpENer_TESTS:BOOL=ON -DCPPUTEST_HOME:PATH=/usr 48 | 49 | - name: Build 50 | # Build your program with the given configuration 51 | run: | 52 | build-wrapper-linux-x86-64 --out-dir ${{ env.BUILD_WRAPPER_OUT_DIR }} cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} 53 | 54 | - name: Test 55 | working-directory: ${{github.workspace}}/build 56 | # Execute tests defined by the CMake configuration. 57 | # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail 58 | run: ctest -C ${{env.BUILD_TYPE}} 59 | 60 | - name: Collect coverage into one XML report 61 | run: | 62 | gcovr --sonarqube > coverage.xml 63 | 64 | - name: Run sonar-scanner 65 | env: 66 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 67 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Put the name of your token here 68 | run: | 69 | sonar-scanner \ 70 | --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" \ 71 | --define sonar.coverageReportPaths=coverage.xml 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | contrib/ 3 | source/src/cip_objects/ 4 | !source/src/cip_objects/CMakeLists.txt 5 | .project 6 | .cproject 7 | .settings/ 8 | *.*~ 9 | *~ 10 | CMakeCache.txt 11 | .idea/ 12 | *.a 13 | *.cmake 14 | Makefile 15 | CMakeFiles 16 | devicedata.h 17 | source/src/ports/POSIX/OpENer 18 | source/cip_objects/ 19 | 20 | .vscode/ 21 | 22 | 23 | build/ 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | branches: 3 | except: 4 | - gh-pages 5 | env: 6 | global: 7 | - GH_REPO_NAME: OpENer 8 | - DOXYFILE: opener.doxyfile 9 | - GH_REPO_REF: github.com/EIPStackGroup/OpENer.git 10 | - secure: h1vuX5cGZd5W7f5TitD+EamJIsvG2qq8aBpO9MUGIOj3bShwTaR0S0qbcpCyltXiZ9DJklLc7kP5kB0XtX1o6vZMelQsqBjiHQK5yFW0vHmFAg1sLMpVBbsAN0lMWgeGJEmyRstA1KYBixwExtc5GpcgMBvS/mnJQ10zboZNcRU= 11 | - secure: CN1E5LJZwa7PLJOcst1MNb1c5Nx1rM9ifrc4llevRyTRyHxJ2S15mVQdnCvMcdPFK+ranBdsl1WcIxP3BQtI4zDwj0UWjj44EHz67VTp1o3zuaCa0fExYUwbe0D8uGRP4XbX+B4+HQWneGbabOLAZcS3Gc/pUpC3WEJO2pO2BHg= 12 | addons: 13 | apt: 14 | packages: 15 | - libcap-dev 16 | - lcov 17 | - doxygen 18 | - doxygen-doc 19 | - doxygen-latex 20 | - doxygen-gui 21 | - graphviz 22 | sonarcloud: 23 | organization: eipstackgroup 24 | token: $SONAR_TOKEN 25 | 26 | jobs: 27 | - name: "GCC on Linux" 28 | os: linux 29 | dist: xenial 30 | env: CMAKE_URL="https://cmake.org/files/v3.7/cmake-3.7.2-Linux-x86_64.tar.gz" 31 | 32 | install: 33 | - git fetch --unshallow --tags 34 | - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps" 35 | - mkdir -p ${DEPS_DIR} && cd ${DEPS_DIR} 36 | - | 37 | mkdir cmake && travis_retry wget --no-check-certificate --quiet -O - ${CMAKE_URL} | tar --strip-components=1 -xz -C cmake 38 | export PATH=${DEPS_DIR}/cmake/bin:${PATH} 39 | cmake --version 40 | before_script: 41 | - $TRAVIS_BUILD_DIR/travis_scripts/linuxBeforeScript.sh 42 | script: 43 | - $TRAVIS_BUILD_DIR/travis_scripts/linuxScript.sh 44 | after_success: 45 | - $TRAVIS_BUILD_DIR/travis_scripts/linuxAfterSuccessScript.sh 46 | - name: "MSVC on Windows" 47 | os: windows 48 | script: 49 | - $TRAVIS_BUILD_DIR/travis_scripts/windowsScript.sh 50 | - name: "MinGW on Windows" 51 | os: windows 52 | env: 53 | - CC="/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin/x86_64-w64-mingw32-gcc" 54 | - CXX="/c/ProgramData/chocolatey/lib/mingw/tools/install/mingw64/bin/x86_64-w64-mingw32-g++" 55 | script: 56 | - $TRAVIS_BUILD_DIR/travis_scripts/windowsMinGWScript.sh -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Kurt Schweiger 2 | Rene Smodic 3 | Alois Zoitl 4 | Jonathan Engdahl -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | This file serves as brainstorming buffer for ideas to improve and enhance OpENer: 2 | 3 | * New Features: 4 | - Implementation of common CIP-objects 5 | - CIP-Sync 6 | - CIP-Motion 7 | 8 | 9 | * Improvements and Optimizations 10 | - Remove the need for the response buffer in the explicit message handling 11 | (zero copy stack) 12 | - Rework I/O message handling: 13 | - own buffers for each connection that are preconfigured and only runtime 14 | data needs to be changed. 15 | - Rework socket handling 16 | - Use only one single UDP socket for all I/O messages 17 | - may not be possible for point to point consuming connections where 18 | the scanner gives a different port number 19 | - use the registered port number 2222 for all I/O communication 20 | 21 | -------------------------------------------------------------------------------- /bin/mingw/setup_mingw.bat: -------------------------------------------------------------------------------- 1 | cmake -G "MinGW Makefiles" -DCMAKE_INSTALL_PREFIX:PATH="C:/Program Files (x86)/OpENer" -DOpENer_PLATFORM:STRING="MINGW" ../../source -------------------------------------------------------------------------------- /bin/posix/setup_posix.sh: -------------------------------------------------------------------------------- 1 | cmake -DCMAKE_C_COMPILER=gcc -DOpENer_PLATFORM:STRING="POSIX" -DCMAKE_BUILD_TYPE:STRING="" -DBUILD_SHARED_LIBS:BOOL=OFF ../../source 2 | 3 | -------------------------------------------------------------------------------- /bin/posix/setup_posix_fuzz_afl.sh: -------------------------------------------------------------------------------- 1 | cmake -DCMAKE_C_COMPILER=afl-clang-fast -DUSE_FUZZ_AFL=ON -DOpENer_PLATFORM:STRING="POSIX" -DCMAKE_BUILD_TYPE:STRING="" -DBUILD_SHARED_LIBS:BOOL=OFF ../../source 2 | 3 | -------------------------------------------------------------------------------- /bin/win32/setup_windows.bat: -------------------------------------------------------------------------------- 1 | cmake -DCMAKE_INSTALL_PREFIX:PATH="C:/Program Files (x86)/OpENer" -DOpENer_PLATFORM:STRING="WIN32" ../../source 2 | -------------------------------------------------------------------------------- /fuzz/imgs/fuzz.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIPStackGroup/OpENer/70d6947c6ef97c5c8ccbc908b94651432e18cf9c/fuzz/imgs/fuzz.png -------------------------------------------------------------------------------- /fuzz/inputs/cip_req_forward_open: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIPStackGroup/OpENer/70d6947c6ef97c5c8ccbc908b94651432e18cf9c/fuzz/inputs/cip_req_forward_open -------------------------------------------------------------------------------- /fuzz/inputs/cip_req_list_identity_cip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIPStackGroup/OpENer/70d6947c6ef97c5c8ccbc908b94651432e18cf9c/fuzz/inputs/cip_req_list_identity_cip -------------------------------------------------------------------------------- /fuzz/inputs/enip_req_list_identity: -------------------------------------------------------------------------------- 1 | c -------------------------------------------------------------------------------- /fuzz/inputs/enip_req_register_session: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIPStackGroup/OpENer/70d6947c6ef97c5c8ccbc908b94651432e18cf9c/fuzz/inputs/enip_req_register_session -------------------------------------------------------------------------------- /fuzz/scripts/send_testcase.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import socket 3 | import struct 4 | 5 | if len(sys.argv) != 3: 6 | print("python {} IP TESTCASE_PATH".format(sys.argv[0])) 7 | sys.exit(1) 8 | 9 | HOST_IP = sys.argv[1] 10 | HOST_PORT = 44818 11 | TESTCASE_PATH = sys.argv[2] 12 | 13 | ENIP_SESSION_CONTEXT = b"\x92\x83J\x0b=\x9e\x0cW" 14 | ENIP_INIT_SESSION_PACKET = b"e\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00" + ENIP_SESSION_CONTEXT + b"\x00\x00\x00\x00\x01\x00\x00\x00" 15 | 16 | 17 | print("[-] Connecting to {}:{}".format(HOST_IP, HOST_PORT)) 18 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 19 | s.connect((HOST_IP, HOST_PORT)) 20 | 21 | print("[-] Init ENIP session") 22 | s.sendall(ENIP_INIT_SESSION_PACKET) 23 | enip_session = s.recv(1024) 24 | session_handle = enip_session[4:8] 25 | print("[-] Got ENIP Session Handle: {}".format(struct.unpack(" 2 | 3 |
4 | 5 | 6 | ../../Inc 7 | ../../Src 8 | ../../Drivers/CMSIS/Device/ST/STM32F7xx/Include 9 | ../../Drivers/STM32F7xx_HAL_Driver/Inc 10 | ../../Drivers/BSP/STM32746G-Discovery 11 | ../../Drivers/BSP/Components/Common 12 | ../../Drivers/BSP/Components 13 | ../../Middlewares/Third_Party/LwIP/src/include 14 | ../../Middlewares/Third_Party/LwIP/system 15 | ../../Middlewares/Third_Party/FreeRTOS/Source/portable/GCC/ARM_CM7/r0p1 16 | ../../Middlewares/Third_Party/FreeRTOS/Source 17 | ../../Middlewares/Third_Party/FreeRTOS/Source/CMSIS_RTOS 18 | ../../Middlewares/Third_Party/FreeRTOS/Source/include 19 | ../../Utilities 20 | ../../Utilities/Log 21 | ../../Drivers/CMSIS/Include 22 | ../../Middlewares/Third_Party/OpENer 23 | ../../Middlewares/Third_Party/OpENer/cip 24 | ../../Middlewares/Third_Party/OpENer/enet_encap 25 | ../../Middlewares/Third_Party/OpENer/ports 26 | ../../Middlewares/Third_Party/OpENer/ports/STM32 27 | ../../Middlewares/Third_Party/OpENer/ports/STM32/sample_application 28 | ../../Middlewares/Third_Party/OpENer/utils 29 | 30 | 31 |
32 |
33 | 34 | 35 | 36 | USE_HAL_DRIVER 37 | 38 | 39 | 40 | STM32F746xx 41 | 42 | 43 | 44 | USE_STM32746G_DISCOVERY 45 | 46 | 47 | 48 | RESTRICT 49 | __restrict 50 | 51 | 52 | STM32 53 | 54 | 55 | 56 | _POSIX_C_SOURCE 57 | 200112L 58 | 59 | 60 | _GNU_SOURCE 61 | 62 | 63 | 64 | OPENER_TRACE_LEVEL 65 | 15 66 | 67 | 68 | OPENER_CONSUMED_DATA_HAS_RUN_IDLE_HEADER 69 | 1 70 | 71 | 72 | OPENER_WITH_TRACES 73 | 1 74 | 75 | 76 | PC_OPENER_ETHERNET_BUFFER_SIZE 77 | 512 78 | 79 | 80 | REDIRECT_PRINTF_TO_SWV_ITM 81 | 1 82 | 83 | 84 | 85 |
86 |
87 | -------------------------------------------------------------------------------- /source/doc/STM32/OpENer STM32 Port.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIPStackGroup/OpENer/70d6947c6ef97c5c8ccbc908b94651432e18cf9c/source/doc/STM32/OpENer STM32 Port.pdf -------------------------------------------------------------------------------- /source/doc/coding_rules/opener_coding_rules.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EIPStackGroup/OpENer/70d6947c6ef97c5c8ccbc908b94651432e18cf9c/source/doc/coding_rules/opener_coding_rules.pdf -------------------------------------------------------------------------------- /source/doc/opener.bib: -------------------------------------------------------------------------------- 1 | @book{CipVol1, 2 | series = "The CIP Networks Library", 3 | title = "Common {I}ndustrial {P}rotocol", 4 | volume = "1", 5 | edition = "3.3", 6 | month = "November", 7 | year = "2007" 8 | } 9 | 10 | 11 | @book{CipVol2, 12 | series = "The CIP Networks Library", 13 | title = "Ether{N}et/{IP} {A}daptation of {CIP}", 14 | volume = "2", 15 | edition = "1.4", 16 | month = "November", 17 | year = "2007" 18 | } 19 | -------------------------------------------------------------------------------- /source/src/.gitignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | -------------------------------------------------------------------------------- /source/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Add subdirectories # 3 | ####################################### 4 | add_subdirectory( enet_encap ) 5 | add_subdirectory( cip ) 6 | add_subdirectory( cip_objects ) 7 | add_subdirectory( ports ) 8 | add_subdirectory( utils ) 9 | 10 | ####################################### 11 | # Add common includes # 12 | ####################################### 13 | opener_common_includes() 14 | 15 | ####################################### 16 | # Add platform specific things # 17 | ####################################### 18 | 19 | opener_platform_support( "INCLUDES" ) 20 | 21 | if( OPENER_INSTALL_AS_LIB ) 22 | install(DIRECTORY . 23 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 24 | FILES_MATCHING PATTERN "*.h" 25 | ) 26 | endif() 27 | -------------------------------------------------------------------------------- /source/src/cip/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # CIP library # 3 | ####################################### 4 | 5 | ####################################### 6 | # Add common includes # 7 | ####################################### 8 | opener_common_includes() 9 | 10 | ####################################### 11 | # Add platform-specific includes # 12 | ####################################### 13 | opener_platform_support("INCLUDES") 14 | 15 | set( CIP_SRC appcontype.c cipassembly.c cipclass3connection.c cipcommon.c cipconnectionobject.c cipconnectionmanager.c cipdlr.c ciperror.h cipethernetlink.c cipidentity.c cipioconnection.c cipmessagerouter.c ciptcpipinterface.c ciptypes.h cipepath.c cipelectronickey.c cipstring.c cipstringi.c cipqos.c ciptypes.c) 16 | 17 | add_library( CIP ${CIP_SRC} ) 18 | 19 | if( OPENER_INSTALL_AS_LIB ) 20 | target_link_libraries(CIP ENET_ENCAP) 21 | install(TARGETS CIP 22 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 23 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 24 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} 25 | ) 26 | install(DIRECTORY ${CIP_SRC_DIR} 27 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 28 | FILES_MATCHING PATTERN "*.h" 29 | ) 30 | endif() 31 | -------------------------------------------------------------------------------- /source/src/cip/appcontype.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_APPCONTYPE_H_ 7 | #define OPENER_APPCONTYPE_H_ 8 | 9 | #include "cipconnectionmanager.h" 10 | 11 | void InitializeIoConnectionData(void); 12 | 13 | /** @brief check if for the given connection data received in a forward_open request 14 | * a suitable connection is available. 15 | * 16 | * If a suitable connection is found the connection data is transfered the 17 | * application connection type is set (i.e., EConnType). 18 | * @param connection_object connection data to be used 19 | * @param extended_error pointer to the extended_error variable, if an error occurred this value has the according 20 | * error code for the response 21 | * @return 22 | * - on success: A pointer to the connection object already containing the connection 23 | * data given in connection_object. 24 | * - on error: NULL 25 | */ 26 | CipConnectionObject *GetIoConnectionForConnectionData( 27 | CipConnectionObject *const RESTRICT connection_object, 28 | EipUint16 *const extended_error); 29 | 30 | /** @brief Check if there exists already an exclusive owner or listen only connection 31 | * which produces the input assembly. 32 | * 33 | * @param multicast_only Look only for multi-cast connections 34 | * @param input_point the Input point to be produced 35 | * @return a pointer to the found connection; NULL if nothing found 36 | */ 37 | CipConnectionObject *GetExistingProducerIoConnection( 38 | const bool multicast_only, 39 | const EipUint32 input_point); 40 | 41 | /** @brief check if there exists an producing multicast exclusive owner or 42 | * listen only connection that should produce the same input but is not in charge 43 | * of the connection. 44 | * 45 | * @param input_point the produced input 46 | * @return if a connection could be found the pointer to this connection 47 | * otherwise NULL. 48 | */ 49 | CipConnectionObject *GetNextNonControlMasterConnection( 50 | const EipUint32 input_point); 51 | 52 | /** @brief Close all connection producing the same input and have the same type 53 | * (i.e., listen only or input only). 54 | * 55 | * @param input_point the input point 56 | * @param instance_type the connection application type 57 | */ 58 | void CloseAllConnectionsForInputWithSameType(const EipUint32 input_point, 59 | const ConnectionObjectInstanceType instance_type); 60 | 61 | /**@ brief close all open connections. 62 | * 63 | * For I/O connections the sockets will be freed. The sockets for explicit 64 | * connections are handled by the encapsulation layer, and freed there. 65 | */ 66 | void CloseAllConnections(void); 67 | 68 | /** @brief Check if there is an established connection that uses the same 69 | * config point. 70 | * 71 | * @param config_point The configuration point 72 | * @return true if connection was found, otherwise false 73 | */ 74 | bool ConnectionWithSameConfigPointExists(const EipUint32 config_point); 75 | 76 | #endif /* OPENER_APPCONTYPE_H_ */ 77 | -------------------------------------------------------------------------------- /source/src/cip/cipassembly.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_CIPASSEMBLY_H_ 7 | #define OPENER_CIPASSEMBLY_H_ 8 | 9 | #include "typedefs.h" 10 | #include "ciptypes.h" 11 | 12 | /** @brief Assembly class code */ 13 | static const CipUint kCipAssemblyClassCode = 0x04U; 14 | 15 | 16 | /** @brief Assembly object instance attribute IDs. 17 | * 18 | * Reference: 19 | * \cite CipVol1, Table 5-5.4 20 | */ 21 | typedef enum { 22 | kAssemblyObjectInstanceAttributeIdData = 3 23 | } AssemblyObjectInstanceAttributeId; 24 | 25 | 26 | /* public functions */ 27 | 28 | /** @brief Setup the Assembly object 29 | * 30 | * Creates the Assembly Class with zero instances and sets up all services. 31 | * 32 | * @return Returns kEipStatusOk if assembly object was successfully created, otherwise kEipStatusError 33 | */ 34 | EipStatus CipAssemblyInitialize(void); 35 | 36 | /** @brief clean up the data allocated in the assembly object instances 37 | * 38 | * Assembly object instances allocate per instance data to store attribute 3. 39 | * This will be freed here. The assembly object data given by the application 40 | * is not freed neither the assembly object instances. These are handled in the 41 | * main shutdown function. 42 | */ 43 | void ShutdownAssemblies(void); 44 | 45 | /** @brief notify an Assembly object that data has been received for it. 46 | * 47 | * The data will be copied into the assembly objects attribute 3 and 48 | * the application will be informed with the AfterAssemblyDataReceived function. 49 | * 50 | * @param instance the assembly object instance for which the data was received 51 | * @param data pointer to the data received 52 | * @param data_length number of bytes received 53 | * @return 54 | * - kEipStatusOk the received data was okay 55 | * - kEipStatusError the received data was wrong 56 | */ 57 | EipStatus NotifyAssemblyConnectedDataReceived(CipInstance *const instance, 58 | const EipUint8 *const data, 59 | const size_t data_length); 60 | 61 | #endif /* OPENER_CIPASSEMBLY_H_ */ 62 | -------------------------------------------------------------------------------- /source/src/cip/cipclass3connection.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2011, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | 9 | #include "cipclass3connection.h" 10 | 11 | #include "encap.h" 12 | 13 | /**** Global variables ****/ 14 | extern CipConnectionObject explicit_connection_object_pool[ 15 | OPENER_CIP_NUM_EXPLICIT_CONNS]; 16 | 17 | CipConnectionObject *GetFreeExplicitConnection(void); 18 | 19 | void Class3ConnectionTimeoutHandler(CipConnectionObject *connection_object) { 20 | CheckForTimedOutConnectionsAndCloseTCPConnections(connection_object, 21 | CloseSessionBySessionHandle); 22 | CloseConnection(connection_object); 23 | } 24 | 25 | /**** Implementation ****/ 26 | CipError EstablishClass3Connection( 27 | CipConnectionObject *RESTRICT const connection_object, 28 | EipUint16 *const extended_error) { 29 | CipError cip_error = kCipErrorSuccess; 30 | 31 | CipConnectionObject *explicit_connection = GetFreeExplicitConnection(); 32 | 33 | if (NULL == explicit_connection) { 34 | cip_error = kCipErrorConnectionFailure; 35 | *extended_error = 36 | kConnectionManagerExtendedStatusCodeErrorNoMoreConnectionsAvailable; 37 | } else { 38 | ConnectionObjectDeepCopy(explicit_connection, connection_object); 39 | 40 | ConnectionObjectGeneralConfiguration(explicit_connection); 41 | 42 | ConnectionObjectSetInstanceType(explicit_connection, 43 | kConnectionObjectInstanceTypeExplicitMessaging); 44 | 45 | /* set the connection call backs */ 46 | explicit_connection->connection_close_function = 47 | CloseConnection; 48 | /* explicit connection have to be closed on time out*/ 49 | explicit_connection->connection_timeout_function = 50 | Class3ConnectionTimeoutHandler; 51 | 52 | AddNewActiveConnection(explicit_connection); 53 | } 54 | return cip_error; 55 | } 56 | 57 | /** @brief Searches and returns a free explicit connection slot 58 | * 59 | * @return Free explicit connection slot, or NULL if no slot is free 60 | */ 61 | CipConnectionObject *GetFreeExplicitConnection(void) { 62 | for (size_t i = 0; i < OPENER_CIP_NUM_EXPLICIT_CONNS; ++i) { 63 | if (ConnectionObjectGetState(&(explicit_connection_object_pool[i]) ) == 64 | kConnectionObjectStateNonExistent) { 65 | return &(explicit_connection_object_pool[i]); 66 | } 67 | } 68 | return NULL; 69 | } 70 | 71 | void InitializeClass3ConnectionData(void) { 72 | memset( explicit_connection_object_pool, 0, 73 | OPENER_CIP_NUM_EXPLICIT_CONNS * sizeof(CipConnectionObject) ); 74 | } 75 | 76 | EipStatus CipClass3ConnectionObjectStateEstablishedHandler( 77 | CipConnectionObject *RESTRICT const connection_object, 78 | ConnectionObjectState new_state) { 79 | switch(new_state) { 80 | case kConnectionObjectStateNonExistent: 81 | ConnectionObjectInitializeEmpty(connection_object); 82 | ConnectionObjectSetState(connection_object, new_state); 83 | return kEipStatusOk; 84 | default: return kEipStatusError; 85 | } 86 | } 87 | 88 | EipStatus CipClass3ConnectionObjectStateNonExistentHandler( 89 | CipConnectionObject *RESTRICT const connection_object, 90 | ConnectionObjectState new_state) { 91 | switch(new_state) { 92 | case kConnectionObjectStateEstablished: 93 | ConnectionObjectSetState(connection_object, new_state); 94 | return kEipStatusOk; 95 | default: return kEipStatusError; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /source/src/cip/cipclass3connection.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2011, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef OPENER_CIPCLASS3CONNECTION_H_ 8 | #define OPENER_CIPCLASS3CONNECTION_H_ 9 | 10 | /** @file cipclass3connection.h 11 | * @brief CIP Class 3 connection 12 | * * Explicit Connection Object State Transition Diagram 13 | * ---------------------------------------------- 14 | * @dot 15 | * digraph ExplicitConnectionObjectStateTransition { 16 | * A[label="Any State"] 17 | * N[label="Non-existent"] 18 | * D[label="Deferred Delete"] 19 | * E[label="Established"] 20 | * 21 | * A->N [label="Delete"] 22 | * N->E [label="Open Explicit Messaging Connection Response"] 23 | * E->N [label="Delete or inactivity time-out"] 24 | * E->E [label="Get/Set/Apply Attribute, Reset, Message Produced/Consumed"] 25 | * E->D [label="Inactivity time-out and deferred delete set"] 26 | * D->N [label="Delete"] 27 | * } 28 | * @enddot 29 | */ 30 | 31 | #include "opener_api.h" 32 | #include "cipconnectionmanager.h" 33 | #include "cipconnectionobject.h" 34 | 35 | typedef EipStatus (*CipConnectionStateHandler)(CipConnectionObject *RESTRICT 36 | const connection_object, 37 | ConnectionObjectState new_state); 38 | 39 | EipStatus CipClass3ConnectionObjectStateEstablishedHandler( 40 | CipConnectionObject *RESTRICT const connection_object, 41 | ConnectionObjectState new_state); 42 | 43 | /** @brief Check if Class3 connection is available and if yes setup all data. 44 | * 45 | * This function can be called after all data has been parsed from the forward open request 46 | * @param connection_object pointer to the connection object structure holding the parsed data from the forward open request 47 | * @param extended_error the extended error code in case an error happened 48 | * @return general status on the establishment 49 | * - kEipStatusOk ... on success 50 | * - On an error the general status code to be put into the response 51 | */ 52 | CipError EstablishClass3Connection( 53 | CipConnectionObject *RESTRICT const connection_object, 54 | EipUint16 *const extended_error); 55 | 56 | /** @brief Initializes the explicit connections mechanism 57 | * 58 | * Prepares the available explicit connection slots for use at the start of the OpENer 59 | */ 60 | void InitializeClass3ConnectionData(void); 61 | 62 | #endif /* OPENER_CIPCLASS3CONNECTION_H_ */ 63 | -------------------------------------------------------------------------------- /source/src/cip/cipdlr.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | *****************************************************************************/ 6 | 7 | /** @file 8 | * @brief Declare public interface of the DLR object 9 | * 10 | * @author Stefan Maetje 11 | * 12 | */ 13 | 14 | #ifndef OPENER_CIPDLR_H_ 15 | #define OPENER_CIPDLR_H_ 16 | 17 | #include "typedefs.h" 18 | #include "ciptypes.h" 19 | 20 | /** @brief DLR object class code */ 21 | static const CipUint kCipDlrClassCode = 0x47U; 22 | 23 | /* ******************************************************************** 24 | * Type declarations 25 | */ 26 | /** @brief Provide bit masks for the Capability Flags attribute (#12) 27 | * 28 | * The @ref kDlrCapAnnounceBased and @ref kDlrCapBeaconBased capability 29 | * flags are mutually exclusive but one of it must be set. 30 | */ 31 | typedef enum { 32 | /** Device is an announce based ring node. */ 33 | kDlrCapAnnounceBased = 0x01, 34 | /** Device is a beacon based ring node. */ 35 | kDlrCapBeaconBased = 0x02, 36 | /** The device is capable of providing the supervisor function. */ 37 | kDlrCapSupervisor = 0x20, 38 | /** The device is capable of providing the redundant gateway function. */ 39 | kDlrCapRedundantGateway = 0x40, 40 | /** The device is capable of supporting the Flush_Tables frame. */ 41 | kDlrCapFlushTableFrame = 0x80, 42 | } CipDlrCapabilityFlags; 43 | 44 | 45 | /** @brief Node address information for a DLR node 46 | * 47 | * This is the node address information that uniquely identifies a 48 | * participant of the DLR protocol. 49 | */ 50 | typedef struct { 51 | CipUdint device_ip; /**< IP address of a participating DLR node */ 52 | CipUsint device_mac[6]; /**< MAC address of a participating DLR node */ 53 | } CipNodeAddress; 54 | 55 | /** @brief Type declaration for the DLR object 56 | * 57 | * This is the type declaration for the DLR object. It contains only the 58 | * attributes needed for a non supervisor and non redundant gateway 59 | * ring participant. 60 | */ 61 | typedef struct { 62 | CipUsint network_topology; /**< Attribute #1: */ 63 | CipUsint network_status; /**< Attribute #2: */ 64 | CipNodeAddress active_supervisor_address; /**< Attribute #10: */ 65 | CipDword capability_flags; /**< Attribute #12: */ 66 | } CipDlrObject; 67 | 68 | 69 | /* ******************************************************************** 70 | * global public variables 71 | */ 72 | extern CipDlrObject g_dlr; /**< declaration of DLR object instance 1 data */ 73 | 74 | 75 | /* ******************************************************************** 76 | * public functions 77 | */ 78 | /** @brief Initializing the data structures of the DLR object 79 | * 80 | * @return kEipStatusOk on success, otherwise kEipStatusError 81 | */ 82 | EipStatus CipDlrInit(void); 83 | 84 | #endif /* of OPENER_CIPDLR_H_ */ 85 | -------------------------------------------------------------------------------- /source/src/cip/cipelectronickey.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | 9 | #include "cipelectronickey.h" 10 | 11 | void ElectronicKeySetKeyFormat(CipElectronicKey *const electronic_key, 12 | const CipUsint key_format) { 13 | electronic_key->key_format = key_format; 14 | } 15 | 16 | CipUint ElectronicKeyGetKeyFormat(const CipElectronicKey *const electronic_key) 17 | { 18 | return electronic_key->key_format; 19 | } 20 | 21 | void ElectronicKeySetKeyData(CipElectronicKey *const electronic_key, 22 | void *key_data) { 23 | electronic_key->key_data = key_data; 24 | } 25 | 26 | void *ElectronicKeyGetKeyData(const CipElectronicKey *const electronic_key) { 27 | return electronic_key->key_data; 28 | } 29 | 30 | typedef struct electronic_key_format_4 { 31 | CipUint vendor_id; 32 | CipUint device_type; 33 | CipUint product_code; 34 | CipByte major_revision_compatibility; 35 | CipUsint minor_revision; 36 | } ElectronicKeyFormat4; 37 | 38 | const size_t kElectronicKeyFormat4Size = sizeof(ElectronicKeyFormat4); 39 | 40 | ElectronicKeyFormat4 *ElectronicKeyFormat4New() { 41 | return (ElectronicKeyFormat4 *)calloc( 1, sizeof(ElectronicKeyFormat4) ); 42 | } 43 | 44 | void ElectronicKeyFormat4Delete(ElectronicKeyFormat4 **electronic_key) { 45 | free(*electronic_key); 46 | *electronic_key = NULL; 47 | } 48 | 49 | void ElectronicKeyFormat4SetVendorId(ElectronicKeyFormat4 *const electronic_key, 50 | const CipUint vendor_id) { 51 | electronic_key->vendor_id = vendor_id; 52 | } 53 | 54 | CipUint ElectronicKeyFormat4GetVendorId( 55 | const ElectronicKeyFormat4 *const electronic_key) { 56 | return electronic_key->vendor_id; 57 | } 58 | 59 | void ElectronicKeyFormat4SetDeviceType( 60 | ElectronicKeyFormat4 *const electronic_key, 61 | const CipUint device_type) { 62 | electronic_key->device_type = device_type; 63 | } 64 | 65 | CipUint ElectronicKeyFormat4GetDeviceType( 66 | const ElectronicKeyFormat4 *const electronic_key) { 67 | return electronic_key->device_type; 68 | } 69 | 70 | void ElectronicKeyFormat4SetProductCode( 71 | ElectronicKeyFormat4 *const electronic_key, 72 | const CipUint product_code) { 73 | electronic_key->product_code = product_code; 74 | } 75 | 76 | CipUint ElectronicKeyFormat4GetProductCode( 77 | const ElectronicKeyFormat4 *const electronic_key) { 78 | return electronic_key->product_code; 79 | } 80 | 81 | void ElectronicKeyFormat4SetMajorRevisionCompatibility( 82 | ElectronicKeyFormat4 *const electronic_key, 83 | const CipByte major_revision_compatibility) { 84 | electronic_key->major_revision_compatibility = major_revision_compatibility; 85 | } 86 | 87 | CipByte ElectronicKeyFormat4GetMajorRevision( 88 | const ElectronicKeyFormat4 *const electronic_key) { 89 | const CipByte kMajorRevisionMask = 0x7F; 90 | return (electronic_key->major_revision_compatibility & kMajorRevisionMask); 91 | } 92 | 93 | bool ElectronicKeyFormat4GetMajorRevisionCompatibility( 94 | const ElectronicKeyFormat4 *const electronic_key) { 95 | const CipByte kCompatibilityMask = 0x80; 96 | if( kCompatibilityMask == 97 | (electronic_key->major_revision_compatibility & kCompatibilityMask) ) { 98 | return true; 99 | } 100 | return false; 101 | } 102 | 103 | void ElectronicKeyFormat4SetMinorRevision( 104 | ElectronicKeyFormat4 *const electronic_key, 105 | const CipUsint minor_revision) { 106 | electronic_key->minor_revision = minor_revision; 107 | } 108 | 109 | CipUsint ElectronicKeyFormat4GetMinorRevision( 110 | const ElectronicKeyFormat4 *const electronic_key) { 111 | return electronic_key->minor_revision; 112 | } 113 | -------------------------------------------------------------------------------- /source/src/cip/cipidentity.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_CIPIDENTITY_H_ 7 | #define OPENER_CIPIDENTITY_H_ 8 | 9 | #include "typedefs.h" 10 | #include "ciptypes.h" 11 | 12 | /** @brief Identity class code */ 13 | static const CipUint kCipIdentityClassCode = 0x01U; 14 | 15 | /** @brief Status of the CIP Identity object */ 16 | typedef enum { 17 | kOwned = 0x0001, /**< Indicates that the device has an owner */ 18 | kConfigured = 0x0004, /**< Indicates that the device is configured to do 19 | something different, than the out-of-the-box default. */ 20 | kMinorRecoverableFault = 0x0100, /**< Indicates that the device detected a 21 | fault with itself, which was thought to be recoverable. The device did not 22 | switch to a faulted state. */ 23 | kMinorUncoverableFault = 0x0200, /**< Indicates that the device detected a 24 | fault with itself, which was thought to be recoverable. The device did not 25 | switch to a faulted state. */ 26 | kMajorRecoverableFault = 0x0400, /**< Indicates that the device detected a 27 | fault with itself,which was thought to be recoverable. The device changed 28 | to the "Major Recoverable Fault" state */ 29 | kMajorUnrecoverableFault = 0x0800 /**< Indicates that the device detected a 30 | fault with itself,which was thought to be recoverable. The device changed 31 | to the "Major Unrecoverable Fault" state */ 32 | } CipIdentityStatus; 33 | 34 | /** @brief Constants for the extended status field in the Status word */ 35 | typedef enum { 36 | kSelftestingUnknown = 0x0000U, 37 | kFirmwareUpdateInProgress = 0x0010U, 38 | kStatusAtLeastOneFaultedIoConnection = 0x0020U, 39 | kNoIoConnectionsEstablished = 0x0030U, 40 | kNonVolatileConfigurationBad = 0x0040U, 41 | kMajorFault = 0x0050U, 42 | kAtLeastOneIoConnectionInRunMode = 0x0060U, 43 | kAtLeastOneIoConnectionEstablishedAllInIdleMode = 0x0070U, 44 | kExtStatusMask = 0x00F0U 45 | } CipIdentityExtendedStatus; 46 | 47 | /** @brief Constants for the state member of the Identity object. */ 48 | typedef enum { 49 | kStateNonExistent = 0U, 50 | kStateSelfTesting = 1U, 51 | kStateStandby = 2U, 52 | kStateOperational = 3U, 53 | kStateMajorRecoverableFault = 4U, 54 | kStateMajorUnrecoverableFault = 5U, 55 | kStateDefault = 255U 56 | } CipIdentityState; 57 | 58 | /** @brief Declaration of the Identity object's structure type 59 | */ 60 | typedef struct { 61 | CipUint vendor_id; /**< Attribute 1: Vendor ID */ 62 | CipUint device_type; /**< Attribute 2: Device Type */ 63 | CipUint product_code; /**< Attribute 3: Product Code */ 64 | CipRevision revision; /**< Attribute 4: Revision / CipUsint Major, CipUsint Minor */ 65 | CipWord status; /**< Attribute 5: Status */ 66 | CipWord ext_status; /**< Attribute 5: last set extended status, needed for Status handling */ 67 | CipUdint serial_number; /**< Attribute 6: Serial Number, has to be set prior to OpENer's network initialization */ 68 | CipShortString product_name; /**< Attribute 7: Product Name */ 69 | CipUsint state; /** Attribute 8: state, this member could control the Module Status LED blink pattern */ 70 | } CipIdentityObject; 71 | 72 | 73 | /* global public variables */ 74 | extern CipIdentityObject g_identity; 75 | 76 | 77 | /* public functions */ 78 | /** @brief CIP Identity object constructor 79 | * 80 | * @returns kEipStatusError if the class could not be created, otherwise kEipStatusOk 81 | */ 82 | EipStatus CipIdentityInit(void); 83 | 84 | void CipIdentitySetStatusFlags(const CipWord status_flags); 85 | void CipIdentityClearStatusFlags(const CipWord status_flags); 86 | void CipIdentitySetExtendedDeviceStatus( 87 | CipIdentityExtendedStatus extended_status); 88 | 89 | #endif /* OPENER_CIPIDENTITY_H_ */ 90 | -------------------------------------------------------------------------------- /source/src/cip/cipioconnection.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2011, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** 8 | * @file cipioconnection.h 9 | * CIP I/O Connection implementation 10 | * ================================= 11 | * 12 | * 13 | * I/O Connection Object State Transition Diagram 14 | * ---------------------------------------------- 15 | * @dot 16 | * digraph IOConnectionObjectStateTransition { 17 | * A[label="Any State"] 18 | * N[label="Non-existent"] 19 | * C[label="Configuring"] 20 | * E[label="Established"] 21 | * W[label="Waiting for Connection ID"] 22 | * T[label="Timed Out"] 23 | * 24 | * A->N [label="Delete"] 25 | * N->C [label="Create"] 26 | * C->C [label="Get/Set/Apply Attribute"] 27 | * C->W [label="Apply Attribute"] 28 | * W->W [label="Get/Set Attribute"] 29 | * C->E [label="Apply Attribute"] 30 | * E->E [label="Get/Set/Apply Attribute, Reset, Message Produced/Consumed"] 31 | * W->E [label="Apply Attribute"] 32 | * E->T [label="Inactivity/Watchdog"] 33 | * T->E [label="Reset"] 34 | * T->N [label="Delete"] 35 | * } 36 | * @enddot 37 | * 38 | */ 39 | 40 | #ifndef OPENER_CIPIOCONNECTION_H_ 41 | #define OPENER_CIPIOCONNECTION_H_ 42 | 43 | #include "opener_api.h" 44 | #include "cipconnectionmanager.h" 45 | #include "cipconnectionobject.h" 46 | 47 | /** @brief Setup all data in order to establish an IO connection 48 | * 49 | * This function can be called after all data has been parsed from the forward open request 50 | * @param connection_object pointer to the connection object structure holding the parsed data from the forward open request 51 | * @param extended_error the extended error code in case an error happened 52 | * @return general status on the establishment 53 | * - EIP_OK ... on success 54 | * - On an error the general status code to be put into the response 55 | */ 56 | CipError EstablishIoConnection( 57 | CipConnectionObject *RESTRICT const connection_object, 58 | EipUint16 *const extended_error); 59 | 60 | /** @brief Take the data given in the connection object structure and open the necessary communication channels 61 | * 62 | * This function will use the g_stCPFDataItem! 63 | * @param connection_object pointer to the connection object data 64 | * @return general status on the open process 65 | * - EIP_OK ... on success 66 | * - On an error the general status code to be put into the response 67 | */ 68 | CipError OpenCommunicationChannels(CipConnectionObject *connection_object); 69 | 70 | /** @brief close the communication channels of the given connection and remove it 71 | * from the active connections list. 72 | * 73 | * @param connection_object pointer to the connection object data 74 | */ 75 | void CloseCommunicationChannelsAndRemoveFromActiveConnectionsList( 76 | CipConnectionObject *connection_object); 77 | 78 | extern EipUint8 *g_config_data_buffer; 79 | extern unsigned int g_config_data_length; 80 | 81 | #endif /* OPENER_CIPIOCONNECTION_H_ */ 82 | -------------------------------------------------------------------------------- /source/src/cip/cipmessagerouter.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_CIPMESSAGEROUTER_H_ 7 | #define OPENER_CIPMESSAGEROUTER_H_ 8 | 9 | #include "typedefs.h" 10 | #include "ciptypes.h" 11 | 12 | /** @brief Message Router class code */ 13 | static const CipUint kCipMessageRouterClassCode = 0x02U; 14 | 15 | /* public functions */ 16 | 17 | /** @brief Initialize the data structures of the message router 18 | * @return kEipStatusOk if class was initialized, otherwise kEipStatusError 19 | */ 20 | EipStatus CipMessageRouterInit(void); 21 | 22 | /** @brief Free all data allocated by the classes created in the CIP stack 23 | */ 24 | void DeleteAllClasses(void); 25 | 26 | /** @brief Notify the MessageRouter that an explicit message (connected or unconnected) 27 | * has been received. This function will be called from the encapsulation layer. 28 | * The CPF structure is already parsed an can be accessed via the global variable: 29 | * g_stCPFDataItem. 30 | * @param data pointer to the data buffer of the message directly at the beginning of the CIP part. 31 | * @param data_length number of bytes in the data buffer 32 | * @param originator_address The address of the originator as received 33 | * @param encapsulation_session The associated encapsulation session of the explicit message 34 | * @return kEipStatusError on fault 35 | * kEipStatusOk on success 36 | */ 37 | EipStatus NotifyMessageRouter(EipUint8 *data, 38 | int data_length, 39 | CipMessageRouterResponse *message_router_response, 40 | const struct sockaddr *const originator_address, 41 | const CipSessionHandle encapsulation_session); 42 | 43 | /*! Register a class at the message router. 44 | * In order that the message router can deliver 45 | * explicit messages each class has to register. 46 | * Will be automatically done when invoking create 47 | * createCIPClass. 48 | * @param cip_class CIP class to be registered 49 | * @return kEipStatusOk on success 50 | */ 51 | EipStatus RegisterCipClass(CipClass *cip_class); 52 | 53 | #endif /* OPENER_CIPMESSAGEROUTER_H_ */ 54 | -------------------------------------------------------------------------------- /source/src/cip/cipqos.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef OPENER_CIPQOS_H_ 8 | #define OPENER_CIPQOS_H_ 9 | 10 | /** @file cipqos.h 11 | * @brief Public interface of the QoS Object 12 | * 13 | */ 14 | 15 | #include "typedefs.h" 16 | #include "ciptypes.h" 17 | #include "cipconnectionmanager.h" 18 | 19 | /** @brief QoS Object class code */ 20 | static const CipUint kCipQoSClassCode = 0x48U; 21 | 22 | /* public types */ 23 | 24 | /** This type represents the group of DSCP values of the QoS object. */ 25 | typedef struct cip_qos_dscp_values { 26 | CipUsint event; /**< Attr. #2: DSCP value for event messages */ 27 | CipUsint general; /**< Attr. #3: DSCP value for general messages */ 28 | CipUsint urgent; /**< Attr. #4: DSCP value for CIP transport class 0/1 Urgent priority messages */ 29 | CipUsint scheduled; /**< Attr. #5: DSCP value for CIP transport class 0/1 Scheduled priority messages */ 30 | CipUsint high; /**< Attr. #6: DSCP value for CIP transport class 0/1 High priority messages */ 31 | CipUsint low; /**< Attr. #7: DSCP value for CIP transport class 0/1 low priority messages */ 32 | CipUsint explicit_msg; /**< Attr. #8: DSCP value for CIP explicit messages (transport class 2/3 and UCMM) 33 | and all other EtherNet/IP encapsulation messages */ 34 | } CipQosDscpValues; 35 | 36 | /** This type represents the QoS object */ 37 | typedef struct { 38 | CipUsint q_frames_enable; /**< Attr. #1: Enables or disable sending 802.1Q frames on CIP and IEEE 1588 messages */ 39 | CipQosDscpValues dscp; /**< Attributes #2 ... #8 of DSCP values - beware! must not be the used set */ 40 | } CipQosObject; 41 | 42 | 43 | /* public data */ 44 | extern CipQosObject g_qos; 45 | 46 | 47 | /* public functions */ 48 | /** @brief Provide the matching DSCP value for a given connection object priority level 49 | */ 50 | CipUsint CipQosGetDscpPriority(ConnectionObjectPriority priority); 51 | 52 | /** @brief Create and initialize the QoS object 53 | */ 54 | EipStatus CipQoSInit(void); 55 | 56 | /** @brief Updates the currently used set of DSCP priority values 57 | */ 58 | void CipQosUpdateUsedSetQosValues(void); 59 | 60 | /** @brief Reset attribute values to default. Does not update currently used set */ 61 | void CipQosResetAttributesToDefaultValues(void); 62 | 63 | #endif /* OPENER_CIPQOS_H_*/ 64 | -------------------------------------------------------------------------------- /source/src/cip/cipstringi.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "ciptypes.h" 8 | 9 | void CipStringIDelete(CipStringI *const string); 10 | 11 | bool CipStringIsAnyStringEmpty(const CipStringI *const string); 12 | 13 | void CipStringICopy(CipStringI *const to, 14 | const CipStringI *const from); 15 | 16 | void CipStringIDecodeFromMessage(CipStringI *data_to, 17 | CipMessageRouterRequest *const message_router_request); 18 | 19 | bool CipStringICompare(const CipStringI *const stringI_1, 20 | const CipStringI *const stringI_2); 21 | -------------------------------------------------------------------------------- /source/src/cip/ciptcpipinterface.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_CIPTCPIPINTERFACE_H_ 7 | #define OPENER_CIPTCPIPINTERFACE_H_ 8 | 9 | /** @file ciptcpipinterface.h 10 | * @brief Public interface of the TCP/IP Interface Object 11 | * 12 | */ 13 | 14 | #include "typedefs.h" 15 | #include "ciptypes.h" 16 | 17 | /** @brief TCP/IP Interface class code */ 18 | static const CipUint kCipTcpIpInterfaceClassCode = 0xF5U; 19 | 20 | /* Declare constants for status attribute (#1) */ 21 | /** Indicates a pending configuration change in the TTL Value and/or Mcast Config attributes.*/ 22 | static const CipDword kTcpipStatusMcastPend = 0x10U; 23 | /** Indicates a pending configuration change in the Interface Configuration attribute. */ 24 | static const CipDword kTcpipStatusIfaceCfgPend = 0x20U; 25 | /** Indicates when an IP address conflict has been detected by ACD. */ 26 | static const CipDword kTcpipStatusAcdStatus = 0x40U; 27 | /** Indicates when an IP address conflict has been detected by ACD or the defense failed. */ 28 | static const CipDword kTcpipStatusAcdFault = 0x80U; 29 | 30 | /* Declare constants for config_control attribute (#3) */ 31 | static const CipDword kTcpipCfgCtrlStaticIp = 0x00U; /**< IP configuration method is manual IP assignment */ 32 | static const CipDword kTcpipCfgCtrlBootp = 0x01U; /**< IP configuration method is BOOTP */ 33 | static const CipDword kTcpipCfgCtrlDhcp = 0x02U; /**< IP configuration method is DHCP */ 34 | static const CipDword kTcpipCfgCtrlMethodMask = 0x0FU; /**< bit mask for the method field */ 35 | static const CipDword kTcpipCfgCtrlDnsEnable = 0x10U; /**< enables DNS resolution on originator devices */ 36 | 37 | /** @brief Multicast Configuration struct, called Mcast config 38 | * 39 | */ 40 | typedef struct multicast_address_configuration { 41 | CipUsint alloc_control; /**< 0 for default multicast address generation algorithm; 1 for multicast addresses according to Num MCast and MCast Start Addr */ 42 | CipUsint reserved_shall_be_zero; /**< shall be zero */ 43 | CipUint number_of_allocated_multicast_addresses; /**< Number of IP multicast addresses allocated */ 44 | CipUdint starting_multicast_address; /**< Starting multicast address from which Num Mcast addresses are allocated */ 45 | } MulticastAddressConfiguration; 46 | 47 | /** @brief Declaration of the TCP/IP object's structure type 48 | */ 49 | typedef struct { 50 | CipDword status; /**< attribute #1 TCP status */ 51 | CipDword config_capability; /**< attribute #2 bitmap of capability flags */ 52 | CipDword config_control; /**< attribute #3 bitmap: control the interface configuration method: static / BOOTP / DHCP */ 53 | CipEpath physical_link_object; /**< attribute #4 references the Ethernet Link object for this interface */ 54 | CipTcpIpInterfaceConfiguration interface_configuration;/**< attribute #5 IP, network mask, gateway, name server 1 & 2, domain name*/ 55 | CipString hostname; /**< #6 host name*/ 56 | CipUsint mcast_ttl_value; /**< #8 the time to live value to be used for multi-cast connections */ 57 | 58 | /** #9 The multicast configuration for this device */ 59 | MulticastAddressConfiguration mcast_config; 60 | CipBool select_acd; /**< attribute #10 - Is ACD enabled? */ 61 | 62 | /** #13 Number of seconds of inactivity before TCP connection is closed */ 63 | CipUint encapsulation_inactivity_timeout; 64 | } CipTcpIpObject; 65 | 66 | 67 | /* global public variables */ 68 | extern CipTcpIpObject g_tcpip; /**< declaration of TCP/IP object instance 1 data */ 69 | 70 | /* public functions */ 71 | /** @brief Initializing the data structures of the TCP/IP interface object 72 | * 73 | * @return kEipStatusOk on success, otherwise kEipStatusError 74 | */ 75 | EipStatus CipTcpIpInterfaceInit(void); 76 | 77 | /** @brief Clean up the allocated data of the TCP/IP interface object. 78 | * 79 | * Currently this is the host name string and the domain name string. 80 | * 81 | */ 82 | void ShutdownTcpIpInterface(void); 83 | 84 | /** @brief Calculate Multicast address base from current IP setting 85 | * 86 | * @param tcpip pointer to TCP/IP object 87 | */ 88 | void CipTcpIpCalculateMulticastIp(CipTcpIpObject *const tcpip); 89 | 90 | /** @brief Public Method to get Encapsulation Inactivity Timeout Value 91 | * 92 | * 93 | */ 94 | EipUint16 GetEncapsulationInactivityTimeout(CipInstance *instance); 95 | 96 | #endif /* OPENER_CIPTCPIPINTERFACE_H_ */ 97 | -------------------------------------------------------------------------------- /source/src/cip/ciptypes.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | #include 8 | #include 9 | 10 | 11 | const CipInstanceNum kCipInstanceNumMax = UINT16_MAX; 12 | 13 | 14 | /* functions*/ 15 | size_t GetCipDataTypeLength(EipUint8 type, const EipUint8 *data) { 16 | 17 | size_t length = 0; 18 | 19 | switch (type) { 20 | case kCipBool: 21 | case kCipSint: 22 | case kCipUsint: 23 | case kCipByte: 24 | length = 1; 25 | break; 26 | 27 | case kCipInt: 28 | case kCipUint: 29 | case kCipWord: 30 | case kCipUsintUsint: 31 | case kCipItime: 32 | case kCipDate: 33 | case kCipEngUnit: 34 | length = 2; 35 | break; 36 | 37 | case kCipDint: 38 | case kCipUdint: 39 | case kCipDword: 40 | case kCipStime: 41 | case kCipFtime: 42 | case kCipTime: 43 | case kCipReal: 44 | case kCipTimeOfDay: 45 | length = 4; 46 | break; 47 | 48 | case kCipLint: 49 | case kCipUlint: 50 | case kCipLreal: 51 | case kCipLword: 52 | case kCipLtime: 53 | length = 8; 54 | break; 55 | 56 | case kCip6Usint: 57 | length = 6; 58 | break; 59 | 60 | case kCipString: 61 | case kCipString2: 62 | case kCipStringN: 63 | if(NULL != data){ 64 | length = GetIntFromMessage(&data) + 2; // string length + 2 bytes length indicator 65 | } 66 | break; 67 | 68 | case kCipShortString: 69 | if(NULL != data){ 70 | length = GetSintFromMessage(&data) + 1; // string length + 1 byte length indicator 71 | } 72 | break; 73 | 74 | case kCipEpath: 75 | if(NULL != data){ 76 | length = GetIntFromMessage(&data) + 2; // path size + 2 bytes path size indicator 77 | } 78 | break; 79 | 80 | case kCipByteArray: 81 | if (NULL != data) { 82 | CipByteArray *byte_array = (CipByteArray*) data; 83 | length = byte_array->length; 84 | } 85 | break; 86 | 87 | default: 88 | OPENER_TRACE_ERR("GetCipDataTypeLength ERROR\n"); 89 | return 0; 90 | 91 | /* TODO: missing data types: 92 | * kCipAny 93 | * kCipDateAndTime 94 | * kCipStringI 95 | * kCipMemberList 96 | */ 97 | } 98 | return length; 99 | } 100 | -------------------------------------------------------------------------------- /source/src/cip_objects/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | INCLUDE(${CMAKE_BINARY_DIR}/cip_objects/CMakeLists.txt) 2 | -------------------------------------------------------------------------------- /source/src/enet_encap/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Ethernet encapsulation library # 3 | ####################################### 4 | 5 | set( ENET_ENCAP_SRC cpf.c encap.c endianconv.c ) 6 | 7 | ####################################### 8 | # Add common includes # 9 | ####################################### 10 | opener_common_includes() 11 | 12 | ####################################### 13 | # Add platform-specific includes # 14 | ####################################### 15 | opener_platform_support("INCLUDES") 16 | 17 | add_library( ENET_ENCAP ${ENET_ENCAP_SRC} ) 18 | 19 | if( OPENER_INSTALL_AS_LIB ) 20 | install(TARGETS ENET_ENCAP 21 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 22 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 23 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} 24 | ) 25 | install(DIRECTORY ${ENET_ENCAP_SRC_DIR} 26 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 27 | FILES_MATCHING PATTERN "*.h" 28 | ) 29 | endif() 30 | 31 | target_link_libraries( ENET_ENCAP Utils) 32 | -------------------------------------------------------------------------------- /source/src/enet_encap/encap.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_ENCAP_H_ 7 | #define OPENER_ENCAP_H_ 8 | 9 | #include "typedefs.h" 10 | #include "cipconnectionobject.h" 11 | #include "generic_networkhandler.h" 12 | 13 | /** @file encap.h 14 | * @brief This file contains the public interface of the encapsulation layer 15 | */ 16 | 17 | /** @defgroup ENCAP OpENer Ethernet encapsulation layer 18 | * The Ethernet encapsulation layer handles provides the abstraction between the Ethernet and the CIP layer. 19 | */ 20 | 21 | /*** defines ***/ 22 | 23 | #define ENCAPSULATION_HEADER_LENGTH 24 24 | 25 | /** @brief definition of status codes in encapsulation protocol 26 | * All other codes are either legacy codes, or reserved for future use 27 | * */ 28 | typedef enum { 29 | kEncapsulationProtocolSuccess = 0x0000, 30 | kEncapsulationProtocolInvalidCommand = 0x0001, 31 | kEncapsulationProtocolInsufficientMemory = 0x0002, 32 | kEncapsulationProtocolIncorrectData = 0x0003, 33 | kEncapsulationProtocolInvalidSessionHandle = 0x0064, 34 | kEncapsulationProtocolInvalidLength = 0x0065, 35 | kEncapsulationProtocolUnsupportedProtocol = 0x0069 36 | } EncapsulationProtocolErrorCode; 37 | 38 | /*** structs ***/ 39 | typedef struct encapsulation_data { 40 | CipUint command_code; 41 | CipUint data_length; 42 | CipSessionHandle session_handle; 43 | CipUdint status; 44 | CipOctet sender_context[8]; /**< length of 8, according to the specification */ 45 | CipUdint options; 46 | const EipUint8 *communication_buffer_start; /**< Pointer to the communication buffer used for this message */ 47 | const EipUint8 *current_communication_buffer_position; /**< The current position in the communication buffer during the decoding process */ 48 | } EncapsulationData; 49 | 50 | typedef struct encapsulation_service_information { 51 | EipUint16 type_code; 52 | EipUint16 length; 53 | EipUint16 encapsulation_protocol_version; 54 | EipUint16 capability_flags; 55 | EipInt8 name_of_service[16]; 56 | } EncapsulationServiceInformation; 57 | 58 | /*** global variables (public) ***/ 59 | 60 | /*** public functions ***/ 61 | /** @ingroup ENCAP 62 | * @brief Initialize the encapsulation layer. 63 | */ 64 | void EncapsulationInit(void); 65 | 66 | /** @ingroup ENCAP 67 | * @brief Shutdown the encapsulation layer. 68 | * 69 | * This means that all open sessions including their sockets are closed. 70 | */ 71 | void EncapsulationShutDown(void); 72 | 73 | /** @ingroup ENCAP 74 | * @brief Handle delayed encapsulation message responses 75 | * 76 | * Certain encapsulation message requests require a delayed sending of the response 77 | * message. This functions checks if messages need to be sent and performs the 78 | * sending. 79 | */ 80 | void ManageEncapsulationMessages(const MilliSeconds elapsed_time); 81 | 82 | CipSessionHandle GetSessionFromSocket(const int socket_handle); 83 | 84 | void RemoveSession(const int socket); 85 | 86 | void CloseSessionBySessionHandle(const CipConnectionObject *const connection_object); 87 | 88 | void CloseEncapsulationSessionBySockAddr(const CipConnectionObject *const connection_object); 89 | 90 | void CloseClass3ConnectionBasedOnSession(CipSessionHandle encapsulation_session_handle); 91 | 92 | /* No reason to use this functions outside the encapsulation layer, they are here for testing */ 93 | typedef struct enip_message ENIPMessage; 94 | 95 | void EncapsulateListIdentityResponseMessage(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message); 96 | 97 | int_fast32_t CreateEncapsulationStructure(const EipUint8 *receive_buffer, 98 | size_t receive_buffer_length, 99 | EncapsulationData *const encapsulation_data); 100 | 101 | void SkipEncapsulationHeader(ENIPMessage *const outgoing_message); 102 | 103 | void GenerateEncapsulationHeader(const EncapsulationData *const receive_data, const size_t command_specific_data_length, const CipSessionHandle session_handle, 104 | const EncapsulationProtocolErrorCode encapsulation_protocol_status, ENIPMessage *const outgoing_message); 105 | 106 | void HandleReceivedListServicesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message); 107 | 108 | void HandleReceivedListInterfacesCommand(const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message); 109 | 110 | void HandleReceivedRegisterSessionCommand(int socket, const EncapsulationData *const receive_data, ENIPMessage *const outgoing_message); 111 | 112 | EipStatus HandleReceivedSendRequestResponseDataCommand(const EncapsulationData *const receive_data, const struct sockaddr *const originator_address, 113 | ENIPMessage *const outgoing_message); 114 | 115 | #endif /* OPENER_ENCAP_H_ */ 116 | -------------------------------------------------------------------------------- /source/src/enet_encap/endianconv.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_ENDIANCONV_H_ 7 | #define OPENER_ENDIANCONV_H_ 8 | 9 | #include "typedefs.h" 10 | #include "ciptypes.h" 11 | 12 | /** @file endianconv.h 13 | * @brief Responsible for Endianess conversion 14 | */ 15 | 16 | typedef enum { 17 | kOpenerEndianessUnknown = -1, 18 | kOpENerEndianessLittle = 0, 19 | kOpENerEndianessBig = 1 20 | } OpenerEndianess; 21 | 22 | /** @ingroup ENCAP 23 | * @brief Reads EIP_UINT8 from *buffer and converts little endian to host. 24 | * @param buffer pointer where data should be reed. 25 | * @return EIP_UINT8 data value 26 | */ 27 | CipSint GetSintFromMessage(const EipUint8 **const buffer); 28 | 29 | CipByte GetByteFromMessage(const CipOctet **const buffer_address); 30 | 31 | CipUsint GetUsintFromMessage(const CipOctet **const buffer_address); 32 | 33 | CipBool GetBoolFromMessage(const EipBool8 **const buffer_address); 34 | 35 | /** @ingroup ENCAP 36 | * 37 | * @brief Get an 16Bit integer from the network buffer, and moves pointer beyond the 16 bit value 38 | * @param buffer Pointer to the network buffer array. This pointer will be incremented by 2! 39 | * @return Extracted 16 bit integer value 40 | */ 41 | CipInt GetIntFromMessage(const EipUint8 **const buffer); 42 | 43 | CipUint GetUintFromMessage(const CipOctet **const buffer_address); 44 | 45 | CipWord GetWordFromMessage(const CipOctet **const buffer_address); 46 | 47 | /** @ingroup ENCAP 48 | * 49 | * @brief Get an 32Bit integer from the network buffer. 50 | * @param buffer pointer to the network buffer array. This pointer will be incremented by 4! 51 | * @return Extracted 32 bit integer value 52 | */ 53 | CipDint GetDintFromMessage(const EipUint8 **const buffer); 54 | 55 | CipUdint GetUdintFromMessage(const CipOctet **const buffer_address); 56 | 57 | CipUdint GetDwordFromMessage(const CipOctet **const buffer_address); 58 | 59 | /** @ingroup ENCAP 60 | * 61 | * @brief converts UINT8 data from host to little endian an writes it to buffer. 62 | * @param data value to be written 63 | * @param buffer pointer where data should be written. 64 | */ 65 | void AddSintToMessage(const EipUint8 data, 66 | ENIPMessage *const outgoing_message); 67 | 68 | /** @ingroup ENCAP 69 | * 70 | * @brief Write an 16Bit integer to the network buffer. 71 | * @param data value to write 72 | * @param buffer pointer to the network buffer array. This pointer will be incremented by 2! 73 | * 74 | * @return Length in bytes of the encoded message 75 | */ 76 | void AddIntToMessage(const EipUint16 data, 77 | ENIPMessage *const outgoing_message); 78 | 79 | /** @ingroup ENCAP 80 | * 81 | * @brief Write an 32Bit integer to the network buffer. 82 | * @param data value to write 83 | * @param buffer pointer to the network buffer array. This pointer will be incremented by 4! 84 | * 85 | * @return Length in bytes of the encoded message 86 | */ 87 | void AddDintToMessage(const EipUint32 data, 88 | ENIPMessage *const outgoing_message); 89 | 90 | EipUint64 GetLintFromMessage(const EipUint8 **const buffer); 91 | 92 | /** @ingroup ENCAP 93 | * 94 | * @brief Write an 64Bit integer to the network buffer. 95 | * @param data value to write 96 | * @param buffer pointer to the network buffer array. This pointer will be incremented by 8! 97 | * 98 | */ 99 | void AddLintToMessage(const EipUint64 pa_unData, 100 | ENIPMessage *const outgoing_message); 101 | 102 | /** @brief Encapsulate the sockaddr information as necessary for the Common Packet Format data items 103 | * 104 | * Converts and adds the provided port and IP address into an common packet format message 105 | * 106 | * @param port Port of the socket, has to be provided in big-endian 107 | * @param address IP address of the socket, has to be provided in big-endian 108 | * @param communication_buffer The message buffer for sending the message 109 | */ 110 | void EncapsulateIpAddress(EipUint16 port, 111 | EipUint32 address, 112 | ENIPMessage *const outgoing_message); 113 | 114 | /** Identify if we are running on a big or little endian system and set 115 | * variable. 116 | */ 117 | void DetermineEndianess(void); 118 | 119 | /** @brief Return the endianess identified on system startup 120 | * @return 121 | * - -1 endianess has not been identified up to now 122 | * - 0 little endian system 123 | * - 1 big endian system 124 | */ 125 | int GetEndianess(void); 126 | 127 | void MoveMessageNOctets(const int amount_of_bytes_moved, 128 | ENIPMessage *const outgoing_message); 129 | 130 | void FillNextNMessageOctetsWith(CipOctet value, 131 | unsigned int amount_of_bytes_written, 132 | ENIPMessage *const outgoing_message); 133 | 134 | void FillNextNMessageOctetsWithValueAndMoveToNextPosition(CipOctet value, 135 | unsigned int amount_of_filled_bytes, 136 | ENIPMessage *const outgoing_message); 137 | 138 | #endif /* OPENER_ENDIANCONV_H_ */ 139 | -------------------------------------------------------------------------------- /source/src/ports/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # OpENer ports # 3 | ####################################### 4 | 5 | add_subdirectory( ${OpENer_PLATFORM} ) 6 | add_subdirectory( nvdata ) 7 | 8 | ####################################### 9 | # Add common includes # 10 | ####################################### 11 | opener_common_includes() 12 | 13 | ####################################### 14 | # Add platform-specific includes # 15 | ####################################### 16 | opener_platform_support("INCLUDES") 17 | 18 | set( PLATFORM_GENERIC_SRC generic_networkhandler.c socket_timer.c ) 19 | 20 | add_library( PLATFORM_GENERIC ${PLATFORM_GENERIC_SRC} ) 21 | 22 | if( OPENER_INSTALL_AS_LIB ) 23 | install(TARGETS PLATFORM_GENERIC 24 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 25 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 26 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} 27 | ) 28 | install(DIRECTORY ${PORTS_SRC_DIR} 29 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 30 | USE_SOURCE_PERMISSIONS 31 | FILES_MATCHING 32 | PATTERN "*.h" 33 | PATTERN "sample_application" EXCLUDE 34 | ) 35 | endif() 36 | 37 | -------------------------------------------------------------------------------- /source/src/ports/MINGW/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(sample_application) 2 | 3 | set( PLATFORM_SPEC_SRC networkhandler.c opener_error.c networkconfig.c) 4 | 5 | ####################################### 6 | # Add common includes # 7 | ####################################### 8 | opener_common_includes() 9 | 10 | ####################################### 11 | # Add platform specific things # 12 | ####################################### 13 | opener_platform_support("INCLUDES") 14 | 15 | set (PLATFORMLIBNAME ${OpENer_PLATFORM}PLATFORM) 16 | 17 | add_library( ${PLATFORMLIBNAME} ${PLATFORM_SPEC_SRC}) 18 | 19 | if( NOT OpENer_TESTS) 20 | add_executable(OpENer main.c) 21 | target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils SAMPLE_APP ENET_ENCAP NVDATA ws2_32 iphlpapi ${OpENer_CIP_OBJECTS} ) 22 | endif() 23 | 24 | # Add additional CIP Objects 25 | string(COMPARE NOTEQUAL "${OpENer_ADD_CIP_OBJECTS}" "" OpENer_HAS_ADDITIONAL_OBJECT ) 26 | if( OpENer_HAS_ADDITIONAL_OBJECT ) 27 | message(STATUS "Additional activated objects: ${OpENer_ADD_CIP_OBJECTS}") 28 | string(REPLACE " " ";" OpENer_ADD_CIP_OBJECTS_LIST ${OpENer_ADD_CIP_OBJECTS} ) 29 | foreach(CIP_OBJECT IN LISTS OpENer_ADD_CIP_OBJECTS_LIST) 30 | include_directories(${${CIP_OBJECT}_SOURCE_DIR}) 31 | target_link_libraries( OpENer ${CIP_OBJECT} ) 32 | endforeach() 33 | else() 34 | message(STATUS "No additional activated objects") 35 | endif() 36 | -------------------------------------------------------------------------------- /source/src/ports/MINGW/networkconfig.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | -------------------------------------------------------------------------------- /source/src/ports/MINGW/networkhandler.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #define WIN32_LEAN_AND_MEAN 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "networkhandler.h" 14 | 15 | #include "generic_networkhandler.h" 16 | 17 | MicroSeconds getMicroSeconds() { 18 | LARGE_INTEGER performance_counter; 19 | LARGE_INTEGER performance_frequency; 20 | 21 | QueryPerformanceCounter(&performance_counter); 22 | QueryPerformanceFrequency(&performance_frequency); 23 | 24 | return (MicroSeconds) (performance_counter.QuadPart * 1000000LL 25 | / performance_frequency.QuadPart); 26 | } 27 | 28 | MilliSeconds GetMilliSeconds(void) { 29 | return (MilliSeconds) (getMicroSeconds() / 1000ULL); 30 | } 31 | 32 | EipStatus NetworkHandlerInitializePlatform(void) { 33 | WSADATA wsaData; 34 | const WORD wVersionRequested = MAKEWORD(2, 2); 35 | WSAStartup(wVersionRequested, &wsaData); 36 | 37 | return kEipStatusOk; 38 | } 39 | 40 | void ShutdownSocketPlatform(int socket_handle) { 41 | #warning \ 42 | "Untested. Is a shutdown() needed under Windows like for the POSIX port?" 43 | } 44 | 45 | void CloseSocketPlatform(int socket_handle) { 46 | closesocket(socket_handle); 47 | } 48 | 49 | int SetSocketToNonBlocking(int socket_handle) { 50 | u_long iMode = 1; 51 | return ioctlsocket(socket_handle, FIONBIO, &iMode); 52 | } 53 | 54 | int SetQosOnSocket(int socket, 55 | CipUsint qos_value) { 56 | /* Quote from Vol. 2, Section 5-7.4.2 DSCP Value Attributes: 57 | * Note that the DSCP value, if placed directly in the ToS field 58 | * in the IP header, must be shifted left 2 bits. */ 59 | DWORD set_tos = qos_value << 2; 60 | return setsockopt(socket, 61 | IPPROTO_IP, 62 | IP_TOS, 63 | (char *)&set_tos, 64 | sizeof(set_tos) ); 65 | } 66 | -------------------------------------------------------------------------------- /source/src/ports/MINGW/opener_error.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file opener_error.c 8 | * @author Martin Melik Merkumians 9 | * @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError 10 | * 11 | */ 12 | 13 | #include 14 | 15 | #include "opener_error.h" 16 | 17 | int GetSocketErrorNumber(void) { 18 | return WSAGetLastError(); 19 | } 20 | 21 | char *GetErrorMessage(int error_number) { 22 | char *error_message = NULL; 23 | FormatMessageA( 24 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 25 | NULL, 26 | error_number, 27 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 28 | (LPSTR)&error_message, 29 | 0, 30 | NULL); 31 | return error_message; 32 | } 33 | 34 | void FreeErrorMessage(char *error_message) { 35 | LocalFree(error_message); 36 | } 37 | -------------------------------------------------------------------------------- /source/src/ports/MINGW/platform_network_includes.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include -------------------------------------------------------------------------------- /source/src/ports/MINGW/sample_application/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Add common includes # 3 | ####################################### 4 | opener_common_includes() 5 | 6 | ####################################### 7 | # Add platform specific things # 8 | ####################################### 9 | 10 | opener_platform_support("INCLUDES") 11 | 12 | opener_platform_support("INCLUDES") 13 | 14 | if (OPENER_IS_DLR_DEVICE) 15 | set( SAMPLE_APP_DLR_ONLY_SRC ethlinkcbs.c ) 16 | endif() 17 | 18 | add_library(SAMPLE_APP sampleapplication.c ${SAMPLE_APP_DLR_ONLY_SRC}) 19 | -------------------------------------------------------------------------------- /source/src/ports/MINGW/sample_application/ethlinkcbs.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | *****************************************************************************/ 6 | #ifndef OPENER_ETHLINKCBS_H_ 7 | #define OPENER_ETHLINKCBS_H_ 8 | /** @file 9 | * @brief Declaration of Ethernet Link object callbacks 10 | * 11 | * This header declares the Ethernet Link object callbacks. These callbacks 12 | * handle the update and clear operation for the interface and media counters 13 | * of every Ethernet Link object of our device. 14 | */ 15 | 16 | 17 | /*---------------------------------------------------------------------------*/ 18 | /* INCLUDES */ 19 | /*---------------------------------------------------------------------------*/ 20 | #include "typedefs.h" 21 | #include "ciptypes.h" 22 | 23 | /*---------------------------------------------------------------------------*/ 24 | /* PROTOTYPES */ 25 | /*---------------------------------------------------------------------------*/ 26 | 27 | EipStatus EthLnkPreGetCallback 28 | ( 29 | CipInstance *const instance, 30 | CipAttributeStruct *const attribute, 31 | CipByte service 32 | ); 33 | 34 | EipStatus EthLnkPostGetCallback 35 | ( 36 | CipInstance *const instance, 37 | CipAttributeStruct *const attribute, 38 | CipByte service 39 | ); 40 | 41 | 42 | #endif /* #ifndef OPENER_ETHLINKCBS_H_ */ 43 | -------------------------------------------------------------------------------- /source/src/ports/POSIX/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(sample_application) 2 | 3 | set( PLATFORM_SPEC_SRC networkhandler.c opener_error.c networkconfig.c) 4 | 5 | ####################################### 6 | # OpENer RT patch # 7 | ####################################### 8 | set( OpENer_RT OFF CACHE BOOL "Activate OpENer RT" ) 9 | if(OpENer_RT) 10 | set( OpENer_RT_Additional_Stacksize "10240" CACHE STRING "Additional stack size above the defined minimum") 11 | add_definitions( -DOPENER_RT ) 12 | add_definitions(-DOPENER_RT_THREAD_SIZE=${OpENer_RT_Additional_Stacksize}) 13 | endif(OpENer_RT) 14 | 15 | ####################################### 16 | # AFL Fuzzing # 17 | ####################################### 18 | if(USE_FUZZ_AFL) 19 | add_definitions( -DFUZZING_AFL ) 20 | endif(USE_FUZZ_AFL) 21 | 22 | ####################################### 23 | # Add common includes # 24 | ####################################### 25 | opener_common_includes() 26 | 27 | ####################################### 28 | # Add platform-specific includes # 29 | ####################################### 30 | opener_platform_support("INCLUDES") 31 | 32 | add_library( ${OpENer_PLATFORM}PLATFORM ${PLATFORM_SPEC_SRC}) 33 | # Mark executables and/or libraries for installation 34 | 35 | if( OPENER_INSTALL_AS_LIB ) 36 | install(TARGETS ${OpENer_PLATFORM}PLATFORM 37 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 38 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 39 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} 40 | ) 41 | install(DIRECTORY ${PORTS_SRC_DIR} 42 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 43 | USE_SOURCE_PERMISSIONS 44 | FILES_MATCHING 45 | PATTERN "*.h" 46 | PATTERN "sample_application" EXCLUDE 47 | ) 48 | endif() 49 | 50 | if( NOT OpENer_TESTS) 51 | add_executable(OpENer main.c) 52 | target_link_libraries( OpENer PLATFORM_GENERIC ${OpENer_PLATFORM}PLATFORM ${PLATFORM_SPEC_LIBS} CIP Utils SAMPLE_APP ENET_ENCAP NVDATA rt cap pthread) 53 | 54 | # Add additional CIP Objects 55 | string(COMPARE NOTEQUAL "${OpENer_ADD_CIP_OBJECTS}" "" OpENer_HAS_ADDITIONAL_OBJECT ) 56 | if( OpENer_HAS_ADDITIONAL_OBJECT ) 57 | message(STATUS "Additional activated objects: ${OpENer_ADD_CIP_OBJECTS}") 58 | string(REPLACE " " ";" OpENer_ADD_CIP_OBJECTS_LIST ${OpENer_ADD_CIP_OBJECTS} ) 59 | foreach(CIP_OBJECT IN LISTS OpENer_ADD_CIP_OBJECTS_LIST) 60 | include_directories(${${CIP_OBJECT}_SOURCE_DIR}) 61 | target_link_libraries( OpENer ${CIP_OBJECT} ) 62 | endforeach() 63 | else() 64 | message(STATUS "No additional activated objects") 65 | endif() 66 | endif() 67 | 68 | if(NOT ${CMAKE_INSTALL_BINDIR} STREQUAL "") 69 | install (PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/OpENer DESTINATION ${CMAKE_INSTALL_FULL_BINDIR}) 70 | endif() 71 | -------------------------------------------------------------------------------- /source/src/ports/POSIX/networkconfig.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | -------------------------------------------------------------------------------- /source/src/ports/POSIX/networkhandler.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "networkhandler.h" 16 | 17 | #include "opener_error.h" 18 | #include "trace.h" 19 | #include "encap.h" 20 | #include "opener_user_conf.h" 21 | 22 | MicroSeconds GetMicroSeconds(void) { 23 | struct timespec now = { .tv_nsec = 0, .tv_sec = 0 }; 24 | 25 | int error = clock_gettime( CLOCK_MONOTONIC, &now ); 26 | OPENER_ASSERT(-1 != error); 27 | MicroSeconds micro_seconds = (MicroSeconds)now.tv_nsec / 1000ULL + 28 | now.tv_sec * 1000000ULL; 29 | return micro_seconds; 30 | } 31 | 32 | MilliSeconds GetMilliSeconds(void) { 33 | return (MilliSeconds) (GetMicroSeconds() / 1000ULL); 34 | } 35 | 36 | EipStatus NetworkHandlerInitializePlatform(void) { 37 | /* Add platform dependent code here if necessary */ 38 | return kEipStatusOk; 39 | } 40 | 41 | void ShutdownSocketPlatform(int socket_handle) { 42 | if(0 != shutdown(socket_handle, SHUT_RDWR) ) { 43 | int error_code = GetSocketErrorNumber(); 44 | char *error_message = GetErrorMessage(error_code); 45 | OPENER_TRACE_ERR("Failed shutdown() socket %d - Error Code: %d - %s\n", 46 | socket_handle, 47 | error_code, 48 | error_message); 49 | FreeErrorMessage(error_message); 50 | } 51 | } 52 | 53 | void CloseSocketPlatform(int socket_handle) { 54 | close(socket_handle); 55 | } 56 | 57 | int SetSocketToNonBlocking(int socket_handle) { 58 | return fcntl(socket_handle, F_SETFL, fcntl(socket_handle, 59 | F_GETFL, 60 | 0) | O_NONBLOCK); 61 | } 62 | 63 | int SetQosOnSocket(const int socket, 64 | CipUsint qos_value) { 65 | /* Quote from Vol. 2, Section 5-7.4.2 DSCP Value Attributes: 66 | * Note that the DSCP value, if placed directly in the ToS field 67 | * in the IP header, must be shifted left 2 bits. */ 68 | int set_tos = qos_value << 2; 69 | return setsockopt(socket, IPPROTO_IP, IP_TOS, &set_tos, sizeof(set_tos) ); 70 | } 71 | -------------------------------------------------------------------------------- /source/src/ports/POSIX/opener_error.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file POSIX/opener_error.c 8 | * @author Martin Melik Merkumians 9 | * @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError 10 | * 11 | */ 12 | 13 | #undef _GNU_SOURCE /* Force the use of the XSI compliant strerror_r() function. */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "opener_error.h" 21 | 22 | const int kErrorMessageBufferSize = 255; 23 | 24 | int GetSocketErrorNumber(void) { 25 | return errno; 26 | } 27 | 28 | char *GetErrorMessage(int error_number) { 29 | char *error_message = malloc(kErrorMessageBufferSize); 30 | strerror_r(error_number, error_message, kErrorMessageBufferSize); 31 | return error_message; 32 | } 33 | 34 | void FreeErrorMessage(char *error_message) { 35 | free(error_message); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /source/src/ports/POSIX/platform_network_includes.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | -------------------------------------------------------------------------------- /source/src/ports/POSIX/sample_application/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | ####################################### 3 | # Add common includes # 4 | ####################################### 5 | opener_common_includes() 6 | 7 | ####################################### 8 | # Add platform-specific includes # 9 | ####################################### 10 | opener_platform_support("INCLUDES") 11 | 12 | if (OPENER_IS_DLR_DEVICE) 13 | set( SAMPLE_APP_DLR_ONLY_SRC ethlinkcbs.c ) 14 | endif() 15 | 16 | add_library(SAMPLE_APP sampleapplication.c ${SAMPLE_APP_DLR_ONLY_SRC}) 17 | target_link_libraries( SAMPLE_APP NVDATA ) 18 | -------------------------------------------------------------------------------- /source/src/ports/POSIX/sample_application/ethlinkcbs.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | *****************************************************************************/ 6 | #ifndef OPENER_ETHLINKCBS_H_ 7 | #define OPENER_ETHLINKCBS_H_ 8 | /** @file 9 | * @brief Declaration of Ethernet Link object callbacks 10 | * 11 | * This header declares the Ethernet Link object callbacks. These callbacks 12 | * handle the update and clear operation for the interface and media counters 13 | * of every Ethernet Link object of our device. 14 | */ 15 | 16 | 17 | /*---------------------------------------------------------------------------*/ 18 | /* INCLUDES */ 19 | /*---------------------------------------------------------------------------*/ 20 | #include "typedefs.h" 21 | #include "ciptypes.h" 22 | 23 | /*---------------------------------------------------------------------------*/ 24 | /* PROTOTYPES */ 25 | /*---------------------------------------------------------------------------*/ 26 | 27 | EipStatus EthLnkPreGetCallback 28 | ( 29 | CipInstance *const instance, 30 | CipAttributeStruct *const attribute, 31 | CipByte service 32 | ); 33 | 34 | EipStatus EthLnkPostGetCallback 35 | ( 36 | CipInstance *const instance, 37 | CipAttributeStruct *const attribute, 38 | CipByte service 39 | ); 40 | 41 | 42 | #endif /* #ifndef OPENER_ETHLINKCBS_H_ */ 43 | -------------------------------------------------------------------------------- /source/src/ports/STM32/networkconfig.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "cipstring.h" 12 | #include "networkconfig.h" 13 | #include "cipcommon.h" 14 | #include "ciperror.h" 15 | #include "trace.h" 16 | #include "opener_api.h" 17 | #include "lwip/netif.h" 18 | 19 | EipStatus IfaceGetMacAddress(TcpIpInterface *iface, 20 | uint8_t *const physical_address) { 21 | memcpy(physical_address, iface->hwaddr, NETIF_MAX_HWADDR_LEN); 22 | 23 | return kEipStatusOk; 24 | } 25 | 26 | static EipStatus GetIpAndNetmaskFromInterface( 27 | TcpIpInterface *iface, CipTcpIpInterfaceConfiguration *iface_cfg) { 28 | iface_cfg->ip_address = iface->ip_addr.addr; 29 | iface_cfg->network_mask = iface->netmask.addr; 30 | 31 | return kEipStatusOk; 32 | } 33 | 34 | static EipStatus GetGatewayFromRoute(TcpIpInterface *iface, 35 | CipTcpIpInterfaceConfiguration *iface_cfg) { 36 | iface_cfg->gateway = iface->gw.addr; 37 | 38 | return kEipStatusOk; 39 | } 40 | 41 | EipStatus IfaceGetConfiguration(TcpIpInterface *iface, 42 | CipTcpIpInterfaceConfiguration *iface_cfg) { 43 | CipTcpIpInterfaceConfiguration local_cfg; 44 | EipStatus status; 45 | 46 | memset(&local_cfg, 0x00, sizeof local_cfg); 47 | 48 | status = GetIpAndNetmaskFromInterface(iface, &local_cfg); 49 | if (kEipStatusOk == status) { 50 | status = GetGatewayFromRoute(iface, &local_cfg); 51 | } 52 | if (kEipStatusOk == status) { 53 | /* Free first and then making a shallow copy of local_cfg.domain_name is 54 | * ok, because local_cfg goes out of scope now. */ 55 | ClearCipString(&iface_cfg->domain_name); 56 | *iface_cfg = local_cfg; 57 | } 58 | return status; 59 | } 60 | 61 | void GetHostName(TcpIpInterface *iface, 62 | CipString *hostname) { 63 | SetCipStringByCstr(hostname, netif_get_hostname(iface)); 64 | } 65 | -------------------------------------------------------------------------------- /source/src/ports/STM32/networkconfig.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #define IfaceLinkIsUp(iface) netif_is_link_up(iface) 8 | -------------------------------------------------------------------------------- /source/src/ports/STM32/networkhandler.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "networkhandler.h" 8 | 9 | #include "opener_error.h" 10 | #include "trace.h" 11 | #include "encap.h" 12 | #include "opener_user_conf.h" 13 | 14 | MilliSeconds GetMilliSeconds(void) { 15 | return osKernelSysTick(); 16 | } 17 | 18 | EipStatus NetworkHandlerInitializePlatform(void) { 19 | /* Add platform dependent code here if necessary */ 20 | return kEipStatusOk; 21 | } 22 | 23 | void ShutdownSocketPlatform(int socket_handle) { 24 | if (0 != shutdown(socket_handle, SHUT_RDWR)) { 25 | int error_code = GetSocketErrorNumber(); 26 | char *error_message = GetErrorMessage(error_code); 27 | OPENER_TRACE_ERR("Failed shutdown() socket %d - Error Code: %d - %s\n", 28 | socket_handle, 29 | error_code, 30 | error_message); 31 | FreeErrorMessage(error_message); 32 | } 33 | } 34 | 35 | void CloseSocketPlatform(int socket_handle) { 36 | close(socket_handle); 37 | } 38 | 39 | int SetSocketToNonBlocking(int socket_handle) { 40 | return fcntl(socket_handle, F_SETFL, fcntl(socket_handle, 41 | F_GETFL, 42 | 0) | O_NONBLOCK); 43 | } 44 | 45 | int SetQosOnSocket(const int socket, 46 | CipUsint qos_value) { 47 | /* Quote from Vol. 2, Section 5-7.4.2 DSCP Value Attributes: 48 | * Note that the DSCP value, if placed directly in the ToS field 49 | * in the IP header, must be shifted left 2 bits. */ 50 | int set_tos = qos_value << 2; 51 | return setsockopt(socket, IPPROTO_IP, IP_TOS, &set_tos, sizeof(set_tos)); 52 | } 53 | -------------------------------------------------------------------------------- /source/src/ports/STM32/opener.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2023, Peter Christen 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include "generic_networkhandler.h" 7 | #include "opener_api.h" 8 | #include "cipethernetlink.h" 9 | #include "ciptcpipinterface.h" 10 | #include "trace.h" 11 | #include "networkconfig.h" 12 | #include "doublylinkedlist.h" 13 | #include "cipconnectionobject.h" 14 | 15 | #define OPENER_THREAD_PRIO osPriorityAboveNormal 16 | #define OPENER_STACK_SIZE 2000 17 | 18 | static void opener_thread(void const *argument); 19 | osThreadId opener_ThreadId; 20 | volatile int g_end_stack = 0; 21 | 22 | /** 23 | * @brief Initializes the OpENer Ethernet/IP stack 24 | * The network interface has to be configured and the link established 25 | * @param netif address specifying the network interface 26 | * @retval None 27 | */ 28 | void opener_init(struct netif *netif) { 29 | 30 | EipStatus eip_status = 0; 31 | 32 | if (IfaceLinkIsUp(netif)) { 33 | DoublyLinkedListInitialize(&connection_list, 34 | CipConnectionObjectListArrayAllocator, 35 | CipConnectionObjectListArrayFree); 36 | 37 | /* Fetch MAC address from the platform */ 38 | uint8_t iface_mac[6]; 39 | IfaceGetMacAddress(netif, iface_mac); 40 | 41 | /* for a real device the serial number should be unique per device */ 42 | SetDeviceSerialNumber(123456789); 43 | 44 | /* unique_connection_id should be sufficiently random or incremented and stored 45 | * in non-volatile memory each time the device boots. 46 | */ 47 | EipUint16 unique_connection_id = rand(); 48 | 49 | /* Setup the CIP Layer. All objects are initialized with the default 50 | * values for the attribute contents. */ 51 | EipStatus eip_status = CipStackInit(unique_connection_id); 52 | 53 | CipEthernetLinkSetMac(iface_mac); 54 | 55 | /* The current host name is used as a default. */ 56 | GetHostName(netif, &g_tcpip.hostname); 57 | 58 | /* register for closing signals so that we can trigger the stack to end */ 59 | g_end_stack = 0; 60 | 61 | 62 | eip_status = IfaceGetConfiguration(netif, &g_tcpip.interface_configuration); 63 | if (eip_status < 0) { 64 | OPENER_TRACE_WARN("Problems getting interface configuration\n"); 65 | } 66 | 67 | eip_status = NetworkHandlerInitialize(); 68 | } 69 | else { 70 | OPENER_TRACE_WARN("Network link is down, OpENer not started\n"); 71 | g_end_stack = 1; // end in case of network link is down 72 | } 73 | if ((g_end_stack == 0) && (eip_status == kEipStatusOk)) { 74 | osThreadDef(OpENer, opener_thread, OPENER_THREAD_PRIO, 0, 75 | OPENER_STACK_SIZE); 76 | osThreadCreate(osThread(OpENer), netif); 77 | OPENER_TRACE_INFO("OpENer: opener_thread started, free heap size: %d\n", 78 | xPortGetFreeHeapSize()); 79 | } else { 80 | OPENER_TRACE_ERR("NetworkHandlerInitialize error %d\n", eip_status); 81 | } 82 | } 83 | 84 | static void opener_thread(void const *argument) { 85 | struct netif *netif = (struct netif*) argument; 86 | /* The event loop. Put other processing you need done continually in here */ 87 | while (!g_end_stack) { 88 | if (kEipStatusOk != NetworkHandlerProcessCyclic()) { 89 | OPENER_TRACE_ERR("Error in NetworkHandler loop! Exiting OpENer!\n"); 90 | g_end_stack = 1; // end loop in case of error 91 | } 92 | if (!IfaceLinkIsUp(netif)) { 93 | OPENER_TRACE_INFO("Network link is down, exiting OpENer\n"); 94 | g_end_stack = 1; // end loop in case of network link is down 95 | } 96 | } // loop ended 97 | /* clean up network state */ 98 | NetworkHandlerFinish(); 99 | /* close remaining sessions and connections, clean up used data */ 100 | ShutdownCipStack(); 101 | /* Delete the OpENer Thread */ 102 | osThreadTerminate(NULL); 103 | } 104 | -------------------------------------------------------------------------------- /source/src/ports/STM32/opener.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2021,Peter Christen 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | 8 | 9 | /** 10 | * @brief Start OpENer Ethernet/IP stack 11 | * @param none 12 | * @retval None 13 | */ 14 | void opener_init(struct netif *netif); 15 | 16 | 17 | -------------------------------------------------------------------------------- /source/src/ports/STM32/opener_error.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file POSIX/opener_error.c 8 | * @author Martin Melik Merkumians 9 | * @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError 10 | * 11 | */ 12 | 13 | #undef _GNU_SOURCE /* Force the use of the XSI compliant strerror_r() function. */ 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include "opener_error.h" 21 | 22 | const int kErrorMessageBufferSize = 255; 23 | 24 | int GetSocketErrorNumber(void) { 25 | return errno; 26 | } 27 | 28 | char* GetErrorMessage(int error_number) { 29 | char *error_message = malloc(kErrorMessageBufferSize); 30 | strerror_r(error_number, error_message, kErrorMessageBufferSize); 31 | return error_message; 32 | } 33 | 34 | void FreeErrorMessage(char *error_message) { 35 | free(error_message); 36 | } 37 | 38 | -------------------------------------------------------------------------------- /source/src/ports/STM32/platform_network_includes.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | -------------------------------------------------------------------------------- /source/src/ports/STM32/sample_application/ethlinkcbs.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | *****************************************************************************/ 6 | #ifndef OPENER_ETHLINKCBS_H_ 7 | #define OPENER_ETHLINKCBS_H_ 8 | /** @file 9 | * @brief Declaration of Ethernet Link object callbacks 10 | * 11 | * This header declares the Ethernet Link object callbacks. These callbacks 12 | * handle the update and clear operation for the interface and media counters 13 | * of every Ethernet Link object of our device. 14 | */ 15 | 16 | 17 | /*---------------------------------------------------------------------------*/ 18 | /* INCLUDES */ 19 | /*---------------------------------------------------------------------------*/ 20 | #include "typedefs.h" 21 | #include "ciptypes.h" 22 | 23 | /*---------------------------------------------------------------------------*/ 24 | /* PROTOTYPES */ 25 | /*---------------------------------------------------------------------------*/ 26 | 27 | EipStatus EthLnkPreGetCallback 28 | ( 29 | CipInstance *const instance, 30 | CipAttributeStruct *const attribute, 31 | CipByte service 32 | ); 33 | 34 | EipStatus EthLnkPostGetCallback 35 | ( 36 | CipInstance *const instance, 37 | CipAttributeStruct *const attribute, 38 | CipByte service 39 | ); 40 | 41 | 42 | #endif /* #ifndef OPENER_ETHLINKCBS_H_ */ 43 | -------------------------------------------------------------------------------- /source/src/ports/WIN32/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(sample_application) 2 | 3 | set( PLATFORM_SPEC_SRC networkhandler.c opener_error.c networkconfig.c) 4 | 5 | ####################################### 6 | # Add common includes # 7 | ####################################### 8 | opener_common_includes() 9 | 10 | ####################################### 11 | # Add platform specific things # 12 | ####################################### 13 | opener_platform_support("INCLUDES") 14 | 15 | set (PLATFORMLIBNAME ${OpENer_PLATFORM}PLATFORM) 16 | 17 | add_library( ${PLATFORMLIBNAME} ${PLATFORM_SPEC_SRC}) 18 | 19 | if( NOT OpENer_TESTS) 20 | add_executable(OpENer main.c) 21 | target_link_libraries( OpENer PLATFORM_GENERIC ${PLATFORMLIBNAME} CIP Utils SAMPLE_APP ENET_ENCAP NVDATA ws2_32 Iphlpapi ${OpENer_CIP_OBJECTS} ) 22 | endif() 23 | 24 | # Add additional CIP Objects 25 | string(COMPARE NOTEQUAL "${OpENer_ADD_CIP_OBJECTS}" "" OpENer_HAS_ADDITIONAL_OBJECT ) 26 | if( OpENer_HAS_ADDITIONAL_OBJECT ) 27 | message(STATUS "Additional activated objects: ${OpENer_ADD_CIP_OBJECTS}") 28 | string(REPLACE " " ";" OpENer_ADD_CIP_OBJECTS_LIST ${OpENer_ADD_CIP_OBJECTS} ) 29 | foreach(CIP_OBJECT IN LISTS OpENer_ADD_CIP_OBJECTS_LIST) 30 | include_directories(${${CIP_OBJECT}_SOURCE_DIR}) 31 | target_link_libraries( OpENer ${CIP_OBJECT} ) 32 | endforeach() 33 | else() 34 | message(STATUS "No additional activated objects") 35 | endif() 36 | -------------------------------------------------------------------------------- /source/src/ports/WIN32/networkconfig.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | -------------------------------------------------------------------------------- /source/src/ports/WIN32/networkhandler.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "networkhandler.h" 13 | 14 | #include "generic_networkhandler.h" 15 | 16 | MicroSeconds GetMicroSeconds() { 17 | LARGE_INTEGER performance_counter; 18 | LARGE_INTEGER performance_frequency; 19 | 20 | QueryPerformanceCounter(&performance_counter); 21 | QueryPerformanceFrequency(&performance_frequency); 22 | 23 | return (MicroSeconds) (performance_counter.QuadPart * 1000000LL 24 | / performance_frequency.QuadPart); 25 | } 26 | 27 | MilliSeconds GetMilliSeconds(void) { 28 | return (MilliSeconds) (GetMicroSeconds() / 1000ULL); 29 | } 30 | 31 | EipStatus NetworkHandlerInitializePlatform(void) { 32 | WSADATA wsaData; 33 | const WORD wVersionRequested = MAKEWORD(2, 2); 34 | WSAStartup(wVersionRequested, &wsaData); 35 | 36 | return kEipStatusOk; 37 | } 38 | 39 | void ShutdownSocketPlatform(int socket_handle) { 40 | /* Suppress unused parameter compiler warning. */ 41 | (void)socket_handle; 42 | 43 | #pragma \ 44 | message ("Untested. Is a shutdown() needed under Windows like for the POSIX port?") 45 | } 46 | 47 | void CloseSocketPlatform(int socket_handle) { 48 | closesocket(socket_handle); 49 | } 50 | 51 | int SetSocketToNonBlocking(int socket_handle) { 52 | u_long iMode = 1; 53 | return ioctlsocket(socket_handle, FIONBIO, &iMode); 54 | } 55 | 56 | int SetQosOnSocket(const int socket, 57 | CipUsint qos_value) { 58 | /* Suppress unused parameter compiler warning. */ 59 | (void) socket; 60 | (void) qos_value; 61 | 62 | return 0; // Dummy implementation, until a working one is viable 63 | } 64 | -------------------------------------------------------------------------------- /source/src/ports/WIN32/opener_error.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file WIN32/opener_error.c 8 | * @author Martin Melik Merkumians 9 | * @brief This file includes the prototypes for error resolution functions like strerror or WSAGetLastError 10 | * 11 | */ 12 | 13 | #include 14 | 15 | #include "opener_error.h" 16 | 17 | int GetSocketErrorNumber(void) { 18 | return WSAGetLastError(); 19 | } 20 | 21 | char *GetErrorMessage(int error_number) { 22 | char *error_message = NULL; 23 | FormatMessageA( 24 | FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 25 | NULL, 26 | error_number, 27 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 28 | (LPSTR)&error_message, 29 | 0, 30 | NULL); 31 | return error_message; 32 | } 33 | 34 | void FreeErrorMessage(char *error_message) { 35 | LocalFree(error_message); 36 | } 37 | -------------------------------------------------------------------------------- /source/src/ports/WIN32/platform_network_includes.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include -------------------------------------------------------------------------------- /source/src/ports/WIN32/sample_application/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Add common includes # 3 | ####################################### 4 | opener_common_includes() 5 | 6 | ####################################### 7 | # Add platform specific things # 8 | ####################################### 9 | 10 | opener_platform_support("INCLUDES") 11 | 12 | opener_platform_support("INCLUDES") 13 | 14 | if (OPENER_IS_DLR_DEVICE) 15 | set( SAMPLE_APP_DLR_ONLY_SRC ethlinkcbs.c ) 16 | endif() 17 | 18 | add_library(SAMPLE_APP sampleapplication.c ${SAMPLE_APP_DLR_ONLY_SRC}) 19 | -------------------------------------------------------------------------------- /source/src/ports/WIN32/sample_application/ethlinkcbs.h: -------------------------------------------------------------------------------- 1 | /****************************************************************************** 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | *****************************************************************************/ 6 | #ifndef OPENER_ETHLINKCBS_H_ 7 | #define OPENER_ETHLINKCBS_H_ 8 | /** @file 9 | * @brief Declaration of Ethernet Link object callbacks 10 | * 11 | * This header declares the Ethernet Link object callbacks. These callbacks 12 | * handle the update and clear operation for the interface and media counters 13 | * of every Ethernet Link object of our device. 14 | */ 15 | 16 | 17 | /*---------------------------------------------------------------------------*/ 18 | /* INCLUDES */ 19 | /*---------------------------------------------------------------------------*/ 20 | #include "typedefs.h" 21 | #include "ciptypes.h" 22 | 23 | /*---------------------------------------------------------------------------*/ 24 | /* PROTOTYPES */ 25 | /*---------------------------------------------------------------------------*/ 26 | 27 | EipStatus EthLnkPreGetCallback 28 | ( 29 | CipInstance *const instance, 30 | CipAttributeStruct *const attribute, 31 | CipByte service 32 | ); 33 | 34 | EipStatus EthLnkPostGetCallback 35 | ( 36 | CipInstance *const instance, 37 | CipAttributeStruct *const attribute, 38 | CipByte service 39 | ); 40 | 41 | 42 | #endif /* #ifndef OPENER_ETHLINKCBS_H_ */ 43 | -------------------------------------------------------------------------------- /source/src/ports/devicedata.h.in: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef DEVICE_DATA_H_ 8 | #define DEVICE_DATA_H_ 9 | 10 | #define OPENER_DEVICE_VENDOR_ID (@OpENer_Device_Config_Vendor_Id@) 11 | #define OPENER_DEVICE_TYPE (@OpENer_Device_Config_Device_Type@) 12 | #define OPENER_DEVICE_PRODUCT_CODE (@OpENer_Device_Config_Product_Code@) 13 | #define OPENER_DEVICE_MAJOR_REVISION (@OpENer_Device_Major_Version@) 14 | #define OPENER_DEVICE_MINOR_REVISION (@OpENer_Device_Minor_Version@) 15 | #define OPENER_DEVICE_NAME ("@OpENer_Device_Config_Device_Name@") 16 | 17 | #endif /* DEVICE_DATA_H_ */ -------------------------------------------------------------------------------- /source/src/ports/generic_networkhandler.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file generic_networkhandler.h 8 | * @author Martin Melik Merkumians 9 | * @brief This file includes all platform-independent functions of the network handler to reduce code duplication 10 | * 11 | * @attention This file should only be used for implementing port-specific network handlers and is not intended to grant other parts of the OpENer access to the network layer 12 | */ 13 | 14 | #ifndef GENERIC_NETWORKHANDLER_H_ 15 | #define GENERIC_NETWORKHANDLER_H_ 16 | 17 | #include 18 | #include 19 | #include 20 | #if !defined(STM32) /** Not STM32 target */ 21 | #include 22 | #else /** STM32 target (GCC), lwip has its own error code list */ 23 | #include "lwip/errno.h" 24 | #endif /* STM32 target */ 25 | 26 | #include "opener_api.h" 27 | #include "typedefs.h" 28 | #include "endianconv.h" 29 | #include "cipconnectionmanager.h" 30 | #include "networkhandler.h" 31 | #include "appcontype.h" 32 | #include "socket_timer.h" 33 | 34 | /*The port to be used per default for I/O messages on UDP.*/ 35 | extern const uint16_t kOpenerEipIoUdpPort; 36 | extern const uint16_t kOpenerEthernetPort; 37 | 38 | extern SocketTimer g_timestamps[OPENER_NUMBER_OF_SUPPORTED_SESSIONS]; 39 | /** @brief Ethernet/IP standard ports */ 40 | #define kOpenerEthernetPort 44818 /** Port to be used per default for messages on TCP */ 41 | #define kOpenerEipIoUdpPort 2222 /** Port to be used per default for I/O messages on UDP.*/ 42 | 43 | 44 | //EipUint8 g_ethernet_communication_buffer[PC_OPENER_ETHERNET_BUFFER_SIZE]; /**< communication buffer */ 45 | 46 | extern fd_set master_socket; 47 | extern fd_set read_socket; 48 | 49 | extern int highest_socket_handle; /**< temporary file descriptor for select() */ 50 | 51 | /** @brief This variable holds the TCP socket the received to last explicit message. 52 | * It is needed for opening point to point connection to determine the peer's 53 | * address. 54 | */ 55 | extern int g_current_active_tcp_socket; 56 | 57 | extern struct timeval g_time_value; 58 | extern MilliSeconds g_actual_time; 59 | extern MilliSeconds g_last_time; 60 | /** @brief Struct representing the current network status 61 | * 62 | */ 63 | typedef struct { 64 | int tcp_listener; /**< TCP listener socket */ 65 | int udp_unicast_listener; /**< UDP unicast listener socket */ 66 | int udp_global_broadcast_listener; /**< UDP global network broadcast listener */ 67 | int udp_io_messaging; /**< UDP IO messaging socket */ 68 | CipUdint ip_address; /**< IP being valid during NetworkHandlerInitialize() */ 69 | CipUdint network_mask; /**< network mask being valid during NetworkHandlerInitialize() */ 70 | MilliSeconds elapsed_time; 71 | } NetworkStatus; 72 | 73 | extern NetworkStatus g_network_status; /**< Global variable holding the current network status */ 74 | 75 | /** @brief The platform independent part of network handler initialization routine 76 | * 77 | * @return Returns the OpENer status after the initialization routine 78 | */ 79 | EipStatus NetworkHandlerInitialize(void); 80 | 81 | void CloseUdpSocket(int socket_handle); 82 | 83 | void CloseTcpSocket(int socket_handle); 84 | 85 | EipStatus NetworkHandlerProcessCyclic(void); 86 | 87 | EipStatus NetworkHandlerFinish(void); 88 | 89 | /** @brief check if the given socket is set in the read set 90 | * @param socket The socket to check 91 | * @return true if socket is set 92 | */ 93 | EipBool8 CheckSocketSet(int socket); 94 | 95 | /** @brief Returns the socket with the highest id 96 | * @param socket1 First socket 97 | * @param socket2 Second socket 98 | * @param socket3 Third socket 99 | * @param socket4 Fourth socket 100 | * 101 | * @return Highest socket id from the provided sockets 102 | */ 103 | int GetMaxSocket(int socket1, 104 | int socket2, 105 | int socket3, 106 | int socket4); 107 | 108 | /** @brief Set the Qos the socket for implicit IO messaging 109 | * 110 | * @return 0 if successful, else the error code */ 111 | int SetQos(CipUsint qos_for_socket); 112 | 113 | /** @brief Set the socket options for Multicast Producer 114 | * 115 | * @return 0 if successful, else the error code */ 116 | int SetSocketOptionsMulticastProduce(void); 117 | 118 | /** @brief Get the peer address 119 | * 120 | * @return peer address if successful, else any address (0) */ 121 | EipUint32 GetPeerAddress(void); 122 | 123 | #endif /* GENERIC_NETWORKHANDLER_H_ */ 124 | -------------------------------------------------------------------------------- /source/src/ports/networkhandler.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_NETWORKHANDLER_H_ 7 | #define OPENER_NETWORKHANDLER_H_ 8 | 9 | #include "typedefs.h" 10 | 11 | #define OPENER_SOCKET_WOULD_BLOCK EWOULDBLOCK 12 | 13 | /** @brief Executes platform dependent network handler initialization code 14 | * 15 | * @return EipStatusOk if initialization was successful, otherwise EipStatusError 16 | */ 17 | EipStatus NetworkHandlerInitializePlatform(void); 18 | 19 | /** @brief Platform dependent code to shutdown a stream socket 20 | * 21 | * @param socket_handle The socket to shut down 22 | */ 23 | void ShutdownSocketPlatform(int socket_handle); 24 | 25 | /** @brief Platform dependent code to close a socket 26 | * 27 | * @param socket_handle The socket handle to be closed 28 | */ 29 | void CloseSocketPlatform(int socket_handle); 30 | 31 | /** @brief Tries to set socket to non blocking behavior 32 | * 33 | * @param socket_handle The socket handle to be set 34 | * 35 | * @return platform specific result code 36 | */ 37 | int SetSocketToNonBlocking(int socket_handle); 38 | 39 | /** @brief Returns current time in microseconds from monotonic time base, please note 40 | * that this does not represent a real absolute time, but measured from an arbitrary starting point 41 | * 42 | * This function returns the current time relative to an arbitrary starting point from a monotonic time source. 43 | * As monotonic clocks and clock functions in general are platform dependent, this has to be implemented for each platform 44 | * (see ports subfolders) 45 | * 46 | * @return Current time relative to monotonic clock starting point as MicroSeconds 47 | */ 48 | MicroSeconds GetMicroSeconds(void); 49 | 50 | /** @brief Returns current time in milliseconds from monotonic time base, please note 51 | * that this does not represent a real absolute time, but measured from an arbitrary starting point 52 | * 53 | * This function returns the current time relative to an arbitrary starting point from a monotonic time source. 54 | * As monotonic clocks and clock functions in general are platform dependent, this has to be implemented for each platform 55 | * (see ports subfolders) 56 | * 57 | * @return Current time relative to monotonic clock starting point as MilliSeconds 58 | */ 59 | MilliSeconds GetMilliSeconds(void); 60 | 61 | /** @brief Sets QoS on socket 62 | * 63 | * A wrapper function - needs a platform dependent implementation to set QoS on a socket 64 | * 65 | * @param socket The socket which QoS shall be set 66 | * @param qos_value The desired QoS value, as specified in ENIP Vol.2 QoS Object 67 | * 68 | * @return platform dependent result code 69 | * 70 | */ 71 | int SetQosOnSocket(const int socket, 72 | CipUsint qos_value); 73 | 74 | #endif /* OPENER_NETWORKHANDLER_H_ */ 75 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Non Volatile data storage library # 3 | ####################################### 4 | 5 | set( NVDATA_SRC nvdata.c conffile.c nvqos.c nvtcpip.c ) 6 | 7 | ####################################### 8 | # Add common includes # 9 | ####################################### 10 | opener_common_includes() 11 | 12 | ####################################### 13 | # Add platform-specific includes # 14 | ####################################### 15 | opener_platform_support("INCLUDES") 16 | 17 | add_library( NVDATA ${NVDATA_SRC} ) 18 | 19 | if( OPENER_INSTALL_AS_LIB ) 20 | install(TARGETS NVDATA 21 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 22 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 23 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} 24 | ) 25 | install(DIRECTORY ${NVDATA_SRC_DIR} 26 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 27 | FILES_MATCHING PATTERN "*.h" 28 | ) 29 | endif() 30 | 31 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/conffile.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file nvqos.c 8 | * @brief This file implements the functions to handle QoS object's NV data. 9 | * 10 | * This is only proof-of-concept code. Don't use it in a real product. 11 | * Please think about atomic update of the external file and doing something 12 | * like fsync() to flush the data really to disk. 13 | */ 14 | /* COMPILATION SWITCHES */ 15 | #define ENABLE_VERBOSE 0 /* Enable this to observe internal operation */ 16 | 17 | /* INCLUDES */ 18 | #include "conffile.h" 19 | 20 | #include 21 | #include 22 | 23 | #if defined _WIN32 24 | #include 25 | #else 26 | #include 27 | #include 28 | #endif /* if defined _WIN32 */ 29 | 30 | #include "trace.h" 31 | #include "opener_error.h" 32 | 33 | /** Base path for configuration file store */ 34 | #define CFG_BASE "nvdata/" 35 | 36 | #if ENABLE_VERBOSE != 0 37 | #define VERBOSE(pFile, pFmt, ...) do { fprintf(pFile, pFmt, ## __VA_ARGS__); \ 38 | } while (0) 39 | #else 40 | #define VERBOSE(pFile, pFmt, ...) 41 | #endif 42 | 43 | 44 | /** @brief Portable wrapper for mkdir(). Internally used by RecMkdir() 45 | * 46 | * @param[in] path the full path of the directory to create 47 | * @return zero on success, otherwise -1 and errno set 48 | */ 49 | static inline int Mkdir(const char *path) { 50 | #ifdef _WIN32 51 | return _mkdir(path); 52 | #else 53 | /* The mode of 0777 will be constrained by umask. */ 54 | return mkdir(path, 0777); 55 | #endif 56 | } 57 | 58 | 59 | static void RecMkdir(char *const p_path) { 60 | char *sep = strrchr(p_path, '/' ); 61 | if(sep && p_path != sep) { /* "p_path != sep" avoids mkdir("/")! */ 62 | *sep = '\0'; 63 | RecMkdir(p_path); 64 | *sep = '/'; 65 | } 66 | VERBOSE(stdout, " ->mkdir('%s')", p_path); 67 | if(Mkdir(p_path) && EEXIST != errno) { 68 | char *error_message = GetErrorMessage(errno); 69 | OPENER_TRACE_ERR("error while trying to create '%s', %d - %s\n", 70 | p_path, errno, error_message); 71 | FreeErrorMessage(error_message); 72 | } 73 | } 74 | 75 | 76 | static FILE *FopenMkdir(char *p_path, 77 | char *mode) { 78 | char *sep = strrchr(p_path, '/' ); 79 | /* In write mode create missing directories. */ 80 | if(sep && 'w' == *mode) { 81 | *sep = '\0'; 82 | RecMkdir(p_path); 83 | *sep = '/'; 84 | VERBOSE(stdout, "%s", "\n"); 85 | } 86 | 87 | /* Disable VS fopen depreciation warning. */ 88 | #ifdef _MSC_VER 89 | #pragma warning(disable : 4996) 90 | #endif /* _MSC_VER */ 91 | 92 | return fopen(p_path, mode); 93 | 94 | /* Restore default depreciation warning behavior. */ 95 | #ifdef _MSC_VER 96 | #pragma warning(default : 4996) 97 | #endif /* _MSC_VER */ 98 | } 99 | 100 | 101 | /** @brief Open the named configuration file possibly for write operation 102 | * 103 | * @param write Open for write? 104 | * @param p_name pointer to file name string 105 | 106 | * @return valid file handle: success; NULL: failure, errno set 107 | * 108 | * This function open a configuration file, possibly for write operation, 109 | * in the NV data storage directory. 110 | */ 111 | 112 | FILE *ConfFileOpen(const bool write, 113 | const char *const p_name) { 114 | char path_buf[64]; 115 | int rc; 116 | 117 | rc = snprintf(path_buf, sizeof path_buf, "%s%s", CFG_BASE, p_name); 118 | if (rc > 0) { 119 | return FopenMkdir(path_buf, write ? "w" : "r"); 120 | } 121 | return NULL; 122 | } 123 | 124 | /** @brief Close the configuration file associated with the FILE* given 125 | * 126 | * @param p_filep pointer to FILE* to close 127 | * @return kEipStatusOk: success; kEipStatusError: failure and errno set 128 | * 129 | * Closes the configuration file associated to p_filep. No data 130 | * synchronization to disk yet. 131 | */ 132 | EipStatus ConfFileClose(FILE **p_filep) { 133 | EipStatus eip_status = kEipStatusOk; 134 | if( 0 != fclose(*p_filep) ) { 135 | eip_status = kEipStatusError; 136 | } 137 | *p_filep = NULL; 138 | return eip_status; 139 | } 140 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/conffile.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file conffile.h 8 | * @brief The interface to handle a configuration file in an abstracted way. 9 | * 10 | */ 11 | #ifndef _CONFFILE_H_ 12 | #define _CONFFILE_H_ 13 | 14 | #include 15 | #include 16 | 17 | 18 | #include "typedefs.h" 19 | 20 | FILE *ConfFileOpen(const bool write, 21 | const char *const p_name); 22 | 23 | EipStatus ConfFileClose(FILE **p_filep); 24 | 25 | #endif /* _CONFFILE_H_ */ 26 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/nvdata.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file nvdata.c 8 | * @brief This file implements common stuff to handle Non Volatile data. 9 | * 10 | * This module implements NvdataLoad(), a function to load all NV data of 11 | * known common objects. 12 | * Also this module provides callback functions to store NV data of known 13 | * objects when called by the EIP stack. 14 | */ 15 | #include "nvdata.h" 16 | 17 | #include "trace.h" 18 | 19 | /* Include headers of objects that need support for NV data here. */ 20 | #include "nvqos.h" 21 | #include "nvtcpip.h" 22 | 23 | /** @brief Load NV data for all object classes 24 | * 25 | * @return kEipStatusOk on success, kEipStatusError if failure for any object occurred 26 | * 27 | * This function loads the NV data for each object class that supports NV data from 28 | * external storage. If any of the load routines fails then for that object class 29 | * the current object instance values are written as new NV data. That should be 30 | * the default data. 31 | * 32 | * The load routines should be of the form 33 | * int NvLoad( *p_obj_instance); 34 | * and return (-1) on failure and (0) on success. 35 | */ 36 | EipStatus NvdataLoad(void) { 37 | /* Load NV data for QoS object instance */ 38 | EipStatus eip_status = NvQosLoad(&g_qos); 39 | if (kEipStatusError != eip_status) { 40 | eip_status = 41 | ( kEipStatusError == NvQosStore(&g_qos) ) ? kEipStatusError : eip_status; 42 | } 43 | 44 | return eip_status; 45 | } 46 | 47 | /** A PostSetCallback for QoS class to store NV attributes 48 | * 49 | * @param instance pointer to instance of QoS class 50 | * @param attribute pointer to attribute structure 51 | * @param service the CIP service code of current request 52 | * 53 | * @return kEipStatusOk: success; kEipStatusError: failure 54 | * 55 | * This function implements the PostSetCallback for the QoS class. The 56 | * purpose of this function is to save the NV attributes of the QoS 57 | * class instance to external storage. 58 | * 59 | * This application specific implementation chose to save all attributes 60 | * at once using a single NvQosStore() call. 61 | */ 62 | EipStatus NvQosSetCallback(CipInstance *const instance, 63 | CipAttributeStruct *const attribute, 64 | CipByte service) { 65 | /* Suppress unused parameter compiler warning. */ 66 | (void)service; 67 | 68 | /* Suppress parameters used only for trace macros. */ 69 | #ifndef OPENER_WITH_TRACES 70 | (void)instance; 71 | #endif /* OPENER_WITH_TRACES */ 72 | 73 | EipStatus status = kEipStatusOk; 74 | 75 | if ( 0 != (kNvDataFunc & attribute->attribute_flags) ) { 76 | OPENER_TRACE_INFO("NV data update: %s, i %" PRIu32 ", a %" PRIu16 "\n", 77 | instance->cip_class->class_name, 78 | instance->instance_number, 79 | attribute->attribute_number); 80 | status = NvQosStore(&g_qos); 81 | } 82 | return status; 83 | } 84 | 85 | /** A PostSetCallback for TCP/IP class to store NV attributes 86 | * 87 | * @param instance pointer to instance of TCP/IP class 88 | * @param attribute pointer to attribute structure 89 | * @param service the CIP service code of current request 90 | * 91 | * This function implements the PostSetCallback for the TCP/IP class. The 92 | * purpose of this function is to save the NV attributes of the TCP/IP 93 | * class instance to external storage. 94 | * 95 | * This application specific implementation chose to save all attributes 96 | * at once using a single NvTcpipStore() call. 97 | */ 98 | EipStatus NvTcpipSetCallback(CipInstance *const instance, 99 | CipAttributeStruct *const attribute, 100 | CipByte service) { 101 | /* Suppress parameters used only for trace macros. */ 102 | #ifndef OPENER_WITH_TRACES 103 | (void)instance; 104 | #endif /* OPENER_WITH_TRACES */ 105 | 106 | EipStatus status = kEipStatusOk; 107 | 108 | if ( 0 != (kNvDataFunc & attribute->attribute_flags) ) { 109 | /* Workaround: Update only if service is not flagged. */ 110 | if ( 0 == (0x80 & service) ) { 111 | OPENER_TRACE_INFO("NV data update: %s, i %" PRIu32 ", a %" PRIu16 "\n", 112 | instance->cip_class->class_name, 113 | instance->instance_number, 114 | attribute->attribute_number); 115 | status = NvTcpipStore(&g_tcpip); 116 | } 117 | } 118 | return status; 119 | } 120 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/nvdata.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file nvdata.h 8 | * @brief This file provides the interface to load all needed Non Volatile data. 9 | * 10 | */ 11 | 12 | #ifndef NVDATA_H_ 13 | #define NVDATA_H_ 14 | 15 | #include "typedefs.h" 16 | #include "ciptypes.h" 17 | 18 | EipStatus NvdataLoad(void); 19 | 20 | EipStatus NvQosSetCallback 21 | ( 22 | CipInstance *const instance, 23 | CipAttributeStruct *const attribute, 24 | CipByte service 25 | ); 26 | 27 | EipStatus NvTcpipSetCallback 28 | ( 29 | CipInstance *const instance, 30 | CipAttributeStruct *const attribute, 31 | CipByte service 32 | ); 33 | 34 | #endif /* ifndef NVDATA_H_ */ 35 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/nvqos.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file nvqos.c 8 | * @brief This file implements the functions to handle QoS object's NV data. 9 | * 10 | * This is only proof-of-concept code. Don't use it in a real product. 11 | * Please think about atomic update of the external file or better parsing 12 | * of the data on input. 13 | */ 14 | #include "nvqos.h" 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "conffile.h" 22 | #include "ciptypes.h" 23 | 24 | #define QOS_CFG_NAME "qos.cfg" 25 | 26 | 27 | /** @brief Load NV data of the QoS object from file 28 | * 29 | * @param p_qos pointer to the QoS object's data structure 30 | * @return kEipStatusOk: success; kEipStatusError: failure 31 | */ 32 | int NvQosLoad(CipQosObject *p_qos) { 33 | int rd_cnt = 0; 34 | EipStatus eip_status = kEipStatusError; 35 | 36 | /* 37 | * Data type for these parameters explicitly differs from the specification(CipUsint), 38 | * because the required scanf specifier to read that type, "hhu", is not supported 39 | * in MinGW. The generic unsigned type is used instead, which is guaranteed to fit 40 | * a CipUsint, followed by a check after scanf to ensure valid values before 41 | * casting them to CipUsint upon assignment to the output structure. 42 | */ 43 | unsigned int dscp_urgent = 0; 44 | unsigned int dscp_scheduled = 0; 45 | unsigned int dscp_high = 0; 46 | unsigned int dscp_low = 0; 47 | unsigned int dscp_explicit = 0; 48 | 49 | FILE *p_file = ConfFileOpen(false, QOS_CFG_NAME); 50 | if (NULL != p_file) { 51 | 52 | /* Disable VS fscanf depreciation warning. */ 53 | #ifdef _MSC_VER 54 | #pragma warning(disable : 4996) 55 | #endif /* _MSC_VER */ 56 | 57 | /* Read input data */ 58 | rd_cnt = fscanf(p_file, 59 | " %u, %u, %u, %u, %u\n", 60 | &dscp_urgent, 61 | &dscp_scheduled, 62 | &dscp_high, 63 | &dscp_low, 64 | &dscp_explicit); 65 | 66 | /* Restore default depreciation warning behavior. */ 67 | #ifdef _MSC_VER 68 | #pragma warning(default : 4996) 69 | #endif /* _MSC_VER */ 70 | 71 | /* Need to try to close all stuff in any case. */ 72 | eip_status = ConfFileClose(&p_file); 73 | } 74 | if (kEipStatusOk == eip_status) { 75 | 76 | /* 77 | * Ensure all values read from the configuration file are within the CipUsint range; 78 | * see above comments for these dscp_* local variables for details. 79 | */ 80 | if ( (dscp_urgent > UCHAR_MAX) 81 | || (dscp_scheduled > UCHAR_MAX) 82 | || (dscp_high > UCHAR_MAX) 83 | || (dscp_low > UCHAR_MAX) 84 | || (dscp_explicit > UCHAR_MAX) ) { 85 | rd_cnt = 0; 86 | } 87 | 88 | /* If all data were read copy them to the global QoS object. */ 89 | if (5 == rd_cnt) { 90 | p_qos->dscp.urgent = (CipUsint)dscp_urgent; 91 | p_qos->dscp.scheduled = (CipUsint)dscp_scheduled; 92 | p_qos->dscp.high = (CipUsint)dscp_high; 93 | p_qos->dscp.low = (CipUsint)dscp_low; 94 | p_qos->dscp.explicit_msg = (CipUsint)dscp_explicit; 95 | } else { 96 | eip_status = kEipStatusError; 97 | } 98 | } 99 | return eip_status; 100 | } 101 | 102 | /** @brief Store NV data of the QoS object to file 103 | * 104 | * @param p_qos pointer to the QoS object's data structure 105 | * @return kEipStatusOk: success; kEipStatusError: failure 106 | */ 107 | EipStatus NvQosStore(const CipQosObject *p_qos) { 108 | FILE *p_file = ConfFileOpen(true, QOS_CFG_NAME); 109 | EipStatus eip_status = kEipStatusOk; 110 | if (NULL != p_file) { 111 | /* Print output data */ 112 | if ( 0 >= fprintf(p_file, 113 | " %" PRIu8 ", %" PRIu8 ", %" PRIu8 ", %" PRIu8 ", %" PRIu8 114 | "\n", 115 | p_qos->dscp.urgent, 116 | p_qos->dscp.scheduled, 117 | p_qos->dscp.high, 118 | p_qos->dscp.low, 119 | p_qos->dscp.explicit_msg) ) { 120 | eip_status = kEipStatusError; 121 | } 122 | 123 | /* Need to try to close all stuff in any case. */ 124 | eip_status = 125 | ( kEipStatusError == 126 | ConfFileClose(&p_file) ) ? kEipStatusError : eip_status; 127 | } 128 | return eip_status; 129 | } 130 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/nvqos.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file nvqos.h 8 | * @brief This file provides the interface to handle QoS object's NV data. 9 | * 10 | */ 11 | #ifndef _NVQOS_H_ 12 | #define _NVQOS_H_ 13 | 14 | #include "typedefs.h" 15 | 16 | #include "cipqos.h" 17 | 18 | int NvQosLoad(CipQosObject *p_qos); 19 | 20 | int NvQosStore(const CipQosObject *p_qos); 21 | 22 | #endif /* _NVQOS_H_ */ 23 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/nvtcpip.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file nvtcpip.c 8 | * @brief This file implements the functions to handle TCP/IP object's NV data. 9 | * 10 | * This is only a code skeleton. The real load and store operation is NOT 11 | * implemented. 12 | */ 13 | #include "nvtcpip.h" 14 | 15 | #include 16 | 17 | #include "trace.h" 18 | #include "conffile.h" 19 | 20 | #define TCPIP_CFG_NAME "tcpip.cfg" /**< Name of the configuration file */ 21 | 22 | 23 | /** @brief Load NV data of the TCP/IP object from file 24 | * 25 | * @param p_tcp_ip pointer to the TCP/IP object's data structure 26 | * @return kEipStatusOk: success; kEipStatusError: failure 27 | */ 28 | int NvTcpipLoad(CipTcpIpObject *p_tcp_ip) { 29 | /* Suppress unused parameter compiler warning. */ 30 | (void)p_tcp_ip; 31 | 32 | EipStatus eip_status = kEipStatusOk; 33 | 34 | FILE *p_file = ConfFileOpen(false, TCPIP_CFG_NAME); 35 | if (NULL != p_file) { 36 | /* Read input data */ 37 | OPENER_TRACE_ERR( 38 | "ERROR: Loading of TCP/IP object's NV data not implemented yet\n"); 39 | /* TODO: Implement load */ 40 | eip_status = kEipStatusError; 41 | /* If all NV attributes were read copy them attribute by attribute 42 | * to the caller's TCP/IP object. */ 43 | /* TODO: copy all NV attributes */ 44 | 45 | /* Need to try to close all stuff in any case. */ 46 | eip_status = 47 | (kEipStatusError == 48 | ConfFileClose(&p_file) ) ? kEipStatusError : eip_status; 49 | } 50 | 51 | return eip_status; 52 | } 53 | 54 | /** @brief Store NV data of the TCP/IP object to file 55 | * 56 | * @param p_tcp_ip pointer to the TCP/IP object's data structure 57 | * @return kEipStatusOk: success; kEipStatusError: failure 58 | */ 59 | EipStatus NvTcpipStore(const CipTcpIpObject *p_tcp_ip) { 60 | /* Suppress unused parameter compiler warning. */ 61 | (void)p_tcp_ip; 62 | 63 | FILE *p_file = ConfFileOpen(true, TCPIP_CFG_NAME); 64 | if (NULL != p_file) { 65 | /* Print output data */ 66 | OPENER_TRACE_ERR( 67 | "ERROR: Storing of TCP/IP object's NV data not implemented yet\n"); 68 | /* TODO: Implement store */ 69 | EipStatus eip_status = kEipStatusError; 70 | 71 | /* Need to try to close all stuff in any case. */ 72 | return (kEipStatusError == 73 | ConfFileClose(&p_file) ) ? kEipStatusError : eip_status; 74 | } else { 75 | return kEipStatusError; /* File could not be openend*/ 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /source/src/ports/nvdata/nvtcpip.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2019, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file nvtcpip.h 8 | * @brief This file provides the interface to handle TCP/IP object's NV data. 9 | * 10 | */ 11 | #ifndef _NVTCPIP_H_ 12 | #define _NVTCPIP_H_ 13 | 14 | #include "typedefs.h" 15 | 16 | #include "ciptcpipinterface.h" 17 | 18 | int NvTcpipLoad(CipTcpIpObject *p_tcp_ip); 19 | 20 | int NvTcpipStore(const CipTcpIpObject *p_tcp_ip); 21 | 22 | #endif /* _NVTCPIP_H_ */ 23 | -------------------------------------------------------------------------------- /source/src/ports/opener_error.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** @file opener_error.h 8 | * @author Martin Melik Merkumians 9 | * @brief This file includes the prototypes for error resolution functions like strerror_r or WSAGetLastError 10 | * 11 | */ 12 | 13 | /** 14 | * @brief Gets the error number or equivalent 15 | * 16 | * A delegate which implements how to get the error number from the system 17 | * 18 | * @return Error number 19 | */ 20 | int GetSocketErrorNumber(void); 21 | 22 | /** 23 | * @brief Returns a human readable message for the given error number 24 | * 25 | * Returns a human readable error message to be used in logs and traces. 26 | * The error message shall not be a shared memory, like the classic strerror function, as such functions are non-reentrant 27 | * To free the space in which the error message is returned the user shall implement and use the function 28 | * FreeErrorMessage(char *) 29 | * 30 | * @return A human readable error message for the given error number 31 | */ 32 | char *GetErrorMessage(int error_number); 33 | 34 | 35 | /** 36 | * @brief Frees the space of the error message generated by GetErrorMessage(int) 37 | * 38 | * This function shall implement an appropriate method to free the space allocated 39 | * by GetErrorMessage(int) 40 | */ 41 | void FreeErrorMessage(char *error_message); 42 | -------------------------------------------------------------------------------- /source/src/ports/socket_timer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "socket_timer.h" 8 | 9 | #include "trace.h" 10 | 11 | void SocketTimerSetSocket(SocketTimer *const socket_timer, 12 | const int socket) { 13 | socket_timer->socket = socket; 14 | OPENER_TRACE_INFO("Adds socket %d to socket timers\n", socket); 15 | } 16 | 17 | void SocketTimerSetLastUpdate(SocketTimer *const socket_timer, 18 | const MilliSeconds actual_time) { 19 | if (NULL != socket_timer) { 20 | socket_timer->last_update = actual_time; 21 | OPENER_TRACE_INFO("Sets time stamp for socket %d\n", socket_timer->socket); 22 | } 23 | } 24 | 25 | MilliSeconds SocketTimerGetLastUpdate(SocketTimer *const socket_timer) { 26 | return socket_timer->last_update; 27 | } 28 | 29 | void SocketTimerClear(SocketTimer *const socket_timer) { 30 | socket_timer->socket = kEipInvalidSocket; 31 | socket_timer->last_update = 0; 32 | } 33 | 34 | void SocketTimerArrayInitialize(SocketTimer *const array_of_socket_timers, 35 | const size_t array_length) { 36 | for (size_t i = 0; i < array_length; ++i) { 37 | SocketTimerClear(&array_of_socket_timers[i]); 38 | } 39 | } 40 | 41 | SocketTimer *SocketTimerArrayGetSocketTimer( 42 | SocketTimer *const array_of_socket_timers, 43 | const size_t array_length, 44 | const int socket) { 45 | for (size_t i = 0; i < array_length; ++i) { 46 | if (socket == array_of_socket_timers[i].socket) { 47 | return &array_of_socket_timers[i]; 48 | } 49 | } 50 | return NULL; 51 | } 52 | 53 | SocketTimer *SocketTimerArrayGetEmptySocketTimer( 54 | SocketTimer *const array_of_socket_timers, 55 | const size_t array_length) { 56 | return SocketTimerArrayGetSocketTimer(array_of_socket_timers, array_length, 57 | kEipInvalidSocket); 58 | } 59 | -------------------------------------------------------------------------------- /source/src/ports/socket_timer.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef SRC_PORTS_SOCKET_TIMER_H_ 8 | #define SRC_PORTS_SOCKET_TIMER_H_ 9 | 10 | #include "typedefs.h" 11 | 12 | /** @brief Data structure to store last usage times for sockets 13 | * 14 | */ 15 | typedef struct socket_timer { 16 | int socket; /**< key */ 17 | MilliSeconds last_update; /**< time stop of last update */ 18 | } SocketTimer; 19 | 20 | 21 | /** @brief 22 | * Sets socket of a Socket Timer 23 | * 24 | * @param socket_timer Socket Timer to be set 25 | * @param socket Socket handle 26 | */ 27 | void SocketTimerSetSocket(SocketTimer *const socket_timer, 28 | const int socket); 29 | 30 | /** @brief 31 | * Sets time stamp entry of the Socket Timer 32 | * 33 | * @param socket_timer Socket Timer to be set 34 | * @param actual_time Time stamp 35 | */ 36 | void SocketTimerSetLastUpdate(SocketTimer *const socket_timer, 37 | const MilliSeconds actual_time); 38 | 39 | /** @brief 40 | * Gets time stamp of the last update 41 | * 42 | * @param socket_timer Socket Timer to be set 43 | * @return Last update field value 44 | */ 45 | MilliSeconds SocketTimerGetLastUpdate(SocketTimer *const socket_timer); 46 | 47 | /** @brief 48 | * Clears a Socket Timer entry 49 | * 50 | * @param socket_timer Socket Timer to be cleared 51 | */ 52 | void SocketTimerClear(SocketTimer *const socket_timer); 53 | 54 | /** @brief 55 | * Initializes an array of Socket Timer entries 56 | * 57 | * @param array_of_socket_timers The array of Socket Timer entries to be initialized 58 | * @param array_length the length of the array 59 | */ 60 | void SocketTimerArrayInitialize(SocketTimer *const array_of_socket_timers, 61 | const size_t array_length); 62 | 63 | /** @brief 64 | * Get the Socket Timer entry with the spezified socket value 65 | * 66 | * @param array_of_socket_timers The Socket Timer array 67 | * @param array_length The Socket Timer array length 68 | * @param socket The socket value to be searched for 69 | * 70 | * @return The Socket Timer of found, otherwise NULL 71 | */ 72 | SocketTimer *SocketTimerArrayGetSocketTimer( 73 | SocketTimer *const array_of_socket_timers, 74 | const size_t array_length, 75 | const int socket); 76 | 77 | /** @brief 78 | * Get an empty Socket Timer entry 79 | * 80 | * @param array_of_socket_timers The Socket Timer array 81 | * @param array_length The Socket Timer array length 82 | * 83 | * @return An empty Socket Timer entry, or NULL if non is available 84 | */ 85 | SocketTimer *SocketTimerArrayGetEmptySocketTimer( 86 | SocketTimer *const array_of_socket_timers, 87 | const size_t array_length); 88 | 89 | #endif /* SRC_PORTS_SOCKET_TIMER_H_ */ 90 | -------------------------------------------------------------------------------- /source/src/trace.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_TRACE_H_ 7 | #define OPENER_TRACE_H_ 8 | 9 | /** @file trace.h 10 | * @brief Tracing infrastructure for OpENer 11 | */ 12 | 13 | #ifdef OPENER_WITH_TRACES 14 | 15 | #ifndef OPENER_INSTALL_AS_LIB 16 | #include "opener_user_conf.h" 17 | #endif 18 | 19 | /** @def OPENER_TRACE_LEVEL_ERROR Enable tracing of error messages. This is the 20 | * default if no trace level is given. 21 | */ 22 | #define OPENER_TRACE_LEVEL_ERROR 0x01 23 | 24 | /** @def OPENER_TRACE_LEVEL_WARNING Enable tracing of warning messages */ 25 | #define OPENER_TRACE_LEVEL_WARNING 0x02 26 | 27 | /** @def OPENER_TRACE_LEVEL_WARNING Enable tracing of state messages */ 28 | #define OPENER_TRACE_LEVEL_STATE 0x04 29 | 30 | /** @def OPENER_TRACE_LEVEL_INFO Enable tracing of info messages*/ 31 | #define OPENER_TRACE_LEVEL_INFO 0x08 32 | 33 | #ifndef OPENER_TRACE_LEVEL 34 | #ifdef WIN32 35 | #pragma message( \ 36 | "OPENER_TRACE_LEVEL was not defined setting it to OPENER_TRACE_LEVEL_ERROR") 37 | #else 38 | #warning \ 39 | OPENER_TRACE_LEVEL was not defined setting it to OPENER_TRACE_LEVEL_ERROR 40 | #endif 41 | 42 | #define OPENER_TRACE_LEVEL OPENER_TRACE_LEVEL_ERROR 43 | #endif 44 | 45 | /* @def OPENER_TRACE_ENABLED Can be used for conditional code compilation */ 46 | #define OPENER_TRACE_ENABLED 47 | 48 | /** @def OPENER_TRACE_ERR(...) Trace error messages. 49 | * In order to activate this trace level set the OPENER_TRACE_LEVEL_ERROR flag 50 | * in OPENER_TRACE_LEVEL. 51 | */ 52 | #define OPENER_TRACE_ERR(...) \ 53 | do { \ 54 | if (OPENER_TRACE_LEVEL_ERROR & OPENER_TRACE_LEVEL) {LOG_TRACE(__VA_ARGS__);} \ 55 | } while (0) 56 | 57 | /** @def OPENER_TRACE_WARN(...) Trace warning messages. 58 | * In order to activate this trace level set the OPENER_TRACE_LEVEL_WARNING 59 | * flag in OPENER_TRACE_LEVEL. 60 | */ 61 | #define OPENER_TRACE_WARN(...) \ 62 | do { \ 63 | if (OPENER_TRACE_LEVEL_WARNING & OPENER_TRACE_LEVEL) { \ 64 | LOG_TRACE(__VA_ARGS__);} \ 65 | } while (0) 66 | 67 | /** @def OPENER_TRACE_STATE(...) Trace state messages. 68 | * In order to activate this trace level set the OPENER_TRACE_LEVEL_STATE flag 69 | * in OPENER_TRACE_LEVEL. 70 | */ 71 | #define OPENER_TRACE_STATE(...) \ 72 | do { \ 73 | if (OPENER_TRACE_LEVEL_STATE & OPENER_TRACE_LEVEL) {LOG_TRACE(__VA_ARGS__);} \ 74 | } while (0) 75 | 76 | /** @def OPENER_TRACE_INFO(...) Trace information messages. 77 | * In order to activate this trace level set the OPENER_TRACE_LEVEL_INFO flag 78 | * in OPENER_TRACE_LEVEL. 79 | */ 80 | #define OPENER_TRACE_INFO(...) \ 81 | do { \ 82 | if (OPENER_TRACE_LEVEL_INFO & OPENER_TRACE_LEVEL) {LOG_TRACE(__VA_ARGS__);} \ 83 | } while (0) 84 | 85 | #else 86 | /* define the tracing macros empty in order to save space */ 87 | 88 | #define OPENER_TRACE_ERR(...) 89 | #define OPENER_TRACE_WARN(...) 90 | #define OPENER_TRACE_STATE(...) 91 | #define OPENER_TRACE_INFO(...) 92 | #endif 93 | /* TRACING *******************************************************************/ 94 | 95 | #endif /*OPENER_TRACE_H_*/ 96 | -------------------------------------------------------------------------------- /source/src/typedefs.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2009, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef OPENER_TYPEDEFS_H_ 7 | #define OPENER_TYPEDEFS_H_ 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "platform_network_includes.h" 14 | 15 | /** @file typedefs.h 16 | Do not use interface types for internal variables, such as "int i;", which is 17 | commonly used for loop counters or counting things. 18 | 19 | Do not over-constrain data types. Prefer the use of the native "int" and 20 | "unsigned" types. 21 | 22 | Use char for native character strings. 23 | 24 | Do not use "char" for data buffers - use "unsigned char" instead. Using char 25 | for data buffers can occasionally blow up in your face rather nastily. 26 | */ 27 | 28 | /** @brief EIP Data type definitions 29 | */ 30 | typedef uint8_t EipByte; /**< 8-bit bit string */ 31 | typedef int8_t EipInt8; /**< 8-bit signed number */ 32 | typedef int16_t EipInt16; /**< 16-bit signed number */ 33 | typedef int32_t EipInt32; /**< 32-bit signed number */ 34 | typedef uint8_t EipUint8; /**< 8-bit unsigned number */ 35 | typedef uint16_t EipUint16; /**< 16-bit unsigned number */ 36 | typedef uint32_t EipUint32; /**< 32-bit unsigned number */ 37 | typedef float EipFloat; /**< IEEE 754 32-bit floating point number */ 38 | typedef double EipDfloat; /**< IEEE 754 64-bit floating point number */ 39 | typedef uint8_t EipBool8; /**< bool data types */ 40 | 41 | /** @brief Data types as defined in the CIP Specification Vol 1 Appendix C 42 | */ 43 | typedef uint8_t CipOctet; /**< 8 bit value that indicates particular data type */ 44 | typedef uint8_t CipBool; /**< Boolean data type */ 45 | typedef uint8_t CipByte; /**< 8-bit bit string */ 46 | typedef uint16_t CipWord; /**< 16-bit bit string */ 47 | typedef uint32_t CipDword; /**< 32-bit bit string */ 48 | typedef uint8_t CipUsint; /**< 8-bit unsigned integer */ 49 | typedef uint16_t CipUint; /**< CipUint 16-bit unsigned integer */ 50 | typedef uint32_t CipUdint; /**< CipUdint 32-bit unsigned integer */ 51 | typedef int8_t CipSint; /**< 8-bit signed integer */ 52 | typedef int16_t CipInt; /**< 16-bit signed integer */ 53 | typedef int32_t CipDint; /**< 32-bit signed integer */ 54 | typedef float CipReal; /**< 32-bit IEEE 754 floating point */ 55 | typedef double CipLreal; /**< 64-bit IEEE 754 floating point */ 56 | 57 | typedef int64_t EipInt64; /**< 64-bit signed number */ 58 | typedef uint64_t EipUint64; /**< 64-bit unsigned number */ 59 | 60 | typedef int64_t CipLint; /**< 64-bit signed integer */ 61 | typedef uint64_t CipUlint; /**< 64-bit unsigned integer */ 62 | typedef uint64_t CipLword; /**< 64-bit bit string */ 63 | 64 | /** @brief Constant identifying if a socket descriptor is invalid 65 | */ 66 | static const int kEipInvalidSocket = -1; 67 | 68 | typedef unsigned long MilliSeconds; 69 | typedef unsigned long long MicroSeconds; 70 | 71 | 72 | /** @brief CIP object instance number type. 73 | * 74 | * This type is intended for storing values related to identifying or 75 | * quantifying object instances, e.g., the maximum number of instances or 76 | * a specific instance number. The data type is based on @cite CipVol1, 77 | * Table 4-4.2, Attribute IDs 2 and 3. 78 | */ 79 | typedef CipUint CipInstanceNum; 80 | 81 | 82 | /** @brief Session handle type. 83 | * 84 | * Data type for storing session identifiers as described by @cite CipVol2, 85 | * 2-3.4; data type is derived from @cite CipVol2, Table 2-3.1. 86 | */ 87 | typedef CipUdint CipSessionHandle; 88 | 89 | 90 | /** 91 | 92 | The following are generally true regarding return status: 93 | -1 ... an error occurred 94 | 0 ... success 95 | 96 | Occasionally there is a variation on this: 97 | -1 ... an error occurred 98 | 0 .. success and there is no reply to send 99 | 1 ... success and there is a reply to send 100 | 101 | For both of these cases EipStatus is the return type. 102 | 103 | Other return type are: 104 | -- return pointer to thing, 0 if error (return type is "pointer to thing") 105 | -- return count of something, -1 if error, (return type is int) 106 | 107 | */ 108 | 109 | /** @brief EIP stack status enum 110 | * 111 | */ 112 | typedef enum { 113 | kEipStatusOk = 0, /**< Stack is ok */ 114 | kEipStatusOkSend = 1, /**< Stack is ok, after send */ 115 | kEipStatusError = -1 /**< Stack is in error */ 116 | } EipStatus; 117 | 118 | /** @brief Communication direction of an UDP socket; consuming is receiver, 119 | * producing is sender 120 | * 121 | * These are used as array indexes, watch out if changing these values 122 | */ 123 | typedef enum { 124 | kUdpCommuncationDirectionConsuming = 0, /**< Consuming direction; receiver */ 125 | kUdpCommuncationDirectionProducing = 1 /**< Producing direction; sender */ 126 | } UdpCommuncationDirection; 127 | 128 | #ifndef __cplusplus 129 | /** @brief If we don't have C++ define a C++ -like "bool" keyword defines 130 | */ 131 | //typedef enum { 132 | // false = 0, /**< defines "false" as 0 */ 133 | // true = 1 /**< defines "true" as 1 */ 134 | //} BoolKeywords; 135 | #endif /* __cplusplus */ 136 | 137 | #endif /* OPENER_TYPEDEFS_H_ */ 138 | -------------------------------------------------------------------------------- /source/src/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | opener_common_includes() 3 | opener_platform_spec() 4 | 5 | set( UTILS_SRC random.c xorshiftrandom.c doublylinkedlist.c enipmessage.c) 6 | 7 | add_library( Utils ${UTILS_SRC} ) 8 | 9 | if( OPENER_INSTALL_AS_LIB ) 10 | install(TARGETS Utils 11 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 12 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 13 | RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR} 14 | ) 15 | install(DIRECTORY ${UTILS_SRC_DIR} 16 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 17 | FILES_MATCHING PATTERN "*.h" 18 | ) 19 | endif() 20 | 21 | -------------------------------------------------------------------------------- /source/src/utils/doublylinkedlist.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2017, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "doublylinkedlist.h" 8 | 9 | #include "opener_user_conf.h" 10 | #include // Needed to define NULL 11 | #include 12 | 13 | void DoublyLinkedListInitialize(DoublyLinkedList *list, 14 | NodeMemoryAllocator allocator, 15 | NodeMemoryDeallocator deallocator) { 16 | list->first = NULL; 17 | list->last = NULL; 18 | list->allocator = allocator; 19 | list->deallocator = deallocator; 20 | } 21 | 22 | void DoublyLinkedListDestroy(DoublyLinkedList *list) { 23 | DoublyLinkedListNode *iterator = list->first; 24 | while(NULL != iterator) { 25 | DoublyLinkedListNode *to_delete = iterator; 26 | iterator = iterator->next; 27 | DoublyLinkedListNodeDestroy(list, &to_delete); 28 | } 29 | 30 | list->first = NULL; 31 | list->last = NULL; 32 | list->allocator = NULL; 33 | list->deallocator = NULL; 34 | } 35 | 36 | DoublyLinkedListNode *DoublyLinkedListNodeCreate( 37 | const void *const data, 38 | NodeMemoryAllocator const 39 | allocator) { 40 | DoublyLinkedListNode *new_node = (DoublyLinkedListNode *)allocator(); 41 | new_node->data = (void *)data; 42 | return new_node; 43 | } 44 | 45 | void DoublyLinkedListNodeDestroy(const DoublyLinkedList *const list, 46 | DoublyLinkedListNode **node) { 47 | OPENER_ASSERT(list->deallocator != NULL); 48 | list->deallocator(node); 49 | } 50 | 51 | void DoublyLinkedListInsertAtHead(DoublyLinkedList *const list, 52 | const void *const data) { 53 | OPENER_ASSERT(list->allocator != NULL); 54 | DoublyLinkedListNode *new_node = DoublyLinkedListNodeCreate(data, 55 | list->allocator); 56 | if(NULL == list->first) { 57 | list->first = new_node; 58 | list->last = new_node; 59 | } else { 60 | new_node->next = list->first; 61 | list->first->previous = new_node; 62 | list->first = new_node; 63 | } 64 | } 65 | 66 | void DoublyLinkedListInsertAtTail(DoublyLinkedList *const list, 67 | const void *const data) { 68 | OPENER_ASSERT(list->allocator != NULL); 69 | DoublyLinkedListNode *new_node = DoublyLinkedListNodeCreate(data, 70 | list->allocator); 71 | if(NULL == list->last) { 72 | list->first = new_node; 73 | list->last = new_node; 74 | } else { 75 | new_node->previous = list->last; 76 | list->last->next = new_node; 77 | list->last = new_node; 78 | } 79 | } 80 | 81 | void DoublyLinkedListInsertBeforeNode(DoublyLinkedList *const list, 82 | DoublyLinkedListNode *node, 83 | void *data) { 84 | OPENER_ASSERT(list->allocator != NULL); 85 | if(list->first == node) { 86 | DoublyLinkedListInsertAtHead(list, data); 87 | } else { 88 | DoublyLinkedListNode *new_node = DoublyLinkedListNodeCreate(data, 89 | list->allocator); 90 | new_node->previous = node->previous; 91 | new_node->next = node; 92 | node->previous = new_node; 93 | new_node->previous->next = new_node; 94 | } 95 | } 96 | 97 | void DoublyLinkedListInsertAfterNode(DoublyLinkedList *const list, 98 | DoublyLinkedListNode *node, 99 | void *data) { 100 | OPENER_ASSERT(list->allocator != NULL); 101 | if(list->last == node) { 102 | DoublyLinkedListInsertAtTail(list, data); 103 | } else { 104 | DoublyLinkedListNode *new_node = DoublyLinkedListNodeCreate(data, 105 | list->allocator); 106 | new_node->previous = node; 107 | new_node->next = node->next; 108 | node->next->previous = new_node; 109 | node->next = new_node; 110 | } 111 | } 112 | 113 | void DoublyLinkedListRemoveNode(DoublyLinkedList *const list, 114 | DoublyLinkedListNode **pointer_to_node_pointer) 115 | { 116 | DoublyLinkedListNode *node = *pointer_to_node_pointer; 117 | DoublyLinkedListNode *previous = node->previous; 118 | DoublyLinkedListNode *next = node->next; 119 | 120 | if (node == list->first && node == list->last) { 121 | list->first = NULL; 122 | list->last = NULL; 123 | } else { 124 | if(node == list->first) { 125 | list->first = next; 126 | } 127 | if(node == list->last) { 128 | list->last = previous; 129 | } 130 | if(NULL != previous) { 131 | previous->next = next; 132 | } 133 | if(NULL != next) { 134 | next->previous = previous; 135 | } 136 | 137 | } 138 | 139 | 140 | DoublyLinkedListNodeDestroy(list, pointer_to_node_pointer); 141 | } 142 | -------------------------------------------------------------------------------- /source/src/utils/doublylinkedlist.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2017, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef SRC_UTILS_DOUBLYLINKEDLIST_H_ 8 | #define SRC_UTILS_DOUBLYLINKEDLIST_H_ 9 | 10 | /** 11 | * @file doublylinkedlist.h 12 | * 13 | * The public interface for a reference type doubly linked list 14 | */ 15 | 16 | typedef struct doubly_linked_list_node DoublyLinkedListNode; 17 | 18 | typedef DoublyLinkedListNode * (*NodeMemoryAllocator)( 19 | ); 20 | 21 | typedef void (*NodeMemoryDeallocator)(DoublyLinkedListNode **node); 22 | 23 | typedef struct doubly_linked_list_node { 24 | DoublyLinkedListNode *previous; 25 | DoublyLinkedListNode *next; 26 | void *data; 27 | } DoublyLinkedListNode; 28 | 29 | typedef struct { 30 | DoublyLinkedListNode *first; 31 | DoublyLinkedListNode *last; 32 | 33 | NodeMemoryAllocator allocator; 34 | NodeMemoryDeallocator deallocator; 35 | 36 | } DoublyLinkedList; 37 | 38 | void DoublyLinkedListInitialize(DoublyLinkedList *list, 39 | NodeMemoryAllocator allocator, 40 | NodeMemoryDeallocator deallocator); 41 | 42 | void DoublyLinkedListDestroy(DoublyLinkedList *list); 43 | 44 | DoublyLinkedListNode *DoublyLinkedListNodeCreate(const void *const data, 45 | NodeMemoryAllocator const allocator); 46 | 47 | void DoublyLinkedListNodeDestroy(const DoublyLinkedList *const list, 48 | DoublyLinkedListNode **node); 49 | 50 | void DoublyLinkedListInsertAtHead(DoublyLinkedList *const list, 51 | const void *const data); 52 | 53 | void DoublyLinkedListInsertAtTail(DoublyLinkedList *const list, 54 | const void *const data); 55 | 56 | void DoublyLinkedListInsertBeforeNode(DoublyLinkedList *const list, 57 | DoublyLinkedListNode *node, 58 | void *data); 59 | 60 | void DoublyLinkedListInsertAfterNode(DoublyLinkedList *const list, 61 | DoublyLinkedListNode *node, 62 | void *data); 63 | 64 | void DoublyLinkedListRemoveNode(DoublyLinkedList *const list, 65 | DoublyLinkedListNode **pointer_to_node_pointer); 66 | 67 | #endif /* SRC_UTILS_DOUBLYLINKEDLIST_H_ */ 68 | -------------------------------------------------------------------------------- /source/src/utils/enipmessage.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "enipmessage.h" 8 | #include "string.h" 9 | 10 | void InitializeENIPMessage(ENIPMessage *const message) { 11 | memset(message, 0, sizeof(ENIPMessage) ); 12 | message->current_message_position = message->message_buffer; 13 | } 14 | -------------------------------------------------------------------------------- /source/src/utils/enipmessage.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | #ifndef SRC_CIP_ENIPMESSAGE_H_ 7 | #define SRC_CIP_ENIPMESSAGE_H_ 8 | 9 | #include "opener_user_conf.h" 10 | 11 | typedef struct enip_message { 12 | CipOctet message_buffer[PC_OPENER_ETHERNET_BUFFER_SIZE]; 13 | CipOctet *current_message_position; 14 | size_t used_message_length; 15 | } ENIPMessage; 16 | 17 | void InitializeENIPMessage(ENIPMessage *const message); 18 | 19 | #endif /* SRC_CIP_ENIPMESSAGE_H_ */ 20 | -------------------------------------------------------------------------------- /source/src/utils/random.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2017, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include "random.h" 8 | 9 | #include 10 | 11 | Random *RandomNew(SetSeed set_seed, 12 | GetNextUInt32 get_next_uint32) { 13 | Random *random = malloc( sizeof(Random) ); 14 | *random = 15 | (Random ) { .set_seed = set_seed, .get_next_uint32 = get_next_uint32 }; 16 | return random; 17 | } 18 | 19 | void RandomDelete(Random **random) { 20 | free(*random); 21 | *random = NULL; 22 | } 23 | -------------------------------------------------------------------------------- /source/src/utils/random.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2017, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #ifndef OPENER_RANDOM_H_ 8 | #define OPENER_RANDOM_H_ 9 | 10 | #include 11 | 12 | typedef void (*SetSeed)(uint32_t seed); 13 | typedef uint32_t (*GetNextUInt32)(void); 14 | 15 | typedef struct { 16 | uint32_t current_seed_value; /**< Holds the current seed/random value */ 17 | SetSeed set_seed; /**< Function pointer to SetSeed function */ 18 | GetNextUInt32 get_next_uint32; /**< Function pointer to GetNextUInt32 function */ 19 | } Random; 20 | 21 | Random *RandomNew(SetSeed, 22 | GetNextUInt32); 23 | 24 | void RandomDelete(Random **random); 25 | 26 | #endif /* OPENER_RANDOM_H_ */ 27 | -------------------------------------------------------------------------------- /source/src/utils/xorshiftrandom.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2017, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include "xorshiftrandom.h" 9 | 10 | static uint32_t xor_shift_seed; /** < File-global variable holding the current seed*/ 11 | 12 | void SetXorShiftSeed(uint32_t seed) { 13 | xor_shift_seed = seed; 14 | } 15 | 16 | /** @brief Pseudo-random number algorithm 17 | * The algorithm used to create the pseudo-random numbers. 18 | * Works directly on the file global variable 19 | */ 20 | void CalculateNextSeed(void) { 21 | if (xor_shift_seed == 0) 22 | SetXorShiftSeed(time(NULL)); 23 | 24 | xor_shift_seed ^= xor_shift_seed << 13; 25 | xor_shift_seed ^= xor_shift_seed >> 17; 26 | xor_shift_seed ^= xor_shift_seed << 5; 27 | } 28 | 29 | uint32_t NextXorShiftUint32(void) { 30 | CalculateNextSeed(); 31 | return xor_shift_seed; 32 | } 33 | -------------------------------------------------------------------------------- /source/src/utils/xorshiftrandom.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2017, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | /** 8 | * @file xorshiftrandom.h 9 | * 10 | * The public interface of the XOR shift pseudo-random number generator 11 | */ 12 | #include 13 | 14 | #ifndef OPENER_XORSHIFTRANDOM_H_ 15 | #define OPENER_XORSHIFTRANDOM_H_ 16 | 17 | /** 18 | * @brief Sets the initial seed for the XOR shift pseudo-random algorithm 19 | * @param seed The initial seed value 20 | */ 21 | void SetXorShiftSeed(uint32_t seed); 22 | 23 | /** 24 | * @brief Returns the next generated pseudo-random number 25 | * @return The next pseudo-random number 26 | */ 27 | uint32_t NextXorShiftUint32(void); 28 | 29 | #endif /* OPENER__XORSHIFTRANDOM_H_ */ 30 | -------------------------------------------------------------------------------- /source/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Add test subdirectories # 3 | ####################################### 4 | 5 | ####################################### 6 | # Add test includes # 7 | ####################################### 8 | add_test_includes() 9 | 10 | 11 | ####################################### 12 | # Add common includes # 13 | ####################################### 14 | opener_common_includes() 15 | 16 | ####################################### 17 | # Add platform-specific includes # 18 | ####################################### 19 | opener_platform_support("INCLUDES") 20 | 21 | ################################################### 22 | # Copy custom test output file to binary location # 23 | ################################################### 24 | #configure_file( CTestCustom.cmake ${PROJECT_BINARY_DIR}/CTestCustom.cmake ) 25 | 26 | add_subdirectory( cip ) 27 | add_subdirectory( ports ) 28 | add_subdirectory( enet_encap ) 29 | add_subdirectory( utils ) 30 | 31 | add_executable( OpENer_Tests OpENerTests.cpp callback_mock.cpp) 32 | 33 | find_library ( CPPUTEST_LIBRARY CppUTest ${CPPUTEST_HOME}/cpputest_build/lib ) 34 | find_library ( CPPUTESTEXT_LIBRARY CppUTestExt ${CPPUTEST_HOME}/cpputest_build/lib ) 35 | 36 | target_link_libraries( OpENer_Tests rt ) 37 | 38 | target_link_libraries( OpENer_Tests gcov ${CPPUTEST_LIBRARY} ${CPPUTESTEXT_LIBRARY} ) 39 | target_link_libraries( OpENer_Tests UtilsTest Utils ) 40 | target_link_libraries( OpENer_Tests EthernetEncapsulationTest ENET_ENCAP ) 41 | target_link_libraries( OpENer_Tests CipTest CIP ) 42 | target_link_libraries( OpENer_Tests PortsTest PLATFORM_GENERIC ) 43 | target_link_libraries( OpENer_Tests NVDATA ) 44 | 45 | ######################################## 46 | # Adds test to CTest environment # 47 | ######################################## 48 | add_test(OpENer_Tests ${EXECUTABLE_OUTPUT_PATH}/OpENer_Tests -v -c) 49 | -------------------------------------------------------------------------------- /source/tests/OpENerTests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "OpENerTests.h" 6 | #include "CppUTest/TestRegistry.h" 7 | #include "CppUTestExt/MockSupportPlugin.h" 8 | 9 | extern "C" { 10 | #include "endianconv.h" 11 | } 12 | 13 | 14 | /* 15 | * Stores the location in the unit test function where execution should jump 16 | * to upon a failed assertion. 17 | */ 18 | jmp_buf assert_jump; 19 | 20 | 21 | /* 22 | * This pointer is used to indicate if an assertion is expected in the code 23 | * being tested. A non-NULL value means an assertion is expected, and the 24 | * resulting longjmp() target has been stored in the assert_jmp variable. 25 | * The actual address stored here is meaningless, aside from being NULL or not; 26 | * this pointer is never dereferenced. A pointer is used instead of a boolean 27 | * so the SetPointerPlugin can automatically reset it after every test. 28 | */ 29 | jmp_buf *assert_jump_enabled; 30 | 31 | 32 | int main(int argc, 33 | char **argv) { 34 | /* These checks are here to make sure assertions outside test runs don't crash */ 35 | CHECK(true); 36 | LONGS_EQUAL(1, 1); 37 | 38 | DetermineEndianess(); 39 | 40 | TestRegistry* reg = TestRegistry::getCurrentRegistry(); 41 | 42 | MockSupportPlugin mockPlugin; 43 | reg->installPlugin(&mockPlugin); 44 | 45 | /* 46 | * Enable the Cpputest SetPointerPlugin to automatically reset the 47 | * assert_jump_enabled pointer after each test. 48 | */ 49 | SetPointerPlugin assert_restore("AssertJumpRestore"); 50 | reg->installPlugin(&assert_restore); 51 | 52 | return CommandLineTestRunner::RunAllTests(argc, argv); 53 | } 54 | 55 | 56 | /* 57 | * This is the function called by the OPENER_ASSERT macro if the assertion 58 | * condition fails. It will interrupt the code under test in one of two ways 59 | * depending on if an assertion is expected from the CHECK_ASSERT test macro. 60 | * 61 | * Arguments: 62 | * 63 | * file - Path to the source file where the assertion failed. 64 | * line - Line number identifying the failed assertion. 65 | */ 66 | extern "C" void test_assert_fail(const char *const file, 67 | const unsigned int line) { 68 | /* 69 | * Throw an exception with the assertion location if an assertion is not 70 | * expected. Unfortunately, this will stop all further tests and does not 71 | * identify the test that failed. 72 | */ 73 | if (assert_jump_enabled == NULL) { 74 | const char format[] = "Assertion failure: %s:%d"; 75 | char dummy; 76 | 77 | /* Determine how long the exception message would be. */ 78 | int len_no_null = snprintf(&dummy, 1, format, file, line); 79 | 80 | if (len_no_null > 0) { 81 | /* 82 | * Allocate memory for the exception message, including the NULL 83 | * terminator. This memory is not freed because the forthcoming 84 | * exception terminates everything anyway. 85 | */ 86 | const size_t len_with_null = len_no_null + 1; 87 | char *msg = (char *)malloc(len_with_null); 88 | 89 | if (msg != NULL) { 90 | len_no_null = snprintf(msg, len_with_null, format, file, line); 91 | 92 | if (len_no_null > 0) { 93 | throw std::runtime_error(msg); 94 | } 95 | } 96 | } 97 | 98 | /* Throw a generic exception if string generation fails. */ 99 | throw std::runtime_error("Assertion failure."); 100 | } 101 | 102 | /* 103 | * Execute the jump back to the unit test function if an assertion was 104 | * expected. 105 | */ 106 | else { 107 | longjmp(assert_jump, 0); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /source/tests/OpENerTests.h: -------------------------------------------------------------------------------- 1 | #include "CppUTest/CommandLineTestRunner.h" 2 | 3 | IMPORT_TEST_GROUP (RandomClass); 4 | IMPORT_TEST_GROUP (XorShiftRandom); 5 | IMPORT_TEST_GROUP (EndianConversion); 6 | IMPORT_TEST_GROUP (CipCommon); 7 | IMPORT_TEST_GROUP (CipEpath); 8 | IMPORT_TEST_GROUP (CipElectronicKey); 9 | IMPORT_TEST_GROUP (CipElectronicKeyFormat); 10 | IMPORT_TEST_GROUP (CipConnectionManager); 11 | IMPORT_TEST_GROUP (CipConnectionObject); 12 | IMPORT_TEST_GROUP (SocketTimer); 13 | IMPORT_TEST_GROUP (DoublyLinkedList); 14 | IMPORT_TEST_GROUP (EncapsulationProtocol); 15 | IMPORT_TEST_GROUP (CipString); 16 | -------------------------------------------------------------------------------- /source/tests/callback_mock.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * This module provides a set of Cpputest mock functions for the OpENer 3 | * callback API. 4 | ******************************************************************************/ 5 | #include "CppUTestExt/MockSupport.h" 6 | #include 7 | extern "C" { 8 | #include "opener_api.h" 9 | } 10 | 11 | 12 | EipStatus ApplicationInitialization(void) { 13 | return (EipStatus)mock() 14 | .actualCall(__func__) 15 | .returnIntValueOrDefault(kEipStatusOk); 16 | } 17 | 18 | 19 | void HandleApplication(void) { 20 | mock().actualCall(__func__); 21 | } 22 | 23 | 24 | void CheckIoConnectionEvent(unsigned int output_assembly_id, 25 | unsigned int input_assembly_id, 26 | IoConnectionEvent io_connection_event) { 27 | mock().actualCall(__func__); 28 | } 29 | 30 | 31 | EipStatus AfterAssemblyDataReceived(CipInstance *instance) { 32 | return (EipStatus)mock() 33 | .actualCall(__func__) 34 | .returnIntValueOrDefault(kEipStatusOk); 35 | } 36 | 37 | 38 | EipBool8 BeforeAssemblyDataSend(CipInstance *instance) { 39 | return mock() 40 | .actualCall(__func__) 41 | .returnIntValueOrDefault(false); 42 | } 43 | 44 | 45 | EipStatus ResetDevice(void) { 46 | return (EipStatus)mock() 47 | .actualCall(__func__) 48 | .returnIntValueOrDefault(kEipStatusError); 49 | } 50 | 51 | 52 | EipStatus ResetDeviceToInitialConfiguration(void) { 53 | return (EipStatus)mock() 54 | .actualCall(__func__) 55 | .returnIntValueOrDefault(kEipStatusError); 56 | } 57 | 58 | 59 | void *CipCalloc(size_t number_of_elements, 60 | size_t size_of_element) { 61 | MockActualCall& call = mock().actualCall(__func__); 62 | 63 | /* 64 | * The return value is dependent upon if a predetermined result has been 65 | * configured via andReturnValue(), which should only be used to 66 | * simulate an allocation failure by returning NULL. Otherwise, the 67 | * default is to return a normally-allocated pointer. 68 | */ 69 | return call.hasReturnValue() ? NULL : calloc(number_of_elements, 70 | size_of_element); 71 | } 72 | 73 | 74 | void CipFree(void *data) { 75 | mock().actualCall(__func__); 76 | free(data); 77 | } 78 | 79 | 80 | void RunIdleChanged(EipUint32 run_idle_value) { 81 | mock().actualCall(__func__); 82 | } 83 | -------------------------------------------------------------------------------- /source/tests/check_assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This header contains definitions implementing a method for writing test 3 | * cases to confirm an OPENER_ASSERTION failure using the CHECK_ASSERT macro. 4 | * Only code implementing Cpputest test cases should include this header; it 5 | * should not be included by application code. 6 | */ 7 | #include 8 | 9 | 10 | /* See OpENerTests.cpp for descriptions. */ 11 | extern jmp_buf assert_jump; 12 | extern jmp_buf *assert_jump_enabled; 13 | 14 | 15 | /* 16 | * This macro is intended to be used in the unit test code, not the 17 | * application code, to verify a given expression, typically a call to a 18 | * function being tested, generates an assertion via a failed OPENER_ASSERT 19 | * condition. For example: 20 | * 21 | * CHECK_ASSERT(func()); 22 | * 23 | * The above statement will pass if an OPENER_ASSERT fails during func(), 24 | * or cause the test to fail if func() returns normally. 25 | * 26 | * These statements are enclosed within a do/while block to keep the if 27 | * statement isolated from surrounding if statements. 28 | */ 29 | #define CHECK_ASSERT(exp) \ 30 | do { \ 31 | /* Enable an expected assertion by storing a non-NULL pointer. */ \ 32 | UT_PTR_SET(assert_jump_enabled, &assert_jump); \ 33 | \ 34 | /* Store the assertion jump location. */ \ 35 | if (setjmp(assert_jump) == 0) { \ 36 | \ 37 | /* Code under test, which should longjmp() instead of return. */ \ 38 | exp; \ 39 | \ 40 | /* Fail if the above expression did not generate an assertion. */ \ 41 | FAIL("Did not assert as expected."); \ 42 | } \ 43 | } while (0) 44 | -------------------------------------------------------------------------------- /source/tests/cip/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Add common includes # 3 | ####################################### 4 | opener_common_includes() 5 | 6 | ####################################### 7 | # Add platform-specific includes # 8 | ####################################### 9 | opener_platform_support("INCLUDES") 10 | 11 | set( CipTestSrc cipepathtest.cpp cipelectronickeytest.cpp cipelectronickeyformattest.cpp cipconnectionmanagertest.cpp cipconnectionobjecttest.cpp cipcommontests.cpp cipstringtests.cpp) 12 | 13 | include_directories( ${SRC_DIR}/cip ) 14 | 15 | add_library( CipTest ${CipTestSrc} ) 16 | 17 | target_link_libraries( CipTest CIP ENET_ENCAP PLATFORM_GENERIC ${OpENer_PLATFORM}PLATFORM ${PLATFORM_SPEC_LIBS} rt) 18 | 19 | target_link_libraries( CipTest gcov ${CPPUTEST_LIBRARY} ${CPPUTESTEXT_LIBRARY} ) 20 | -------------------------------------------------------------------------------- /source/tests/cip/cipconnectionmanagertest.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | 13 | #include "cipconnectionmanager.h" 14 | 15 | } 16 | 17 | TEST_GROUP(CipConnectionManager) { 18 | 19 | }; 20 | 21 | -------------------------------------------------------------------------------- /source/tests/cip/cipelectronickeytest.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2020, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | 13 | #include "cipepath.h" 14 | #include "cipelectronickey.h" 15 | 16 | } 17 | 18 | TEST_GROUP (CipElectronicKey) { 19 | 20 | }; 21 | 22 | TEST(CipElectronicKey, SetKeyFormat) { 23 | CipElectronicKey key; 24 | ElectronicKeySetKeyFormat(&key, 4); 25 | CHECK_EQUAL(4, key.key_format); 26 | 27 | } 28 | 29 | TEST(CipElectronicKey, GetKeyFormat) { 30 | CipElectronicKey key = {.key_format = 4, .key_data = NULL}; 31 | CHECK_EQUAL(4, ElectronicKeyGetKeyFormat(&key) ); 32 | } 33 | 34 | TEST(CipElectronicKey, SetKeyData) { 35 | char dummyFormatData[] = {0,1,2,3,4,5}; 36 | CipElectronicKey key; 37 | ElectronicKeySetKeyData(&key, dummyFormatData); 38 | POINTERS_EQUAL(dummyFormatData, key.key_data); 39 | } 40 | 41 | TEST(CipElectronicKey, GetKeyData) { 42 | char dummyFormatData[] = {0,1,2,3,4,5}; 43 | CipElectronicKey key = {.key_format = 0, .key_data = dummyFormatData}; 44 | POINTERS_EQUAL(dummyFormatData, ElectronicKeyGetKeyData(&key) ); 45 | } 46 | -------------------------------------------------------------------------------- /source/tests/enet_encap/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Add common includes # 3 | ####################################### 4 | opener_common_includes() 5 | 6 | ####################################### 7 | # Add platform-specific includes # 8 | ####################################### 9 | opener_platform_support("INCLUDES") 10 | 11 | set( EthernetEncapsulationTestSrc endianconvtest.cpp encaptest.cpp) 12 | 13 | include_directories( ${SRC_DIR}/enet_encap ) 14 | 15 | add_library( EthernetEncapsulationTest ${EthernetEncapsulationTestSrc} ) 16 | -------------------------------------------------------------------------------- /source/tests/enet_encap/encaptest.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2018, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | extern "C" { 13 | 14 | #include "encap.h" 15 | 16 | #include "ciptypes.h" 17 | #include "enipmessage.h" 18 | 19 | } 20 | 21 | TEST_GROUP(EncapsulationProtocol) { 22 | 23 | }; 24 | 25 | TEST(EncapsulationProtocol, AnswerListIdentityRequest) { 26 | 27 | CipOctet incoming_message[] = 28 | "\x63\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd7\xdd\x00\x00" \ 29 | "\x00\x00\x00\x00\x00\x00\x00\x00"; 30 | 31 | CipOctet expected_outgoing_message[] = 32 | "\x63\x00\x31\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd7\xdd\x00\x00" \ 33 | "\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x0c\x00\x2b\x00\x01\x00" \ 34 | "\x00\x02\xaf\x12\xc0\xa8\x38\x65\x00\x00\x00\x00\x00\x00\x00\x00" \ 35 | "\x01\x00\x0c\x00\xe9\xfd\x02\x01\x00\x00\x15\xcd\x5b\x07\x09\x4f" \ 36 | "\x70\x45\x4e\x65\x72\x20\x50\x43\xff"; 37 | 38 | ENIPMessage outgoing_message; 39 | InitializeENIPMessage(&outgoing_message); 40 | 41 | EncapsulationData receive_data; 42 | CreateEncapsulationStructure(incoming_message, 43 | sizeof(incoming_message), 44 | &receive_data); 45 | 46 | EncapsulateListIdentityResponseMessage(&receive_data, &outgoing_message); 47 | 48 | } 49 | 50 | TEST(EncapsulationProtocol, AnswerListServicesRequest) { 51 | CipOctet incoming_message[] = 52 | "\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xdd\x00\x00" \ 53 | "\x00\x00\x00\x00\x00\x00\x00\x00"; 54 | 55 | CipOctet expected_outgoing_message[] = 56 | "\x04\x00\x1a\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\xdd\x00\x00" \ 57 | "\x00\x00\x00\x00\x00\x00\x00\x00"; 58 | 59 | ENIPMessage outgoing_message; 60 | InitializeENIPMessage(&outgoing_message); 61 | 62 | EncapsulationData recieved_data = {0}; 63 | CreateEncapsulationStructure(incoming_message, 64 | sizeof(incoming_message), 65 | &recieved_data); 66 | 67 | HandleReceivedListServicesCommand(&recieved_data, &outgoing_message); 68 | 69 | } 70 | 71 | TEST(EncapsulationProtocol, AnswerListInterfacesRequest) { 72 | CipOctet incoming_message[] = ""; 73 | 74 | CipOctet expected_outgoing_message[] = ""; 75 | 76 | ENIPMessage outgoing_message; 77 | InitializeENIPMessage(&outgoing_message); 78 | 79 | EncapsulationData received_data = {0}; 80 | CreateEncapsulationStructure(incoming_message, 81 | sizeof(incoming_message), 82 | &received_data); 83 | 84 | HandleReceivedListInterfacesCommand(&received_data, &outgoing_message); 85 | } 86 | 87 | TEST(EncapsulationProtocol, AnswerRegisterSessionRequestWrongProtocolVersion) { 88 | 89 | CipOctet incoming_message[] = 90 | "\x65\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x67\x88\x00\x00" \ 91 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; 92 | 93 | CipOctet expected_outgoing_message[] = ""; 94 | 95 | ENIPMessage outgoing_message; 96 | InitializeENIPMessage(&outgoing_message); 97 | 98 | EncapsulationData received_data = {0}; 99 | CreateEncapsulationStructure(incoming_message, 100 | sizeof(incoming_message), 101 | &received_data); 102 | 103 | HandleReceivedRegisterSessionCommand(0, &received_data, &outgoing_message); 104 | 105 | } 106 | 107 | TEST(EncapsulationProtocol, SendRRData) { 108 | CipOctet incoming_message[] = 109 | "\x6f\x00\x0c\x00\x01\x00\x00\x00\x00\x00\x00\x00\xf0\xdd\x00\x00" \ 110 | "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00" \ 111 | "\x01\x00\x00\x00"; 112 | 113 | CipOctet expected_outgoing_message[] = ""; 114 | 115 | ENIPMessage outgoing_message = {0}; 116 | InitializeENIPMessage(&outgoing_message); 117 | 118 | EncapsulationData received_data = {0}; 119 | CreateEncapsulationStructure(incoming_message, 120 | sizeof(incoming_message), 121 | &received_data); 122 | 123 | struct sockaddr_in fake_originator = {0}; 124 | struct sockaddr *fake_originator_pointer = 125 | (struct sockaddr *)&fake_originator; 126 | 127 | HandleReceivedSendRequestResponseDataCommand(&received_data, 128 | fake_originator_pointer, 129 | &outgoing_message); 130 | } 131 | -------------------------------------------------------------------------------- /source/tests/ports/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | ####################################### 3 | # Add common includes # 4 | ####################################### 5 | opener_common_includes() 6 | 7 | ####################################### 8 | # Add platform-specific includes # 9 | ####################################### 10 | opener_platform_support("INCLUDES") 11 | 12 | set( PortsTestSrc socket_timer_tests.cpp) 13 | 14 | include_directories( ${SRC_DIR}/ports ) 15 | 16 | add_library( PortsTest ${PortsTestSrc} ) 17 | -------------------------------------------------------------------------------- /source/tests/ports/socket_timer_tests.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2016, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | 13 | #include "socket_timer.h" 14 | 15 | } 16 | 17 | TEST_GROUP(SocketTimer) { 18 | 19 | }; 20 | 21 | TEST(SocketTimer, GetAvailableEmptySocketTimer) { 22 | SocketTimer timers[10]; 23 | SocketTimerArrayInitialize(timers, 10); 24 | POINTERS_EQUAL( &timers[0], SocketTimerArrayGetEmptySocketTimer(timers, 10) ); 25 | } 26 | 27 | TEST(SocketTimer, NoEmptySocketTimerAvailable) { 28 | SocketTimer timers[10]; 29 | memset( timers, 0, sizeof(timers) ); 30 | POINTERS_EQUAL( NULL, SocketTimerArrayGetEmptySocketTimer(timers, 10) ); 31 | } 32 | 33 | TEST(SocketTimer, SetSocket) { 34 | SocketTimer timer = { 35 | socket : -1, 36 | last_update : 0 37 | }; 38 | SocketTimerSetSocket(&timer, 1); 39 | CHECK_EQUAL( 1, timer.socket ); 40 | } 41 | 42 | 43 | TEST(SocketTimer, UpdateSocketTimer) { 44 | SocketTimer timer = { 45 | socket : -1, 46 | last_update : 0 47 | }; 48 | SocketTimerSetLastUpdate(&timer, 10); 49 | CHECK_EQUAL( 10, SocketTimerGetLastUpdate(&timer) ); 50 | } 51 | 52 | TEST(SocketTimer, ClearSocketTimer) { 53 | SocketTimer timer = { 54 | socket : 5, 55 | last_update : 100 56 | }; 57 | SocketTimerClear(&timer); 58 | CHECK_EQUAL(-1, timer.socket); 59 | CHECK_EQUAL(0, timer.last_update); 60 | } 61 | -------------------------------------------------------------------------------- /source/tests/test_assert.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This header defines an implementation of the OPENER_ASSERT macro that 3 | * can be used with Cpputest unit tests to confirm an assertion fails 4 | * under given conditions. It is conditionally included in the application 5 | * code when unit tests are enabled through the Cmake configuration; it should 6 | * not be included in the Cpputest unit test code. 7 | * 8 | * The intent is to create an assertion implementation that both immediately 9 | * stops execution after an assertion failure, as should normally be the 10 | * case, and is detectable from the unit test code. This is accomplished via 11 | * setjmp() and longjmp(), approximating the behavior of a C++ exception, 12 | * where an OPENER_ASSERTION failure results in a longjmp() back to the 13 | * unit test code, which then verifies that an assertion failure occurred. 14 | */ 15 | #ifndef OPENER_TEST_ASSERT_H 16 | #define OPENER_TEST_ASSERT_H 17 | 18 | 19 | /* 20 | * Define the OPENER_ASSERT macro to call the unit test assertion verification 21 | * function. The surrounding do/while loop serves to insulate the if statement 22 | * from any surrounding if statements. 23 | */ 24 | #define OPENER_ASSERT(assertion) \ 25 | do {if ( !(assertion) ) test_assert_fail(__FILE__, __LINE__);} while (0) 26 | 27 | 28 | /* Function Prototypes */ 29 | extern void test_assert_fail(const char *const file, 30 | const unsigned int line); 31 | 32 | 33 | #endif /* OPENER_TEST_ASSERT_H */ 34 | -------------------------------------------------------------------------------- /source/tests/utils/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | opener_common_includes() 3 | 4 | set( UtilsTestSrc randomTests.cpp xorshiftrandomtests.cpp doublylinkedlistTests.cpp) 5 | 6 | include_directories( ${SRC_DIR}/utils ) 7 | 8 | add_library( UtilsTest ${UtilsTestSrc} ) 9 | 10 | target_link_libraries( UtilsTest gcov ${CPPUTEST_LIBRARY} ${CPPUTESTEXT_LIBRARY} ) 11 | -------------------------------------------------------------------------------- /source/tests/utils/randomTests.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | 8 | #include 9 | #include 10 | 11 | extern "C" { 12 | #include 13 | #include 14 | } 15 | 16 | TEST_GROUP(RandomClass) 17 | { 18 | 19 | }; 20 | 21 | TEST(RandomClass, CreateXOrShiftObject) 22 | { 23 | Random *pRandom = NULL; 24 | uint32_t nResult = 0; 25 | pRandom = RandomNew(SetXorShiftSeed, NextXorShiftUint32); 26 | POINTERS_EQUAL(SetXorShiftSeed, pRandom->set_seed); 27 | POINTERS_EQUAL(NextXorShiftUint32, pRandom->get_next_uint32); 28 | RandomDelete(&pRandom); 29 | } 30 | -------------------------------------------------------------------------------- /source/tests/utils/xorshiftrandomtests.cpp: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2015, Rockwell Automation, Inc. 3 | * All rights reserved. 4 | * 5 | ******************************************************************************/ 6 | 7 | #include 8 | #include 9 | 10 | extern "C" { 11 | #include 12 | } 13 | 14 | TEST_GROUP(XorShiftRandom) 15 | { 16 | 17 | }; 18 | 19 | /*Characterization test*/ 20 | TEST(XorShiftRandom, SeedOneCharacterization) 21 | { 22 | uint32_t nResult; 23 | SetXorShiftSeed(1); 24 | nResult = NextXorShiftUint32(); 25 | LONGS_EQUAL(270369, nResult); 26 | nResult = NextXorShiftUint32(); 27 | LONGS_EQUAL(67634689, nResult); 28 | nResult = NextXorShiftUint32(); 29 | LONGS_EQUAL(2647435461, nResult); 30 | nResult = NextXorShiftUint32(); 31 | LONGS_EQUAL(307599695, nResult); 32 | } 33 | -------------------------------------------------------------------------------- /travis_scripts/compileGcovResults.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # Delete old gcov folder 4 | rm -rf gcov_results 5 | 6 | # Create new gcov folder 7 | mkdir gcov_results 8 | # Change into results folder 9 | cd gcov_results 10 | # Get all object files 11 | OBJECTS=$(find ../src -iname "*.o") 12 | # Process all files 13 | for o in $OBJECTS; 14 | do 15 | gcov -abcfu $o; 16 | done 17 | -------------------------------------------------------------------------------- /travis_scripts/generateDocumentationAndDeploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ################################################################################ 3 | # Title : generateDocumentationAndDeploy.sh 4 | # Date created : 2016/12/09 5 | # Notes : 6 | __AUTHOR__="Martin Melik-Merkumians" 7 | # Preconditions: 8 | # - Packages doxygen doxygen-doc doxygen-latex doxygen-gui graphviz 9 | # must be installed. 10 | # - Doxygen configuration file must have the destination directory empty and 11 | # source code directory with a $(TRAVIS_BUILD_DIR) prefix. 12 | # - An gh-pages branch should already exist. See below for mor info on hoe to 13 | # create a gh-pages branch. 14 | # 15 | # Required global variables: 16 | # - TRAVIS_BUILD_NUMBER : The number of the current build. 17 | # - TRAVIS_COMMIT : The commit that the current build is testing. 18 | # - DOXYFILE : The Doxygen configuration file. 19 | # - GH_REPO_NAME : The name of the repository. 20 | # - GH_REPO_REF : The GitHub reference to the repository. 21 | # - GH_REPO_TOKEN : Secure token to the github repository. 22 | # 23 | # For information on how to encrypt variables for Travis CI please go to 24 | # https://docs.travis-ci.com/user/environment-variables/#Encrypted-Variables 25 | # or https://gist.github.com/vidavidorra/7ed6166a46c537d3cbd2 26 | # For information on how to create a clean gh-pages branch from the master 27 | # branch, please go to https://gist.github.com/vidavidorra/846a2fc7dd51f4fe56a0 28 | # 29 | # This script will generate Doxygen documentation and push the documentation to 30 | # the gh-pages branch of a repository specified by GH_REPO_REF. 31 | # Before this script is used there should already be a gh-pages branch in the 32 | # repository. 33 | # 34 | ################################################################################ 35 | 36 | ################################################################################ 37 | ##### Setup this script and get the current gh-pages branch. ##### 38 | echo 'Setting up the script...' 39 | # Exit with nonzero exit code if anything fails 40 | set -e 41 | 42 | # Create a clean working directory for this script. 43 | mkdir code_docs 44 | cd code_docs 45 | 46 | # Get the current gh-pages branch 47 | git clone -b gh-pages https://git@$GH_REPO_REF 48 | 49 | cd $GH_REPO_NAME 50 | ##### Configure git. 51 | # Set the push default to simple i.e. push only the current branch. 52 | git config --global push.default simple 53 | # Pretend to be an user called Travis CI. 54 | git config user.name "Travis CI" 55 | git config user.email "travis@eipstackgroup.org" 56 | 57 | # Remove everything currently in the gh-pages branch. 58 | # GitHub is smart enough to know which files have changed and which files have 59 | # stayed the same and will only update the changed files. So the gh-pages branch 60 | # can be safely cleaned, and it is sure that everything pushed later is the new 61 | # documentation. 62 | rm -rf * 63 | 64 | # Need to create a .nojekyll file to allow filenames starting with an underscore 65 | # to be seen on the gh-pages site. Therefore creating an empty .nojekyll file. 66 | # Presumably this is only needed when the SHORT_NAMES option in Doxygen is set 67 | # to NO, which it is by default. So creating the file just in case. 68 | echo "" > .nojekyll 69 | 70 | cd ../.. 71 | 72 | ################################################################################ 73 | ##### Generate the Doxygen code documentation and log the output. ##### 74 | echo 'Generating Doxygen code documentation...' 75 | # Redirect both stderr and stdout to the log file AND the console. 76 | doxygen $DOXYFILE 2>&1 | tee doxygen.log 77 | 78 | cp -r doc/api_doc/html/* code_docs/$GH_REPO_NAME 79 | cd code_docs/$GH_REPO_NAME 80 | 81 | ################################################################################ 82 | ##### Upload the documentation to the gh-pages branch of the repository. ##### 83 | # Only upload if Doxygen successfully created the documentation. 84 | # Check this by verifying that the html directory and the file html/index.html 85 | # both exist. This is a good indication that Doxygen did it's work. 86 | if [ -f "index.html" ]; then 87 | 88 | echo 'Uploading documentation to the gh-pages branch...' 89 | # Add everything in this directory (the Doxygen code documentation) to the 90 | # gh-pages branch. 91 | # GitHub is smart enough to know which files have changed and which files have 92 | # stayed the same and will only update the changed files. 93 | git add --all . 94 | 95 | # Commit the added files with a title and description containing the Travis CI 96 | # build number and the GitHub commit reference that issued this build. 97 | git commit -m "Deploy code docs to GitHub Pages Travis build: ${TRAVIS_BUILD_NUMBER}" -m "Commit: ${TRAVIS_COMMIT}" 98 | 99 | # Force push to the remote gh-pages branch. 100 | # The ouput is redirected to /dev/null to hide any sensitive credential data 101 | # that might otherwise be exposed. 102 | git push --force "https://${GH_REPO_TOKEN}@${GH_REPO_REF}" HEAD:gh-pages > /dev/null 2>&1 103 | else 104 | echo '' >&2 105 | echo 'Warning: No documentation (html) files have been found!' >&2 106 | echo 'Warning: Not going to push the documentation to GitHub!' >&2 107 | exit 1 108 | fi -------------------------------------------------------------------------------- /travis_scripts/installCppUTestDependency.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | echo 'Setting up the CppUTest script...' 4 | # Exit with nonzero exit code if anything fails 5 | set -e 6 | 7 | mkdir dependencies 8 | cd dependencies 9 | 10 | git clone https://github.com/cpputest/cpputest.git 11 | 12 | cd cpputest 13 | git checkout v3.8 14 | cmake CMakeLists.txt 15 | make 16 | cd ../.. 17 | -------------------------------------------------------------------------------- /travis_scripts/linuxAfterSuccessScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'Linux After Success Script started...' 4 | # Exit with nonzero exit code if anything fails 5 | set -e 6 | 7 | cd $TRAVIS_BUILD_DIR/source 8 | chmod +x $TRAVIS_BUILD_DIR/travis_scripts/generateDocumentationAndDeploy.sh 9 | "$TRAVIS_BUILD_DIR/travis_scripts/generateDocumentationAndDeploy.sh" -------------------------------------------------------------------------------- /travis_scripts/linuxBeforeScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'Linux Before Script started...' 4 | # Exit with nonzero exit code if anything fails 5 | set -e 6 | 7 | cd $TRAVIS_BUILD_DIR/source 8 | chmod +x $TRAVIS_BUILD_DIR/travis_scripts/installCppUTestDependency.sh 9 | $TRAVIS_BUILD_DIR/travis_scripts/installCppUTestDependency.sh -------------------------------------------------------------------------------- /travis_scripts/linuxScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'Linux main Script started...' 4 | # Exit with nonzero exit code if anything fails 5 | set -e 6 | 7 | cd $TRAVIS_BUILD_DIR/source 8 | cmake -DOpENer_PLATFORM:STRING="POSIX" -DCMAKE_BUILD_TYPE:STRING="Debug" \ 9 | -DOpENer_TESTS:BOOL=ON -DCPPUTEST_HOME:PATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest \ 10 | -DCPPUTEST_LIBRARY:FILEPATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest/src/CppUTest/libCppUTest.a \ 11 | -DCPPUTESTEXT_LIBRARY:FILEPATH=$TRAVIS_BUILD_DIR/source/dependencies/cpputest/src/CppUTestExt/libCppUTestExt.a . 12 | build-wrapper-linux-x86-64 --out-dir bw-output make all 13 | ctest --output-on-failure 14 | make OpENer_coverage 15 | chmod +x $TRAVIS_BUILD_DIR/travis_scripts/compileGcovResults.sh 16 | $TRAVIS_BUILD_DIR/travis_scripts/compileGcovResults.sh 17 | sonar-scanner -Dproject.settings=$TRAVIS_BUILD_DIR/sonar-project.properties -Dsonar.sources=. -Dsonar.exclusions=OpENer_coverage/**,dependencies/**,CMakeFiles/** -Dsonar.cfamily.gcov.reportsPath=./gcov_results -Dsonar.coverage.exclusions=tests/*,src/ports/**/sample_application/*,tests/**/* 18 | -------------------------------------------------------------------------------- /travis_scripts/windowsMinGWScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo 'Windows MinGW main Script started...' 3 | # Exit with nonzero exit code if anything fails 4 | set -e 5 | 6 | cd $TRAVIS_BUILD_DIR/source 7 | cmake -G "MinGW Makefiles" -DOpENer_PLATFORM:STRING="MINGW" -DCMAKE_BUILD_TYPE:STRING="Debug" -DCMAKE_SH="CMAKE_SH-NOTFOUND" . 8 | mingw32-make 9 | -------------------------------------------------------------------------------- /travis_scripts/windowsScript.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo 'Windows main Script started...' 4 | # Exit with nonzero exit code if anything fails 5 | set -e 6 | 7 | cd $TRAVIS_BUILD_DIR/source 8 | cmake -DOpENer_PLATFORM:STRING="WIN32" -DCMAKE_BUILD_TYPE:STRING="Debug" . 9 | "c:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\MSBuild.exe" OpENer.sln //p:Configuration=Release //p:Platform="Win32" 10 | -------------------------------------------------------------------------------- /uncrustify.cfg: -------------------------------------------------------------------------------- 1 | tok_split_gte=false 2 | utf8_byte=false 3 | utf8_force=false 4 | indent_cmt_with_tabs=false 5 | indent_align_string=false 6 | indent_braces=false 7 | indent_braces_no_func=false 8 | indent_braces_no_class=false 9 | indent_braces_no_struct=false 10 | indent_brace_parent=false 11 | indent_namespace=false 12 | indent_extern=false 13 | indent_class=true 14 | indent_class_colon=false 15 | indent_else_if=false 16 | indent_var_def_cont=false 17 | indent_func_call_param=false 18 | indent_func_def_param=false 19 | indent_func_proto_param=false 20 | indent_func_class_param=false 21 | indent_func_ctor_var_param=false 22 | indent_template_param=false 23 | indent_func_param_double=false 24 | indent_relative_single_line_comments=false 25 | indent_col1_comment=false 26 | indent_access_spec_body=false 27 | indent_paren_nl=false 28 | indent_comma_paren=false 29 | indent_bool_paren=false 30 | indent_first_bool_expr=false 31 | indent_square_nl=false 32 | indent_preserve_sql=false 33 | indent_align_assign=true 34 | sp_balance_nested_parens=true 35 | align_keep_tabs=false 36 | align_with_tabs=false 37 | align_on_tabstop=false 38 | align_number_right=true 39 | align_func_params=false 40 | align_same_func_call_params=false 41 | align_var_def_colon=false 42 | align_var_def_attribute=false 43 | align_var_def_inline=false 44 | align_right_cmt_mix=false 45 | align_on_operator=false 46 | align_mix_var_proto=false 47 | align_single_line_func=false 48 | align_single_line_brace=false 49 | align_nl_cont=false 50 | align_left_shift=true 51 | align_oc_decl_colon=false 52 | nl_collapse_empty_body=false 53 | nl_assign_leave_one_liners=false 54 | nl_class_leave_one_liners=false 55 | nl_enum_leave_one_liners=false 56 | nl_getset_leave_one_liners=false 57 | nl_func_leave_one_liners=false 58 | nl_if_leave_one_liners=false 59 | nl_multi_line_cond=false 60 | nl_multi_line_define=false 61 | nl_before_case=false 62 | nl_after_case=false 63 | nl_after_return=false 64 | nl_after_semicolon=false 65 | nl_after_brace_open=false 66 | nl_after_brace_open_cmt=false 67 | nl_after_vbrace_open=false 68 | nl_after_vbrace_open_empty=false 69 | nl_after_brace_close=false 70 | nl_after_vbrace_close=false 71 | nl_define_macro=false 72 | nl_squeeze_ifdef=false 73 | nl_ds_struct_enum_cmt=false 74 | nl_ds_struct_enum_close_brace=false 75 | nl_create_if_one_liner=false 76 | nl_create_for_one_liner=false 77 | nl_create_while_one_liner=false 78 | ls_for_split_full=false 79 | ls_func_split_full=true 80 | nl_after_multiline_comment=false 81 | eat_blanks_after_open_brace=false 82 | eat_blanks_before_close_brace=false 83 | mod_full_brace_if_chain=false 84 | mod_pawn_semicolon=false 85 | mod_full_paren_if_bool=false 86 | mod_remove_extra_semicolon=true 87 | mod_sort_import=false 88 | mod_sort_using=false 89 | mod_sort_include=false 90 | mod_move_case_break=false 91 | mod_remove_empty_return=false 92 | cmt_indent_multi=true 93 | cmt_c_group=false 94 | cmt_c_nl_start=false 95 | cmt_c_nl_end=false 96 | cmt_cpp_group=false 97 | cmt_cpp_nl_start=false 98 | cmt_cpp_nl_end=false 99 | cmt_cpp_to_c=false 100 | cmt_star_cont=false 101 | cmt_multi_check_last=true 102 | cmt_insert_before_preproc=false 103 | pp_indent_at_level=false 104 | pp_region_indent_code=false 105 | pp_if_indent_code=false 106 | pp_define_at_level=false 107 | indent_columns=2 108 | indent_switch_case=2 109 | code_width=80 110 | utf8_bom=remove 111 | indent_with_tabs=0 112 | sp_arith=add 113 | sp_assign=add 114 | sp_assign_default=add 115 | sp_enum_assign=add 116 | sp_pp_concat=add 117 | sp_pp_stringify=add 118 | sp_before_ptr_star=add 119 | sp_between_ptr_star=remove 120 | sp_after_ptr_star=remove 121 | sp_after_ptr_star_func=remove 122 | sp_func_def_paren=remove 123 | sp_fparen_brace=force 124 | nl_case_colon_brace=remove 125 | nl_class_brace=remove 126 | nl_func_decl_start_single=remove 127 | nl_func_def_start_single=remove 128 | nl_func_decl_args=add 129 | nl_func_def_args=add 130 | nl_func_decl_end_single=remove 131 | nl_func_def_end_single=remove 132 | nl_func_decl_empty=force 133 | nl_func_def_empty=remove 134 | nl_fdef_brace=remove 135 | mod_full_brace_do=add 136 | mod_full_brace_for=add 137 | mod_full_brace_function=add 138 | mod_full_brace_if=add 139 | mod_full_brace_while=add 140 | 141 | --------------------------------------------------------------------------------