├── .gitattributes ├── .gitignore ├── conf ├── astyle └── astylerc ├── format-project.sh ├── docs ├── class-diagram.png ├── test-run-example.png ├── CHANGELOG.md └── class-diagram.puml ├── start-serv.sh ├── ecu-server-start.sh ├── start-client.sh ├── src ├── etc │ ├── helpers │ │ ├── ophelper-config.h │ │ ├── IProcessable.h │ │ ├── static-allocator.h │ │ ├── ophelper.h │ │ └── IKeeper.h │ └── timers │ │ ├── tickerup.cpp │ │ ├── tickerup.h │ │ ├── d-timer.cpp │ │ └── d-timer.h ├── uds │ ├── inc │ │ ├── diag │ │ │ ├── diag.h │ │ │ ├── uds-session.h │ │ │ ├── nrcnames.h │ │ │ ├── routineids.h │ │ │ └── sidnames.h │ │ ├── iso-tp-const.h │ │ ├── typedefs.h │ │ ├── iso-tp-if.h │ │ └── iso-tp-types.h │ ├── session │ │ ├── apps │ │ │ ├── did-router.h │ │ │ ├── did-keeper.h │ │ │ ├── did-handler.h │ │ │ ├── did-router.cpp │ │ │ └── rctrl-router.h │ │ ├── uds-app-client.h │ │ ├── uds-app-manager.h │ │ ├── session-control.h │ │ ├── session-control.cpp │ │ └── uds-app-manager.cpp │ └── isotp │ │ ├── docan-receiver.h │ │ ├── pci-helper.h │ │ ├── docan-tp.cpp │ │ ├── docan-sender.h │ │ ├── docan-tp.h │ │ ├── pci-helper.cpp │ │ ├── docan-receiver.cpp │ │ └── docan-sender.cpp ├── example │ ├── app-helper.h │ ├── iso-tp-server │ │ ├── serv-factory.h │ │ ├── serv-factory.cpp │ │ └── iso-app.h │ ├── uds-test-server │ │ ├── serv-factory.h │ │ ├── session-client.h │ │ ├── test-did-reader.h │ │ ├── test-routine-server.h │ │ ├── cli-client.h │ │ ├── serv-factory.cpp │ │ └── session-client.cpp │ ├── proc-runner.h │ ├── ticker-wrapper.h │ ├── argcollector.h │ ├── app-helper.cpp │ ├── test-siclient.h │ ├── can-bridge.h │ ├── server-app.cpp │ └── main.cpp ├── gtests │ ├── ophelper-be-test.cpp │ ├── ophelper-le-test.cpp │ ├── etc │ │ └── timer-tests.cpp │ └── app-uds-response-allow-test.cpp └── CMakeLists.txt ├── ecu-client-start.sh ├── format-all.sh ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/build* 2 | loc-test* 3 | .vscode 4 | -------------------------------------------------------------------------------- /conf/astyle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astand/uds-to-go/HEAD/conf/astyle -------------------------------------------------------------------------------- /format-project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ./format-all.sh conf/astyle --options=conf/astylerc -------------------------------------------------------------------------------- /docs/class-diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astand/uds-to-go/HEAD/docs/class-diagram.png -------------------------------------------------------------------------------- /docs/test-run-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/astand/uds-to-go/HEAD/docs/test-run-example.png -------------------------------------------------------------------------------- /start-serv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ./build/udstogo-test -iface vcan0 -phys 0x700 -resp 0x701 -blksize 25 3 | -------------------------------------------------------------------------------- /ecu-server-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ./build/ecu-server-test -iface vcan0 -phys 0x710 -resp 0x711 -blksize 25 3 | -------------------------------------------------------------------------------- /start-client.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ./build/udstogo-test -iface vcan0 -phys 0x701 -resp 0x700 -blksize 7 -stmin 50 3 | -------------------------------------------------------------------------------- /src/etc/helpers/ophelper-config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // this file must be removed and substitued by user define 4 | -------------------------------------------------------------------------------- /ecu-client-start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ./build/ecu-server-test -iface vcan0 -phys 0x711 -resp 0x710 -blksize 7 -stmin 50 3 | -------------------------------------------------------------------------------- /src/uds/inc/diag/diag.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "nrcnames.h" 4 | #include "sidnames.h" 5 | #include "uds-session.h" 6 | -------------------------------------------------------------------------------- /src/etc/timers/tickerup.cpp: -------------------------------------------------------------------------------- 1 | #include "tickerup.h" 2 | 3 | Timers::TickerCounter::systick_t Timers::TickerCounter::root_tick_counter{1}; 4 | -------------------------------------------------------------------------------- /src/etc/helpers/IProcessable.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /// @brief Processable interface 4 | class IProcessable { 5 | 6 | public: 7 | /// @brief Run process implementation 8 | virtual void Process() = 0; 9 | }; 10 | -------------------------------------------------------------------------------- /src/example/app-helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | bool set_byte(char c, uint8_t& byte, bool is_high); 8 | 9 | void try_to_set_param(const std::pair& pair, uint32_t& vset); 10 | -------------------------------------------------------------------------------- /src/example/iso-tp-server/serv-factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | SocketCanSender& GetCanSender(); 8 | DoCAN_TP& GetDoCAN(); 9 | IProcessable& GetMainProcHandler(); 10 | 11 | void BuildApp(); 12 | -------------------------------------------------------------------------------- /format-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | RETURN=0 3 | FORMATTER=$1 4 | OPTIONS=$2 5 | 6 | if [ ! -f "$FORMATTER" ]; then 7 | echo "There is no path to the formatter app" >&2 8 | exit 1 9 | fi 10 | 11 | FILES=`find . | grep -P "^(.*\/src).*\.(c|cpp|h)$"` 12 | 13 | for FILE in $FILES; do 14 | $FORMATTER $OPTIONS $FILE 15 | done 16 | 17 | exit $RETURN 18 | -------------------------------------------------------------------------------- /src/uds/inc/iso-tp-const.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "typedefs.h" 4 | 5 | constexpr datasize_t MIN_CANDL = 8; 6 | constexpr datasize_t MAX_CANDL = 64; 7 | constexpr datasize_t MIN_FF_CANDL = 8; 8 | 9 | constexpr datasize_t CLASSICCAN_DL_MAX = 8; 10 | constexpr datasize_t CANFD_DL_MAX = 64; 11 | 12 | constexpr datasize_t MIN_FF_PAYLOAD_SIZE = 8; 13 | -------------------------------------------------------------------------------- /src/uds/inc/typedefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | typedef uint32_t datasize_t; 6 | typedef uint8_t enumbase_t; 7 | 8 | template 9 | static constexpr uint8_t to_byte(T t) { 10 | 11 | return static_cast(t); 12 | } 13 | 14 | template 15 | static constexpr T from_byte(uint8_t b) { 16 | 17 | return static_cast(b); 18 | } 19 | -------------------------------------------------------------------------------- /src/example/uds-test-server/serv-factory.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | SocketCanSender& GetCanSender(); 9 | UdsAppManager& GetBaseUdsServer(); 10 | DoCAN_TP& GetDoCAN(); 11 | IProcessable& GetMainProcHandler(); 12 | CliMen& GetClientUds(); 13 | 14 | void BuildApp(); 15 | -------------------------------------------------------------------------------- /src/example/proc-runner.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | template 7 | class ProcRunner : public MemAsKeeper { 8 | 9 | public: 10 | ProcRunner() : MemAsKeeper() { 11 | assert(N != 0); 12 | } 13 | 14 | virtual void Process() override { 15 | IProcessable* proc{nullptr}; 16 | 17 | for (size_t i = 0; i < this->Count(); i++) { 18 | if (this->TryReadElem(i, proc)) { 19 | proc->Process(); 20 | } 21 | } 22 | } 23 | }; -------------------------------------------------------------------------------- /docs/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # UDS TO GO 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [v1.0.0] - 2023-04-13 11 | 12 | ### Added 13 | 14 | - New semantic first release 15 | 16 | 17 | [Unreleased]: https://github.com/astand/uds-to-go/compare/v1.0.0...HEAD 18 | [v1.0.0]: https://github.com/astand/uds-to-go/releases/tag/v1.0.0 19 | -------------------------------------------------------------------------------- /conf/astylerc: -------------------------------------------------------------------------------- 1 | --suffix=none 2 | --mode=c 3 | --style=java 4 | --attach-inlines 5 | --indent-switches 6 | --indent-preproc-define 7 | --indent=spaces=2 8 | --break-blocks 9 | --func-empty-line 10 | --class-empty-line 11 | --pad-oper 12 | --pad-header 13 | --pad-paren-joint 14 | --indent-after-parens 15 | --indent-continuation=2 16 | --min-conditional-indent=0 17 | --align-pointer=type 18 | --align-reference=type 19 | --align-pointer-cast-type 20 | --indent-modifiers 21 | --attach-classes 22 | --break-after-logical 23 | --keep-one-line-statements 24 | --indent-after-parens 25 | --add-braces 26 | --max-code-length=180 27 | --max-continuation-indent=120 -------------------------------------------------------------------------------- /src/example/ticker-wrapper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | class TickerWrapper : public IProcessable { 9 | 10 | virtual void Process() override { 11 | static auto first_stamp = std::chrono::steady_clock::now(); 12 | 13 | auto now_stamp = std::chrono::steady_clock::now(); 14 | auto elapsed_us = std::chrono::duration_cast(now_stamp - first_stamp).count(); 15 | 16 | while (elapsed_us > 1000) { 17 | elapsed_us -= 1000; 18 | first_stamp += std::chrono::microseconds(1000); 19 | 20 | Timers::TickerCounter::ProcessTick(); 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /src/gtests/ophelper-be-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define SYS_MEMORY_BE 5 | 6 | #include 7 | 8 | // Demonstrate some basic assertions. 9 | TEST(BigEndianessTest, GeneralTests) { 10 | 11 | EXPECT_EQ(ophelper::from_be_u16(0xcdab), 0xcdab); 12 | EXPECT_EQ(ophelper::to_be_u16(0xcdab), 0xcdab); 13 | EXPECT_EQ(ophelper::from_le_u16(0xcdab), 0xabcd); 14 | EXPECT_EQ(ophelper::to_le_u16(0xcdab), 0xabcd); 15 | 16 | EXPECT_EQ(ophelper::from_be_u32(0x11223344), 0x11223344); 17 | EXPECT_EQ(ophelper::to_be_u32(0x11223344), 0x11223344); 18 | EXPECT_EQ(ophelper::from_le_u32(0x11223344), 0x44332211); 19 | EXPECT_EQ(ophelper::to_le_u32(0x11223344), 0x44332211); 20 | } 21 | -------------------------------------------------------------------------------- /src/uds/session/apps/did-router.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "../uds-app-manager.h" 6 | #include "../uds-app-client.h" 7 | 8 | class DidHandler; 9 | 10 | class DidRouter : public UdsAppClient { 11 | 12 | public: 13 | DidRouter(UdsAppManager& r, DidHandler& didhandler) : UdsAppClient(r), dider(didhandler) {} 14 | virtual bool IsServiceSupported(sid_t sid, size_t& minlength, bool& subfunc) override; 15 | virtual ProcessResult OnAppIndication(const IndicationInfo& inf) override; 16 | virtual void OnAppConfirmation(S_Result res) override; 17 | 18 | protected: 19 | const uint8_t* data_; 20 | uint32_t len_; 21 | 22 | private: 23 | void ReadDataByIdentifierHandler(); 24 | void WriteDataByIdentifierHandler(); 25 | DidHandler& dider; 26 | }; 27 | -------------------------------------------------------------------------------- /src/example/argcollector.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using onepair = std::pair; 8 | using argsret = std::vector; 9 | 10 | argsret collectargs(int argc, char** argv) { 11 | 12 | argsret ret {0}; 13 | onepair param; 14 | 15 | for (int i = 0; i < argc; i++) { 16 | // key found (must start with '-' (e.g. '-dbc')) 17 | if (argv[i][0] == '-') { 18 | param.first = std::string(argv[i]); 19 | param.second.clear(); 20 | 21 | if ((i + 1) < argc && argv[i + 1][0] != '-') { 22 | // key param 23 | param.second = std::string(argv[i + 1]); 24 | // unlooped i incremention 25 | ++i; 26 | } 27 | 28 | ret.push_back(param); 29 | } 30 | } 31 | 32 | return ret; 33 | } 34 | -------------------------------------------------------------------------------- /src/example/uds-test-server/session-client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class DSCClient : public UdsAppClient { 6 | 7 | public: 8 | DSCClient(UdsAppManager& router_, SessionInfo& sessInfo) : 9 | UdsAppClient(router_), sessionInfoContext(sessInfo) { 10 | 11 | sessionInfoContext.currSession = 1; 12 | sessionInfoContext.secLevel = 0u; 13 | } 14 | 15 | virtual bool IsServiceSupported(sid_t sid, size_t& minlength, bool& subfunc) override; 16 | 17 | virtual ProcessResult OnAppIndication(const IndicationInfo& inf) override; 18 | 19 | virtual void OnAppConfirmation(S_Result) override {} 20 | 21 | virtual void OnSessionChange(bool isdefault) override; 22 | 23 | private: 24 | 25 | ProcessResult Handle_SA_request(const IndicationInfo& rcont); 26 | ProcessResult Handle_SA_response(const IndicationInfo& rcont); 27 | 28 | SessionInfo& sessionInfoContext; 29 | uint16_t seedsent{0}; 30 | }; 31 | -------------------------------------------------------------------------------- /src/gtests/ophelper-le-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | // run tests only if the GeneralTests body changes 5 | // disable BE for ophelper-be-test at the same time 6 | // after tests successfully done, disable LE back again 7 | #define ONLY_LITTLE_ENDIAN 0 8 | 9 | #if ONLY_LITTLE_ENDIAN 10 | #define SYS_MEMORY_LE 11 | #endif 12 | 13 | #include 14 | 15 | // Demonstrate some basic assertions. 16 | TEST(LittleEndianessTest, GeneralTests) { 17 | 18 | #if ONLY_LITTLE_ENDIAN 19 | EXPECT_EQ(ophelper::from_le_u16(0xcdab), 0xcdab); 20 | EXPECT_EQ(ophelper::to_le_u16(0xcdab), 0xcdab); 21 | EXPECT_EQ(ophelper::from_be_u16(0xcdab), 0xabcd); 22 | EXPECT_EQ(ophelper::to_be_u16(0xcdab), 0xabcd); 23 | 24 | EXPECT_EQ(ophelper::from_le_u32(0x11223344), 0x11223344); 25 | EXPECT_EQ(ophelper::to_le_u32(0x11223344), 0x11223344); 26 | EXPECT_EQ(ophelper::from_be_u32(0x11223344), 0x44332211); 27 | EXPECT_EQ(ophelper::to_be_u32(0x11223344), 0x44332211); 28 | #endif 29 | } 30 | -------------------------------------------------------------------------------- /src/example/app-helper.cpp: -------------------------------------------------------------------------------- 1 | #include "app-helper.h" 2 | #include 3 | 4 | 5 | bool set_byte(char c, uint8_t& byte, bool is_high) { 6 | 7 | uint8_t value = 0u; 8 | 9 | if (c >= '0' && c <= '9') { 10 | value = c - '0'; 11 | } else if (c >= 'a' && c <= 'f') { 12 | value = (c - 'a') + 10; 13 | } else if (c >= 'A' && c <= 'F') { 14 | value = (c - 'A') + 10; 15 | } else { 16 | return false; 17 | } 18 | 19 | if (is_high) { 20 | byte = value << 4; 21 | } else { 22 | byte |= value & 0x0fu; 23 | } 24 | 25 | return true; 26 | } 27 | 28 | void try_to_set_param(const std::pair& pair, uint32_t& vset) { 29 | 30 | uint32_t scaned = 0u; 31 | std::string frmt = "%u"; 32 | 33 | if (pair.second.size() > 2 && pair.second.at(1) == 'x') { 34 | frmt = "%x"; 35 | } 36 | 37 | if (sscanf(pair.second.c_str(), frmt.c_str(), &scaned) != 1) { 38 | std::cout << "Wrong value (" << pair.second << ") for parameter '" << pair.first << "'"; 39 | } else { 40 | vset = scaned; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Astakhov Andrey 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/etc/helpers/static-allocator.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /// @brief Wrapper for statically allocated memory buffer 6 | /// @tparam T Type of the elements for storing over allocated memory buffer 7 | /// @tparam N Elements capacity 8 | template 9 | class StaticMemAllocator { 10 | 11 | public: 12 | /// @brief Memory begin address getter 13 | /// @return Pointer to allocated memory 14 | uint8_t* ptr() { 15 | return static_cast(__raw__); 16 | } 17 | 18 | /// @brief Buffer size in bytes 19 | constexpr size_t Size() { 20 | return N * sizeof(T); 21 | } 22 | 23 | /// @brief Constructor 24 | /// @tparam T Type of the elements for storing over allocated memory buffer 25 | /// @tparam N Elements capacity 26 | constexpr StaticMemAllocator() = default; 27 | 28 | /// @brief Copy constructor is deleted 29 | StaticMemAllocator(const StaticMemAllocator&) = delete; 30 | 31 | /// @brief Assignment operator is deleted 32 | StaticMemAllocator& operator=(const StaticMemAllocator&) = delete; 33 | 34 | private: 35 | /// @brief Raw memory array 36 | uint8_t __raw__[N * sizeof(T)] = {0}; 37 | }; 38 | -------------------------------------------------------------------------------- /src/uds/session/apps/did-keeper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "did-handler.h" 6 | 7 | template 8 | class DidKeeper : public MemAsKeeper { 9 | 10 | public: 11 | DidKeeper() : MemAsKeeper() {} 12 | 13 | virtual DidResult ReadDID(uint32_t did, uint8_t* data, size_t capacity, size_t& len_out, NRCs& nrc_out) override { 14 | DidResult ret = DidResult::Ignored; 15 | 16 | uint32_t i = 0u; 17 | DidHandler* refdid {nullptr}; 18 | 19 | while (this->TryReadElem(i, refdid)) { 20 | ret = refdid->ReadDID(did, data, capacity, len_out, nrc_out); 21 | 22 | if (ret != DidResult::Ignored) { 23 | break; 24 | } 25 | } 26 | 27 | return ret; 28 | } 29 | 30 | virtual DidResult WriteDID(uint32_t did, const uint8_t* data, size_t len, NRCs& nrc_out) override { 31 | DidResult ret = DidResult::Ignored; 32 | uint32_t i = 0u; 33 | DidHandler* refdid {nullptr}; 34 | 35 | while (this->TryReadElem(i, refdid)) { 36 | ret = refdid->WriteDID(did, data, len, nrc_out); 37 | 38 | if (ret != DidResult::Ignored) { 39 | break; 40 | } 41 | } 42 | 43 | return ret; 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /src/etc/timers/tickerup.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace Timers { 6 | 7 | /// @brief Base static class to process base timers counter 8 | class TickerCounter { 9 | 10 | public: 11 | /// @brief tick counter type 12 | using systick_t = volatile uint32_t; 13 | 14 | /// @brief Tick counter increment call 15 | static void ProcessTick() { 16 | 17 | ++root_tick_counter; 18 | } 19 | 20 | /// @brief Read root ticker counter 21 | /// @return root ticker counter value 22 | static systick_t ReadRootTick() { 23 | 24 | return root_tick_counter; 25 | } 26 | 27 | private: 28 | /// @brief counter 29 | static systick_t root_tick_counter; 30 | }; 31 | 32 | /// @brief Intermediate wrapper for all users of TickerCounter with 33 | /// ability to make Atomic operation, if Atomic argument will 34 | /// make atomic state enabled in its constructor and disable it 35 | /// back in desctructor 36 | /// @tparam Atomic RAII type to fulfill atomicy 37 | template 38 | class TickerUp : public TickerCounter { 39 | 40 | protected: 41 | static systick_t now() { 42 | 43 | Atomic guard; 44 | (void) guard; 45 | 46 | return ReadRootTick(); 47 | } 48 | 49 | TickerUp() = default; 50 | }; 51 | 52 | } // namespace Timers 53 | -------------------------------------------------------------------------------- /src/example/uds-test-server/test-did-reader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class TestDidReader : public DidHandler { 6 | 7 | public: 8 | TestDidReader(const SessionInfo& infoInstance) : sessInfoInstance(infoInstance) {} 9 | 10 | virtual DidResult ReadDID(uint32_t did, uint8_t* data, size_t, size_t& len_out, NRCs&) override { 11 | 12 | DidResult handleResult = DidResult::Positive; 13 | 14 | switch (did) { 15 | case (0x2200u): 16 | data[0] = 1; 17 | data[1] = 2; 18 | data[2] = 3; 19 | data[3] = 4; 20 | data[4] = 5; 21 | data[5] = 6; 22 | data[6] = 7; 23 | len_out = 7; 24 | break; 25 | 26 | case (0x00A0u): 27 | data[0] = sessInfoInstance.currSession; 28 | len_out = 1u; 29 | break; 30 | 31 | case (0x00A1u): 32 | data[0] = sessInfoInstance.secLevel; 33 | len_out = 1u; 34 | break; 35 | 36 | default: 37 | handleResult = DidResult::Ignored; 38 | break; 39 | } 40 | 41 | return handleResult; 42 | } 43 | 44 | virtual DidResult WriteDID(uint32_t, const uint8_t*, size_t, NRCs&) override { 45 | 46 | return DidResult::Ignored; 47 | } 48 | 49 | private: 50 | const SessionInfo& sessInfoInstance; 51 | 52 | }; 53 | -------------------------------------------------------------------------------- /src/etc/timers/d-timer.cpp: -------------------------------------------------------------------------------- 1 | #include "d-timer.h" 2 | 3 | namespace DTimers { 4 | 5 | Timer::Timer(interval_t intv, bool start, bool repeat) 6 | : tick_period(intv), tick_start(0), is_active(start), is_endless(repeat) { 7 | 8 | if (is_active) { 9 | // if is active try to Restart immediately 10 | Restart(); 11 | } 12 | } 13 | 14 | void Timer::Start(interval_t interval) { 15 | 16 | tick_period = interval; 17 | SetStartTick(now()); 18 | is_active = true; 19 | } 20 | 21 | void Timer::Start(interval_t interval, bool repeat) { 22 | 23 | Start(interval); 24 | is_endless = repeat; 25 | } 26 | 27 | void Timer::Restart() { 28 | 29 | Start(tick_period); 30 | } 31 | 32 | bool Timer::Elapsed() { 33 | 34 | if (IsActive()) { 35 | if (tick_period == 0) { 36 | is_active = is_endless; 37 | return true; 38 | } else { 39 | systick_t liveticks = now(); 40 | 41 | if (GetTicksToNextElapse(liveticks) == 0) { 42 | is_active = is_endless; 43 | SetStartTick(liveticks); 44 | return true; 45 | } 46 | } 47 | } 48 | 49 | return false; 50 | } 51 | 52 | 53 | uint32_t Timer::Ticks() const { 54 | 55 | if (IsActive()) { 56 | return static_cast(GetTicksToNextElapse(now())); 57 | } else { 58 | return 0; 59 | } 60 | } 61 | 62 | } // namespace 63 | -------------------------------------------------------------------------------- /src/example/iso-tp-server/serv-factory.cpp: -------------------------------------------------------------------------------- 1 | #include "serv-factory.h" 2 | #include "iso-app.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | constexpr size_t RxBufferSize = 8192; 15 | constexpr size_t TxBufferSize = 8192; 16 | 17 | SocketCanSender& GetCanSender() { 18 | 19 | static SocketCanSender sender; 20 | return sender; 21 | } 22 | 23 | DoCAN_TP& GetDoCAN() { 24 | 25 | static IsoApp isoapp; 26 | static DoCAN_TP_Mem isotpsource(GetCanSender(), isoapp); 27 | 28 | return isotpsource; 29 | } 30 | 31 | static ProcRunner<4>& GetProcRunner() { 32 | 33 | static ProcRunner<4> procrunner; 34 | return procrunner; 35 | } 36 | 37 | IProcessable& GetMainProcHandler() { 38 | 39 | return GetProcRunner(); 40 | } 41 | 42 | void BuildApp() { 43 | 44 | static TickerWrapper ticker; 45 | 46 | GetProcRunner().Add(&ticker); 47 | GetProcRunner().Add(&GetDoCAN()); 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/uds/session/apps/did-handler.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | 6 | //------------------------------------------------------------------------------ 7 | // Result of handling 8 | //------------------------------------------------------------------------------ 9 | enum class DidResult { 10 | 11 | // DID request is not processed and another attempt can be performed using another DIDHandler class 12 | Ignored, 13 | 14 | // DID request is already handled with positive response 15 | Positive, 16 | 17 | // DID request is already handled with negative response 18 | Negative, 19 | }; 20 | 21 | //------------------------------------------------------------------------------ 22 | //------------------------------------------------------------------------------ 23 | class DidHandler { 24 | 25 | public: 26 | // DID not supported: returned DidResult::Ignored 27 | // DID pos handling: returned DidResult::Positive, len_out is filled and > 0 28 | // DID neg handling: returned DidResult::Negative, nrc_out is filled 29 | virtual DidResult ReadDID(uint32_t did, uint8_t* data, size_t capacity, size_t& len_out, NRCs& nrc_out) = 0; 30 | 31 | // DID not supported: returned DidResult::Ignored 32 | // DID pos handling: returned DidResult::Positive 33 | // DID neg handling: returned DidResult::Negative, nrc_out is filled 34 | virtual DidResult WriteDID(uint32_t did, const uint8_t* data, size_t len, NRCs& nrc_out) = 0; 35 | }; 36 | -------------------------------------------------------------------------------- /src/example/test-siclient.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class ProxyUdsAppClient : public UdsAppClient { 6 | 7 | public: 8 | ProxyUdsAppClient(UdsAppManager& router, UdsAppClient& handler) : 9 | UdsAppClient(router), realHandler(handler) {} 10 | 11 | virtual bool IsServiceSupported(sid_t sid, size_t& minlength, bool& subfunc) { 12 | 13 | return realHandler.IsServiceSupported(sid, minlength, subfunc); 14 | } 15 | 16 | virtual ProcessResult OnAppIndication(const IndicationInfo& inf) override { 17 | 18 | std::cout << "SC : On Ind -> " << "Addr: " << ((inf.addr == TargetAddressType::PHYS) ? ("PHYS ") : ("FUNC ")); 19 | std::cout << " -> SI [" << (int) inf.head.SI << "] SF [" << (int) inf.head.SF << "]"; 20 | std::cout << "Data size = " << inf.size << " b." << std::endl; 21 | std::cout << std::endl; 22 | 23 | return realHandler.OnAppIndication(inf); 24 | } 25 | 26 | 27 | virtual void OnAppConfirmation(S_Result res) override { 28 | 29 | std::cout << "SC : On Conf -> [" << ((res == S_Result::OK) ? (" OK ") : (" ! NOK ")) << "]"; 30 | std::cout << std::endl; 31 | 32 | return realHandler.OnAppConfirmation(res); 33 | } 34 | 35 | 36 | virtual void OnSessionChange(bool s3timer_event) override { 37 | 38 | std::cout << ((s3timer_event) ? (" s3 ") : (" -- ")); 39 | realHandler.OnSessionChange(s3timer_event); 40 | std::cout << "Session update :" << (int) udsRouter.GetSession().currSession; 41 | std::cout << std::endl; 42 | } 43 | 44 | private: 45 | UdsAppClient& realHandler; 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /src/uds/inc/iso-tp-if.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "iso-tp-types.h" 4 | 5 | /// @brief ISO TP client interface. ISO TP handler calls this interface 6 | /// on each ISO TP event (confirmation, indication etc) 7 | class IsoTpClient { 8 | 9 | public: 10 | /// @brief ISO TP event context information descriptor 11 | typedef struct { 12 | const uint8_t* data; 13 | size_t length; 14 | N_TarAddress address; 15 | } IsoTpInfo; 16 | 17 | /// @brief ISO TP event notification 18 | /// @param ev event type 19 | /// @param res network event result 20 | /// @param info context information 21 | virtual void OnIsoEvent(N_Event ev, N_Result res, const IsoTpInfo& info) = 0; 22 | }; 23 | 24 | /// @brief ISO TP implementation main interface. Clients use this 25 | /// interface to invoke payload transmittion 26 | class IsoTpImpl { 27 | 28 | public: 29 | 30 | /// @brief ISO TP request 31 | /// @param data pointer to ISO TP payload 32 | /// @param length size of ISO TP payload 33 | virtual IsoTpResult Request(const uint8_t* data, size_t length) = 0; 34 | }; 35 | 36 | /// @brief CAN message receiver interface 37 | class ICAN_Listener { 38 | 39 | public: 40 | 41 | /// @brief Callback on CAN message receiving 42 | /// @param data pointer to CAN message data 43 | /// @param length CAN message payload size 44 | /// @param msgid CAN message ID 45 | virtual void ReadFrame(const uint8_t* data, size_t length, uint32_t msgid) = 0; 46 | }; 47 | 48 | /// @brief CAN message sender interface 49 | class ICAN_Sender { 50 | 51 | public: 52 | 53 | /// @brief Sends CAN message to CAN line 54 | /// @param data pointer to CAN message payload 55 | /// @param length size of CAN message payload 56 | /// @param msgid CAN message ID 57 | /// @return length if OK, 0 if failed 58 | virtual size_t SendFrame(const uint8_t* data, size_t length, uint32_t msgid) = 0; 59 | }; 60 | -------------------------------------------------------------------------------- /src/uds/inc/diag/uds-session.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "sidnames.h" 5 | 6 | /// @brief Target address type (physical, functional) 7 | enum class TargetAddressType { 8 | 9 | PHYS, FUNC, UNKNOWN 10 | }; 11 | 12 | /// @brief Session type (default, non-default) 13 | enum class SessionType { 14 | 15 | DEFAULT, NONDEFAULT 16 | }; 17 | 18 | /// @brief Session processing result (OK, notOK) 19 | enum class S_Result { 20 | 21 | OK, NOK 22 | }; 23 | 24 | /// @brief Session parameter set result 25 | enum class SessParamResult { 26 | 27 | OK, ERR 28 | }; 29 | 30 | /// @brief Session parameter collection 31 | enum class SessParamType { 32 | 33 | S3_TIM, P2_TIM, P2_ENHC 34 | }; 35 | 36 | /// @brief Service processing result 37 | enum class ProcessResult { 38 | 39 | /// @brief Request not handled 40 | NOT_HANDLED, 41 | 42 | /// @brief Request handled, response must be sent 43 | HANDLED_RESP_OK, 44 | 45 | /// @brief Request handled, response must not be sent 46 | HANDLED_RESP_NO, 47 | 48 | /// @brief Request handled, session pending mode must be invoked 49 | HANDLED_PENDING 50 | }; 51 | 52 | /// @brief Send request result 53 | enum class SendResult { 54 | OK, OVRF, ERROR 55 | }; 56 | 57 | typedef struct { 58 | 59 | /// @brief Service identifier 60 | sid_t SI; 61 | /// @brief Sub-function code 62 | uint8_t SF; 63 | /// @brief Suppress positive response indicator 64 | bool suppressPosResponse; 65 | } RequestContext; 66 | 67 | 68 | typedef struct { 69 | 70 | /// @brief Pointer to payload data 71 | const uint8_t* data; 72 | /// @brief Data size 73 | uint32_t size; 74 | /// @brief Requested target address 75 | TargetAddressType addr; 76 | /// @brief Request service information 77 | RequestContext head; 78 | } IndicationInfo; 79 | 80 | /// @brief Session state descriptor 81 | typedef struct { 82 | 83 | /// @brief Current session integer value 84 | uint8_t currSession; 85 | /// @brief Current session security level 86 | uint8_t secLevel; 87 | } SessionInfo; 88 | -------------------------------------------------------------------------------- /src/example/iso-tp-server/iso-app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | /* ---------------------------------------------------------------------------- */ 8 | class IsoApp : public IsoTpClient { 9 | 10 | public: 11 | void OnIsoEvent(N_Event t, N_Result res, const IsoTpInfo& inf) { 12 | 13 | assert((uint8_t)t < 3 && (uint8_t)res < 13); 14 | 15 | std::cout << "Event [" << type_names[(uint8_t)t] << "] Status [" << res_names[(uint8_t)res] << "]"; 16 | 17 | if (t == N_Event::Data && res == N_Result::OK_r) { 18 | // print data 19 | std::cout << std::endl << "------------------------------------------------" << std::endl; 20 | // "00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff "; 21 | assert(inf.data != nullptr && inf.length != 0u); 22 | 23 | std::cout << std::hex; 24 | 25 | for (size_t i = 0; i < inf.length; i++) { 26 | if (i != 0 && (i & 0x0f) == 0u) { 27 | std::cout << std::endl; 28 | } 29 | 30 | if (inf.data[i] < 0x10u) { 31 | std::cout << "0"; 32 | } 33 | 34 | std::cout << (int)inf.data[i] << " "; 35 | } 36 | 37 | std::cout << std::endl << "------------------------------------------------" << std::dec; 38 | std::cout << std::endl << " <--- RECV OK: " << inf.length << " bytes."; 39 | } else if (t == N_Event::DataFF && res == N_Result::OK_r) { 40 | std::cout << " Expected size = " << inf.length << " bytes."; 41 | } 42 | 43 | std::cout << std::endl; 44 | } 45 | 46 | private: 47 | const char* type_names[3] = { 48 | "Conf ", 49 | "Data ", 50 | "DataFF" 51 | }; 52 | 53 | const char* res_names[13] = { 54 | "OK_s", 55 | "OK_r", 56 | "TIMEOUT_As", 57 | "TIMEOUT_Ar", 58 | "TIMEOUT_Bs", 59 | "TIMEOUT_Cr", 60 | "WRONG_SN", 61 | "INVALID_FS", 62 | "UNEXP_PDU", 63 | "WFT_OVRN", 64 | "BUFFER_OVFLW", 65 | "ERROR_s", 66 | "ERROR_r" 67 | }; 68 | }; 69 | 70 | -------------------------------------------------------------------------------- /src/uds/isotp/docan-receiver.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../inc/iso-tp-types.h" 5 | #include "../inc/iso-tp-const.h" 6 | 7 | class DoCAN_TP; 8 | 9 | /// @brief DoCAN receiver class 10 | class DoCAN_Receiver { 11 | 12 | public: 13 | /// @brief DoCAN receiver constructor 14 | /// @param mem pointer to buffer for income paylaod 15 | /// @param bufcap buffer maximum 16 | /// @param isotp reference to iso-tp host object 17 | DoCAN_Receiver(uint8_t* mem, const size_t bufcap, DoCAN_TP& isotp) : rxbuff(mem), RXLEN(bufcap), itp(isotp) {} 18 | 19 | /// @brief General receiver handler 20 | void ProcessRx(); 21 | 22 | /// @brief DoCAN single payload receiver 23 | /// @param data pointer to data from CAN 24 | /// @param candl length of CAN data 25 | void Receive(const uint8_t* data, datasize_t candl); 26 | 27 | /// @brief Sets receiver params 28 | /// @param name name of parameter to set 29 | /// @param v parameter value 30 | /// @return status of setting parameter 31 | SetParamResult SetParameter(ParName name, uint32_t v); 32 | 33 | /// @brief Checks if receiver is busy 34 | /// @return busy status 35 | bool IsBusy() const { 36 | return rxds.state != RxState::IDLE; 37 | } 38 | 39 | private: 40 | /// @brief internal pointer to rx buffer 41 | uint8_t* const rxbuff; 42 | /// @brief rx buffer capacity 43 | const size_t RXLEN; 44 | 45 | /// @brief DoCAN host reference 46 | DoCAN_TP& itp; 47 | 48 | enum class RxState { 49 | IDLE, ACTIVE 50 | }; 51 | 52 | struct RxDescriptor { 53 | RxState state{RxState::IDLE}; 54 | 55 | datasize_t rxsize{0}; 56 | datasize_t passed{0}; 57 | 58 | uint8_t expectsn{0}; 59 | uint8_t currblkn{0}; 60 | uint8_t blksize{16}; 61 | uint8_t stmin{0}; 62 | }; 63 | 64 | /// @brief internal state descriptor 65 | RxDescriptor rxds{}; 66 | 67 | /// @brief can message buffer 68 | uint8_t can_message[MAX_CANDL]; 69 | 70 | /// @brief Cr timer 71 | DTimers::Timer Cr_tim{1000, false, false}; 72 | 73 | }; 74 | 75 | -------------------------------------------------------------------------------- /src/uds/isotp/pci-helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "../inc/typedefs.h" 7 | 8 | /// @brief DoCAN frame type 9 | enum class DC_FrameType { 10 | 11 | SF = 0, FF = 0x10, FC = 0x30, CF = 0x20, ERROR = 0xffu 12 | }; 13 | 14 | /// @brief Segmented transaction state 15 | enum class DC_FlowState { 16 | 17 | CTS = 0, WAIT = 1, OVERFLOW = 2 18 | }; 19 | 20 | /// @brief Helper class for DoCAN protocol information handling 21 | class PciHelper { 22 | 23 | public: 24 | typedef struct { 25 | datasize_t pcilen; 26 | // SF, FF 27 | DC_FrameType type; 28 | datasize_t dlen; 29 | // FC 30 | DC_FlowState flowstate; 31 | uint8_t bs; 32 | uint8_t stmin; 33 | // CF 34 | uint8_t sn; 35 | } PciMeta; 36 | 37 | public: 38 | 39 | /// @brief Extracts protocol control info from raw message payload 40 | /// @param data pointer to message payload 41 | /// @param candl payload size 42 | /// @param pci out reference to pci variable 43 | /// @return pci info size 44 | datasize_t UnpackPciInfo(const uint8_t* data, datasize_t candl, PciMeta& pci); 45 | 46 | /// @brief Packs protocol control information for general data packet 47 | /// @param data pointer to message payload (minimal length is 8) 48 | /// @param length data size 49 | /// @param candl CAN message data length 50 | /// @param reftype out reference to pci packet type 51 | /// @return pci info size 52 | datasize_t PackPciForData(uint8_t* data, datasize_t length, datasize_t candl, DC_FrameType& reftype); 53 | 54 | /// @brief Packs protocol control information for flow control packet 55 | /// @param data pointer to message payload (minimal length is 3) 56 | /// @param state state of flow 57 | /// @param bs block size 58 | /// @param stmin st min 59 | /// @return pci info size 60 | datasize_t PackFlowControl(uint8_t* data, DC_FlowState state, uint8_t bs, uint8_t stmin); 61 | 62 | /// @brief Packs protocol control information consequitive frame 63 | /// @param data pointer to message payload (minimal length is 1) 64 | /// @param sn current serial number 65 | /// @return pci info size 66 | datasize_t PackConseqFrame(uint8_t* data, uint8_t sn); 67 | }; 68 | -------------------------------------------------------------------------------- /src/example/uds-test-server/test-routine-server.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class ClientRoutineBase : public RoutineHandler { 6 | 7 | public: 8 | ClientRoutineBase(RoutineRouter& b) : routman(b) {} 9 | 10 | protected: 11 | RoutineRouter& routman; 12 | }; 13 | 14 | class RotineServ1 : public ClientRoutineBase, public IProcessable { 15 | 16 | public: 17 | RotineServ1(RoutineRouter& routiner) : ClientRoutineBase(routiner) {} 18 | 19 | ProcessResult OnRoutine(routine_id_t rid, uint8_t, const uint8_t*, size_t) { 20 | 21 | auto ret = ProcessResult::NOT_HANDLED; 22 | 23 | if (rid == 0x0102) { 24 | if (true) { 25 | pending = true; 26 | // restart routing execution timer 27 | timeout.Restart(); 28 | // return HANDLED_PENDING status to inform host that final response will be sent later 29 | ret = ProcessResult::HANDLED_PENDING; 30 | } else { 31 | routman.SendRoutineNegResponse(NRCs::IMLOIF); 32 | } 33 | } 34 | 35 | return ret; 36 | } 37 | 38 | virtual void Process() override { 39 | 40 | if (pending && timeout.Elapsed()) { 41 | pending = false; 42 | timeout.Stop(); 43 | 44 | rcontext.id = 0x0111; 45 | rcontext.info = 1; 46 | rcontext.type = 2; 47 | routman.SendRoutineResponse(rcontext); 48 | } 49 | } 50 | 51 | private: 52 | 53 | /// @brief timer for simulating 15s routine execution 54 | DTimers::Timer timeout{15000u}; 55 | bool pending{false}; 56 | }; 57 | 58 | class RotineServ2 : public ClientRoutineBase { 59 | 60 | public: 61 | RotineServ2(RoutineRouter& routiner) : ClientRoutineBase(routiner) {} 62 | 63 | ProcessResult OnRoutine(routine_id_t rid, uint8_t, const uint8_t*, size_t) { 64 | 65 | if (rid == 0xff00) { 66 | if (true) { 67 | // Ok 68 | rcontext.id = 0xff00; 69 | rcontext.info = 99; 70 | rcontext.type = 64; 71 | routman.SendRoutineResponse(rcontext); 72 | return ProcessResult::HANDLED_RESP_NO; 73 | } else { 74 | routman.SendRoutineNegResponse(NRCs::IMLOIF); 75 | return ProcessResult::HANDLED_RESP_NO; 76 | } 77 | } 78 | 79 | return ProcessResult::NOT_HANDLED; 80 | } 81 | }; 82 | -------------------------------------------------------------------------------- /docs/class-diagram.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | 4 | package UDS <> { 5 | 6 | interface IProcessable { 7 | +void {abstract} Process() 8 | } 9 | 10 | interface IsoTpImpl { 11 | +IsoTpResult {abstract} Request() 12 | } 13 | 14 | interface ICAN_Listener{ 15 | +void {abstract} ReadFrame() 16 | } 17 | 18 | interface ICAN_Sender { 19 | +size_t {abstract} SendFrame() 20 | } 21 | 22 | interface IsoTpClient { 23 | +void {abstract} OnIsoEvent() 24 | } 25 | 26 | interface IDidHandler { 27 | +void {abstract} ReadDID() 28 | +void {abstract} WriteDID() 29 | } 30 | 31 | interface IRoutineHandler { 32 | +void {abstract} OnRoutine() 33 | } 34 | 35 | class DoCAN_TP { 36 | +void Process() 37 | +void ReadFrame() 38 | +IsoTpResult Request() 39 | } 40 | 41 | class UdsServerBase { 42 | } 43 | 44 | abstract SessionControl { 45 | +void Process() 46 | +void OnIsoEvent() 47 | } 48 | 49 | 50 | IProcessable <|---- SessionControl 51 | IsoTpClient <|-- SessionControl 52 | 53 | IsoTpImpl <|- DoCAN_TP 54 | ICAN_Listener <|--- DoCAN_TP 55 | IProcessable <|--- DoCAN_TP 56 | 57 | DoCAN_TP *.. ICAN_Sender 58 | IsoTpClient .* DoCAN_TP 59 | IsoTpImpl ....o SessionControl 60 | 61 | SessionControl <|-- UdsServerBase 62 | 63 | abstract UdsServerHandler { 64 | +ProcessResult {abstract} OnIndication() 65 | +ProcessResult {abstract} OnConfirmation() 66 | +void DSCSessionEvent() 67 | 68 | #UdsServerBase rtr1 69 | } 70 | 71 | UdsServerHandler *.. UdsServerBase 72 | UdsServerBase o. UdsServerHandler 73 | 74 | class DidRouter{ 75 | } 76 | 77 | IDidHandler .o DidRouter 78 | 79 | class RoutineRouter { 80 | } 81 | 82 | RoutineRouter o. IRoutineHandler 83 | 84 | UdsServerHandler <|-- DidRouter 85 | UdsServerHandler <|-- RoutineRouter 86 | 87 | } 88 | 89 | package User <> 90 | { 91 | 92 | class AppUdsServer { 93 | } 94 | 95 | UdsServerBase <|---- AppUdsServer 96 | 97 | class AppRoutineHandler { 98 | void OnRoutine() 99 | } 100 | 101 | class AppDidHandler { 102 | void ReadDID() 103 | void WriteDID() 104 | } 105 | 106 | IDidHandler <|--- AppDidHandler 107 | IRoutineHandler <|---- AppRoutineHandler 108 | 109 | class UserCanSender { 110 | } 111 | 112 | ICAN_Sender <|- UserCanSender 113 | 114 | } 115 | 116 | 117 | @enduml -------------------------------------------------------------------------------- /src/gtests/etc/timer-tests.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | using namespace DTimers; 6 | 7 | void RollRootCounter(uint32_t ticks) { 8 | 9 | while (ticks--) { 10 | Timers::TickerCounter::ProcessTick(); 11 | } 12 | } 13 | 14 | // Demonstrate some basic assertions. 15 | TEST(DTimerTests, GeneralTests) { 16 | 17 | // no explicit construction 18 | // Timer d = {1}; 19 | Timer t0{}; 20 | 21 | EXPECT_EQ(t0.Elapsed(), false); 22 | t0.Start(10); 23 | EXPECT_EQ(t0.Elapsed(), false); 24 | 25 | RollRootCounter(9); 26 | EXPECT_EQ(t0.Elapsed(), false); 27 | 28 | // 10th tick should cause elapsed event 29 | RollRootCounter(1); 30 | EXPECT_EQ(t0.Elapsed(), true); 31 | // next elapsed after true must be false 32 | EXPECT_EQ(t0.Elapsed(), false); 33 | 34 | // not repeatable timer, no elapsed event 35 | RollRootCounter(10); 36 | EXPECT_EQ(t0.Elapsed(), false); 37 | 38 | t0.Start(10, true); 39 | RollRootCounter(10); 40 | EXPECT_EQ(t0.Elapsed(), true); 41 | 42 | RollRootCounter(9); 43 | EXPECT_EQ(t0.Elapsed(), false); 44 | 45 | RollRootCounter(1); 46 | EXPECT_EQ(t0.Elapsed(), true); 47 | 48 | RollRootCounter(9); 49 | t0.Stop(); 50 | EXPECT_EQ(t0.Elapsed(), false); 51 | RollRootCounter(1); 52 | EXPECT_EQ(t0.Elapsed(), false); 53 | RollRootCounter(5); 54 | EXPECT_EQ(t0.Elapsed(), false); 55 | 56 | } 57 | 58 | 59 | TEST(DTimerTests, ZeroInterval) { 60 | 61 | Timer t1; 62 | 63 | t1.Start(0); 64 | 65 | EXPECT_EQ(t1.IsActive(), true); 66 | EXPECT_EQ(t1.Elapsed(), true); 67 | // not repeatable 68 | EXPECT_EQ(t1.Elapsed(), false); 69 | EXPECT_EQ(t1.IsActive(), false); 70 | 71 | EXPECT_EQ(t1.Ticks(), 0); 72 | 73 | t1.Stop(); 74 | EXPECT_EQ(t1.IsActive(), false); 75 | EXPECT_EQ(t1.Elapsed(), false); 76 | 77 | t1.Start(0, true); 78 | 79 | EXPECT_EQ(t1.Elapsed(), true); 80 | EXPECT_EQ(t1.Elapsed(), true); 81 | EXPECT_EQ(t1.Elapsed(), true); 82 | } 83 | 84 | TEST(DTimersTests, Restart) { 85 | 86 | Timer t2{10}; 87 | 88 | EXPECT_EQ(t2.IsActive(), true); 89 | 90 | RollRootCounter(9); 91 | EXPECT_EQ(t2.Elapsed(), false); 92 | EXPECT_EQ(t2.Ticks(), 1); 93 | 94 | t2.Restart(); 95 | 96 | EXPECT_EQ(t2.Ticks(), 10); 97 | 98 | RollRootCounter(2); 99 | EXPECT_FALSE(t2.Elapsed()); 100 | 101 | RollRootCounter(8); 102 | EXPECT_TRUE(t2.Elapsed()); 103 | } 104 | -------------------------------------------------------------------------------- /src/example/can-bridge.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | class SocketCanSender : public ICAN_Sender { 15 | 16 | public: 17 | size_t SendFrame(const uint8_t* data, size_t length, uint32_t msgid) { 18 | 19 | assert(length <= 8); 20 | assert(txsocket != 0); 21 | 22 | struct can_frame frame; 23 | frame.can_id = msgid; 24 | frame.can_dlc = 8; 25 | memcpy(frame.data, data, length); 26 | assert(write(txsocket, &frame, sizeof(struct can_frame)) != (sizeof(struct can_frame) > 0u)); 27 | return length; 28 | } 29 | 30 | void SetSocket(int s) { 31 | 32 | assert(s != 0); 33 | txsocket = s; 34 | } 35 | 36 | private: 37 | int txsocket{0}; 38 | }; 39 | 40 | class SocketCanReader : public IProcessable { 41 | 42 | public: 43 | SocketCanReader(ICAN_Listener& receiver) : isoreceiver(receiver) { 44 | 45 | select_to.tv_sec = 0u; 46 | select_to.tv_usec = 0u; 47 | } 48 | 49 | void SetSocket(int s) { 50 | 51 | assert(s != 0); 52 | rxsock = s; 53 | } 54 | 55 | virtual void Process() override { 56 | 57 | assert(rxsock != 0); 58 | struct canfd_frame read_frame; 59 | 60 | /* Prepare readfds */ 61 | FD_ZERO(&readfds); 62 | FD_SET(rxsock, &readfds); 63 | // read all fds with no blocking timeout 64 | int ret = select(rxsock + 1, &readfds, NULL, NULL, &select_to); 65 | assert(ret >= 0); 66 | 67 | if (FD_ISSET(rxsock, &readfds)) { 68 | // read data from vcans 69 | do { 70 | auto recv_bytes = recv(rxsock, &read_frame, sizeof(struct canfd_frame), 0); 71 | 72 | if (recv_bytes < 0) { 73 | if (errno != EWOULDBLOCK) { 74 | std::cout << "CAN read error: " << recv_bytes << ". errno = " << errno << std::endl; 75 | } 76 | 77 | break; 78 | } else if (recv_bytes == CAN_MTU) { 79 | isoreceiver.ReadFrame(read_frame.data, 8, read_frame.can_id); 80 | } else if (recv_bytes == 0) { 81 | break; 82 | } else { 83 | std::cout << "Unexpected length: " << recv_bytes << std::endl; 84 | } 85 | } while (true); 86 | } 87 | } 88 | 89 | private: 90 | ICAN_Listener& isoreceiver; 91 | int rxsock{0}; 92 | timeval select_to; 93 | fd_set readfds; 94 | }; 95 | -------------------------------------------------------------------------------- /src/uds/inc/diag/nrcnames.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum class NRCs : uint8_t { 6 | PR = 0, // positive response PR 7 | GR = 0x10, // generalReject GR 8 | SNS = 0x11, // serviceNotSupported SNS 9 | SFNS = 0x12, // sub-functionNotSupportedSFNS 10 | IMLOIF = 0x13, // ncorrectMessageLengthOrInvalidFormatIMLOIF 11 | RTL = 0x14, // responseTooLongRTL 12 | BRR = 0x21, // busyRepeatRequestBRR 13 | CNC = 0x22, // conditionsNotCorrectCNC 14 | RSE = 0x24, // requestSequenceErrorRSE 15 | NRFSC = 0x25, // noResponseFromSubnetComponent NRFSC 16 | FPEORA = 0x26, // FailurePreventsExecutionOfRequestedAction FPEORA 17 | ROOR = 0x31, // requestOutOfRange ROOR 18 | SAD = 0x33, // securityAccessDenied SAD 19 | IK = 0x35, // invalidKey IK 20 | ENOA = 0x36, // exceedNumberOfAttempts ENOA 21 | RTDNE = 0x37, // requiredTimeDelayNotExpired RTDNE 22 | UDNA = 0x70, // uploadDownloadNotAccepted UDNA 23 | TDS = 0x71, // transferDataSuspended TDS 24 | GPF = 0x72, // generalProgrammingFailure GPF 25 | WBSC = 0x73, // wrongBlockSequenceCounter WBSC 26 | RCRRP = 0x78, // requestCorrectlyReceived-ResponsePending RCRRP 27 | SFNSIAS = 0x7E, // sub-functionNotSupportedInActiveSession SFNSIAS 28 | SNSIAS = 0x7F, // serviceNotSupportedInActiveSession SNSIAS 29 | RPMTH = 0x81, // rpmTooHigh RPMTH 30 | RPMTL = 0x82, // rpmTooLow RPMTL 31 | EIR = 0x83, // engineIsRunning EIR 32 | EINR = 0x84, // engineIsNotRunning EINR 33 | ERTTL = 0x85, // engineRunTimeTooLow ERTTL 34 | TEMPTH = 0x86, // temperatureTooHigh TEMPTH 35 | TEMPTL = 0x87, // temperatureTooLow TEMPTL 36 | VSTH = 0x88, // vehicleSpeedTooHigh VSTH 37 | VSTL = 0x89, // vehicleSpeedTooLow VSTL 38 | TPTH = 0x8A, // throttle/PedalTooHigh TPTH 39 | TPTL = 0x8B, // throttle/PedalTooLow TPTL 40 | TRNIN = 0x8C, // transmissionRangeNotInNeutral TRNIN 41 | TRNIG = 0x8D, // transmissionRangeNotInGear TRNIG 42 | BSNC = 0x8F, // brakeSwitch(es)NotClosed (Brake Pedal not pressed or not applied) BSNC 43 | SLNIP = 0x90, // shifterLeverNotInPark SLNIP 44 | TCCL = 0x91, // torqueConverterClutchLocked TCCL 45 | VTH = 0x92, // voltageTooHigh VTH 46 | VTL = 0x93, // voltageTooLow VTL 47 | 48 | // This range of values is reserved by this document for future definition. 49 | // 0x94 – 0xEF reservedForSpecificConditionsNotCorrect RFSCNC 50 | 51 | // This range of values is reserved for vehicle manufacturer specific condition not 52 | // 0xF0 – 0xFE vehicleManufacturerSpecificConditionsNotCorrect VMSCNC 53 | 54 | }; 55 | 56 | constexpr uint8_t NRC_to_byte(NRCs nrc) { 57 | 58 | return static_cast(nrc); 59 | } 60 | -------------------------------------------------------------------------------- /src/etc/helpers/ophelper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "ophelper-config.h" 5 | 6 | namespace ophelper { 7 | 8 | #if !defined (SYS_MEMORY_LE) && !defined(SYS_MEMORY_BE) 9 | #define SYS_MEMORY_LE 10 | #endif 11 | 12 | #ifndef HWREGH 13 | #define HWREGH(regAddress) (*(uint16_t*) (regAddress)) 14 | #endif 15 | 16 | #ifndef SWAP_16BIT_U_VALUE 17 | #define SWAP_16BIT_U_VALUE(data) ((data << 8) & 0xff00) | ((data >> 8) & 0x00ff) 18 | #endif 19 | 20 | #ifndef SWAP_32BIT_U_VALUE 21 | #define SWAP_32BIT_U_VALUE(data) (\ 22 | (((data) & 0xFF000000) >> 24) \ 23 | | (((data) & 0x00FF0000) >> 8) \ 24 | | (((data) & 0x0000FF00) << 8) \ 25 | | (((data) & 0x000000FF) << 24)) 26 | #endif 27 | 28 | 29 | #ifdef SYS_MEMORY_LE 30 | 31 | #define __SWAP_U16_LE__(data) (data) 32 | #define __SWAP_U32_LE__(data) (data) 33 | #define __SWAP_U16_BE__(data) (SWAP_16BIT_U_VALUE(data)) 34 | #define __SWAP_U32_BE__(data) (SWAP_32BIT_U_VALUE(data)) 35 | 36 | #else // big endian 37 | 38 | #define __SWAP_U16_LE__(data) (SWAP_16BIT_U_VALUE(data)) 39 | #define __SWAP_U32_LE__(data) (SWAP_32BIT_U_VALUE(data)) 40 | #define __SWAP_U16_BE__(data) (data) 41 | #define __SWAP_U32_BE__(data) (data) 42 | 43 | #endif // #ifdef SYS_MEMORY_LE 44 | 45 | 46 | constexpr uint16_t to_be_u16(const uint16_t value) { 47 | return __SWAP_U16_BE__(value); 48 | } 49 | 50 | constexpr uint32_t to_be_u32(const uint32_t value) { 51 | return __SWAP_U32_BE__(value); 52 | } 53 | 54 | constexpr uint16_t to_le_u16(const uint16_t value) { 55 | return __SWAP_U16_LE__(value); 56 | } 57 | 58 | constexpr uint32_t to_le_u32(const uint32_t value) { 59 | return __SWAP_U32_LE__(value); 60 | } 61 | 62 | constexpr uint16_t from_be_u16(const uint16_t value) { 63 | return to_be_u16(value); 64 | } 65 | 66 | constexpr uint32_t from_be_u32(const uint32_t value) { 67 | return to_be_u32(value); 68 | } 69 | 70 | constexpr uint16_t from_le_u16(const uint16_t value) { 71 | return to_le_u16(value); 72 | } 73 | 74 | constexpr uint32_t from_le_u32(const uint32_t value) { 75 | return to_le_u32(value); 76 | } 77 | 78 | template 79 | constexpr bool is_in_range(T value) { 80 | static_assert(Begin <= End, "Range begin must be less than end"); 81 | return (static_cast(value) >= Begin) && (static_cast(value) <= End); 82 | } 83 | 84 | template 85 | constexpr bool is_out_range(T value) { 86 | return !is_in_range(value); 87 | } 88 | 89 | 90 | #undef __SWAP_U16_LE__ 91 | #undef __SWAP_U32_LE__ 92 | #undef __SWAP_U16_BE__ 93 | #undef __SWAP_U32_BE__ 94 | 95 | #undef SWAP_16BIT_U_VALUE 96 | #undef SWAP_32BIT_U_VALUE 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/etc/timers/d-timer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "tickerup.h" 4 | 5 | using interval_t = Timers::TickerCounter::systick_t; 6 | 7 | namespace DTimers { 8 | 9 | using namespace Timers; 10 | 11 | class Timer : private TickerUp { 12 | 13 | public: 14 | /// @brief Default empty constuctor (stopped, interval 0, non repeat) 15 | Timer() = default; 16 | 17 | /// @brief Detailed timer constructor 18 | /// @param intv interval to repeat 19 | /// @param start start immediately 20 | /// @param repeat repeatable or not 21 | explicit Timer(interval_t intv, bool start = true, bool repeat = true); 22 | 23 | /// @brief Start timer with new interval 24 | /// @param interval new timer's interval 25 | void Start(interval_t interval); 26 | 27 | /// @brief Start timer with new interval and repeat setup 28 | /// @param interval new timer's interval 29 | /// @param repeat repeat setup 30 | void Start(interval_t interval, bool repeat); 31 | 32 | /// @brief Restart timer with current configuration 33 | void Restart(); 34 | 35 | /// @brief Checks if the timer elapsed 36 | /// @return is timer elapsed 37 | bool Elapsed(); 38 | 39 | /// @brief Checks if timer is run (active) 40 | /// @return is timer active 41 | bool IsActive() const { 42 | return (is_active); 43 | } 44 | 45 | /// @brief Number of ticks to next elapsing 46 | /// @return 0 if timer is stopped or active elapse event 47 | uint32_t Ticks() const; 48 | 49 | /// @brief Set elapsed forcibly 50 | void ForceElapse() { 51 | SetStartTick(now() - tick_period); 52 | } 53 | 54 | /// @brief Deactivates timer (elapse is not possible) 55 | void Stop() { 56 | is_active = false; 57 | } 58 | 59 | private: 60 | 61 | /// @brief Returns number of ticks to the next elapse event 62 | /// @param liveticks current sys tick 63 | /// @return number of ticks 64 | interval_t GetTicksToNextElapse(systick_t liveticks) const { 65 | // check if current tick (@liveticks) is not further than inteval_ 66 | systick_t passed_ticks = liveticks - tick_start; 67 | return (tick_period > passed_ticks) ? (tick_period - passed_ticks) : (0); 68 | } 69 | 70 | /// @brief Sets start tick point to new value 71 | /// @param ticks ticks to set 72 | void SetStartTick(Timers::TickerCounter::systick_t ticks) { 73 | tick_start = ticks; 74 | } 75 | 76 | /// @brief period value 77 | interval_t tick_period = 0; 78 | /// @brief current period start tick 79 | systick_t tick_start = 0; 80 | 81 | /// @brief is timer active 82 | bool is_active = false; 83 | 84 | /// @brief is timer is_endless 85 | bool is_endless = false; 86 | 87 | public: 88 | /// @brief Non copyable 89 | Timer(const Timer&) = delete; 90 | Timer& operator=(const Timer&) = delete; 91 | 92 | }; 93 | 94 | } // namespace DTimers 95 | -------------------------------------------------------------------------------- /src/uds/inc/diag/routineids.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | //************************************************************************************************** 4 | // This file contains definitions for common routine IDS 5 | //************************************************************************************************** 6 | 7 | //------------------------------------------------------------------------------ 8 | // ISOSAEReserved range 9 | //------------------------------------------------------------------------------ 10 | #define RC_ID_ISOSAERESRVD1_MIN (0x0000U) 11 | #define RC_ID_ISOSAERESRVD1_MAX (0x00FFU) 12 | 13 | //------------------------------------------------------------------------------ 14 | // TachographTestIds range 15 | //------------------------------------------------------------------------------ 16 | #define RC_ID_TACHORI_MIN (0x0100U) 17 | #define RC_ID_TACHORI_MAX (0x01FFU) 18 | 19 | //------------------------------------------------------------------------------ 20 | // vehicleManufacturerSpecific range 21 | //------------------------------------------------------------------------------ 22 | #define RC_ID_VMS_MIN (0x0200U) 23 | #define RC_ID_VMS_MAX (0xDFFFU) 24 | 25 | // preProgrammingCheck 26 | #define RC_ID_SSS_PREPROGCHK (0x0201U) 27 | 28 | // postProgrammingCheck 29 | #define RC_ID_SSS_POSTPROGCHK (0x0202U) 30 | 31 | // eraseSection 32 | #define RC_ID_SSS_ERASESECTION (0x0203U) 33 | 34 | //------------------------------------------------------------------------------ 35 | // OBDTestIds range 36 | //------------------------------------------------------------------------------ 37 | #define RC_ID_OBDRI_MIN (0xE000U) 38 | #define RC_ID_OBDRI_MAX (0xE1FFU) 39 | 40 | // DeployLoopRoutineID 41 | #define RC_ID_DLRI (0xE200U) 42 | 43 | //------------------------------------------------------------------------------ 44 | // SafetySystemRoutineIDs range 45 | //------------------------------------------------------------------------------ 46 | #define RC_ID_SASRI_MIN (0xE201U) 47 | #define RC_ID_SASRI_MAX (0xE2FFU) 48 | 49 | //------------------------------------------------------------------------------ 50 | // ISOSAEReserved range 51 | //------------------------------------------------------------------------------ 52 | #define RC_ID_ISOSAERESRVD2_MIN (0xE300U) 53 | #define RC_ID_ISOSAERESRVD2_MAX (0xEFFFU) 54 | 55 | //------------------------------------------------------------------------------ 56 | // systemSupplierSpecific range 57 | //------------------------------------------------------------------------------ 58 | #define RC_ID_SSS_MIN (0xF000U) 59 | #define RC_ID_SSS_MAX (0xFEFFU) 60 | 61 | // eraseMemory 62 | #define RC_ID_EM (0xFF00U) 63 | 64 | // checkProgrammingDependencies 65 | #define RC_ID_CPD (0xFF01U) 66 | 67 | // eraseMirrorMemoryDTCs 68 | #define RC_ID_EMMDTC (0xFF02U) 69 | 70 | //------------------------------------------------------------------------------ 71 | // ISOSAEReserved range 72 | //------------------------------------------------------------------------------ 73 | #define RC_ID_ISOSAERESRVD3_MIN (0xFF03U) 74 | #define RC_ID_ISOSAERESRVD3_MAX (0xFFFFU) 75 | 76 | -------------------------------------------------------------------------------- /src/uds/session/apps/did-router.cpp: -------------------------------------------------------------------------------- 1 | #include "did-router.h" 2 | #include "did-keeper.h" 3 | 4 | constexpr uint32_t SI_SIZE_BYTES = (1u); 5 | constexpr uint32_t DID_SIZE_BYTES = (2u); 6 | 7 | bool DidRouter::IsServiceSupported(sid_t sid, size_t& minlength, bool& subfunc) { 8 | 9 | (void) subfunc; 10 | 11 | if (sid == sidhelper::RDBI) { 12 | minlength = 3u; 13 | return true; 14 | } 15 | 16 | return false; 17 | } 18 | 19 | ProcessResult DidRouter::OnAppIndication(const IndicationInfo& inf) { 20 | 21 | auto pres = ProcessResult::HANDLED_RESP_OK; 22 | len_ = inf.size; 23 | data_ = inf.data; 24 | 25 | switch (inf.head.SI) { 26 | case sidhelper::RDBI: 27 | ReadDataByIdentifierHandler(); 28 | break; 29 | 30 | case sidhelper::WDBI: 31 | WriteDataByIdentifierHandler(); 32 | break; 33 | 34 | default: 35 | pres = ProcessResult::NOT_HANDLED; 36 | break; 37 | } 38 | 39 | return pres; 40 | } 41 | 42 | void DidRouter::OnAppConfirmation(S_Result) { 43 | } 44 | 45 | 46 | void DidRouter::ReadDataByIdentifierHandler() { 47 | 48 | if (len_ != (SI_SIZE_BYTES + DID_SIZE_BYTES)) { 49 | udsRouter.SendNegResponse(NRCs::IMLOIF); 50 | return; 51 | } 52 | 53 | // Process all DIDs 54 | uint32_t idx = SI_SIZE_BYTES; // index in output buffer 55 | 56 | // Read DID from request (big-endian) 57 | uint16_t dataid = data_[SI_SIZE_BYTES] << 8 | data_[SI_SIZE_BYTES + 1]; 58 | 59 | // Place the next DID into res 60 | udsRouter.pubBuff[idx++] = dataid >> 8; 61 | udsRouter.pubBuff[idx++] = dataid & 0xFF; 62 | 63 | size_t len_out = 0; 64 | NRCs nrc_out = NRCs::ROOR; 65 | DidResult ret = dider.ReadDID(dataid, udsRouter.pubBuff + idx, 64u, len_out, nrc_out); 66 | 67 | // In case of positive handling and zero len_out - response with ROOR 68 | // In case of ignored request - response with ROOR 69 | if (((ret == DidResult::Positive) && (len_out == 0)) || (ret == DidResult::Ignored)) { 70 | ret = DidResult::Negative; 71 | nrc_out = NRCs::ROOR; 72 | } 73 | 74 | // Send positive response 75 | udsRouter.pubRespLength = len_out + SI_SIZE_BYTES + DID_SIZE_BYTES; 76 | } 77 | 78 | void DidRouter::WriteDataByIdentifierHandler() { 79 | 80 | // Only one DID can be written at once 81 | uint16_t dataid = data_[1] << 8 | data_[2]; 82 | udsRouter.pubBuff[1] = data_[1]; 83 | udsRouter.pubBuff[2] = data_[2]; 84 | 85 | // Every WDBI must have len >= 3 bytes 86 | if (len_ <= SI_SIZE_BYTES + DID_SIZE_BYTES) { 87 | udsRouter.SendNegResponse(NRCs::IMLOIF); 88 | } else { 89 | NRCs nrc_out = NRCs::ROOR; 90 | DidResult ret = dider.WriteDID(dataid, data_ + SI_SIZE_BYTES + DID_SIZE_BYTES, len_ - SI_SIZE_BYTES - DID_SIZE_BYTES, 91 | nrc_out); 92 | 93 | // In case of ignored request - response with ROOR 94 | if (ret == DidResult::Ignored) { 95 | ret = DidResult::Negative; 96 | nrc_out = NRCs::ROOR; 97 | } 98 | 99 | // Prepare answer 100 | if (ret == DidResult::Positive) { 101 | // Success - DID is handled and data is copied into buffer 102 | udsRouter.pubRespLength = 3; 103 | } else { 104 | // Error - DID is handled with error 105 | udsRouter.SendNegResponse(nrc_out); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/uds/isotp/docan-tp.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "pci-helper.h" 3 | #include "docan-tp.h" 4 | 5 | 6 | void DoCAN_TP::Process() { 7 | 8 | iso_sender.ProcessTx(); 9 | iso_receiver.ProcessRx(); 10 | } 11 | 12 | void DoCAN_TP::ReadFrame(const uint8_t* data, size_t length, uint32_t msgid) { 13 | 14 | bool is_phys = (msgid == configDoCAN.phys_id); 15 | bool is_func = (msgid == configDoCAN.func_id); 16 | 17 | DC_FrameType ptype = static_cast(data[0] & 0xf0u); 18 | 19 | // request with functional address can be only SF 20 | if ((is_func) && (ptype == DC_FrameType::SF)) { 21 | // request with functional address can be only SF 22 | pduContext.address = N_TarAddress::TAtype_2_Functional; 23 | 24 | if ((data[0] & 0x0fu) == 0) { 25 | pduContext.data = data + 2; 26 | pduContext.length = data[1]; 27 | } else { 28 | pduContext.data = data + 1; 29 | pduContext.length = data[0] & 0x0fu; 30 | } 31 | 32 | // notify upper layer 33 | iso_client.OnIsoEvent(N_Event::Data, N_Result::OK_r, pduContext); 34 | } else if (is_phys) { 35 | switch (ptype) { 36 | case (DC_FrameType::FC): 37 | // flow control message for ICAN_Sender 38 | iso_sender.OnFlowControl(data[0] & 0x0fu, data[1], data[2]); 39 | break; 40 | 41 | default: 42 | iso_receiver.Receive(data, length); 43 | break; 44 | } 45 | } 46 | } 47 | 48 | IsoTpResult DoCAN_TP::Request(const uint8_t* data, size_t length) { 49 | 50 | return iso_sender.Send(data, length); 51 | } 52 | 53 | SetParamResult DoCAN_TP::SetParameter(ParName name, uint32_t v) { 54 | 55 | auto ret = SetParamResult::OK; 56 | 57 | if (iso_sender.IsBusy() || iso_receiver.IsBusy()) { 58 | // TODO: make this return more clear (for the case when Tx is busy) 59 | return SetParamResult::RX_ON; 60 | } 61 | 62 | switch (name) { 63 | case (ParName::BLKSZ): 64 | case (ParName::ST_MIN): 65 | case (ParName::Br_TIM_ms): 66 | case (ParName::Cr_TIM_ms): 67 | ret = iso_receiver.SetParameter(name, v); 68 | break; 69 | 70 | case (ParName::As_TIM_ms): 71 | case (ParName::Bs_TIM_ms): 72 | ret = iso_sender.SetParameter(name, v); 73 | break; 74 | 75 | case (ParName::CANDL): 76 | configDoCAN.candl = (v <= MAX_CANDL) ? v : MAX_CANDL; 77 | break; 78 | 79 | case (ParName::PADD_BYTE): 80 | configDoCAN.padding = static_cast(v); 81 | break; 82 | 83 | case (ParName::PHYS_ADDR): 84 | configDoCAN.phys_id = v; 85 | break;; 86 | 87 | case (ParName::FUNC_ADDR): 88 | configDoCAN.func_id = v; 89 | break; 90 | 91 | case (ParName::RESP_ADDR): 92 | configDoCAN.resp_id = v; 93 | break; 94 | 95 | default: 96 | break; 97 | } 98 | 99 | return ret; 100 | } 101 | 102 | void DoCAN_TP::OnIsoRxEvent(N_Event event, N_Result result, const uint8_t* data, size_t length) { 103 | 104 | pduContext.data = data; 105 | pduContext.length = length; 106 | pduContext.address = N_TarAddress::TAtype_1_Physical; 107 | 108 | iso_client.OnIsoEvent(event, result, pduContext); 109 | } 110 | 111 | void DoCAN_TP::OnIsoTxEvent(N_Event event, N_Result result) { 112 | 113 | iso_client.OnIsoEvent(event, result, pduContext); 114 | } 115 | -------------------------------------------------------------------------------- /src/uds/isotp/docan-sender.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../inc/iso-tp-types.h" 5 | #include "../inc/iso-tp-const.h" 6 | 7 | class DoCAN_TP; 8 | 9 | class DoCAN_Sender { 10 | 11 | public: 12 | /// @brief DoCAN sender constructor 13 | /// @param mem pointer to buffer for transmit message 14 | /// @param bufcap transmit buffer length 15 | /// @param isotp reference to DoCAN iso tp object 16 | DoCAN_Sender(uint8_t* mem, const size_t bufcap, DoCAN_TP& isotp) : txbuff(mem), TXLEN(bufcap), itp(isotp) {} 17 | 18 | /// @brief General transmitter handler 19 | void ProcessTx(); 20 | 21 | /// @brief FlowControl frame handler 22 | /// @param flow_status flow status from FC payload 23 | /// @param blks block size from FC payload 24 | /// @param stm STmin from FC payload 25 | void OnFlowControl(uint8_t flow_status, uint8_t blks, uint8_t stm); 26 | 27 | /// @brief Payload sender 28 | /// @param data pointer to ISO TP packet 29 | /// @param len length of ISO TP packet 30 | /// @return call result 31 | IsoTpResult Send(const uint8_t* data, datasize_t len); 32 | 33 | /// @brief DoCAN sender parameter setter 34 | /// @param name parameter name 35 | /// @param v parameter value 36 | /// @return parameter set result 37 | SetParamResult SetParameter(ParName name, uint32_t v); 38 | 39 | /// @brief Is DoCAN sender is busy 40 | /// @return true - if busy 41 | bool IsBusy() const { 42 | return (txds.state != DtState::IDLE); 43 | } 44 | 45 | private: 46 | /// @brief Checks if datasize fits to transmit buffer 47 | /// @param datasize size of data 48 | /// @return result of data size check 49 | IsoTpResult CheckTxValid(datasize_t datasize); 50 | 51 | /// @brief pointer to general transmit buffer (from outside) 52 | uint8_t* const txbuff; 53 | 54 | /// @brief length of general transmit buffer 55 | const size_t TXLEN; 56 | 57 | /// @brief host DoCAN object (DoCAN_TP) 58 | DoCAN_TP& itp; 59 | 60 | /// @brief DoCAN sender states 61 | enum class DtState { 62 | IDLE, SF_DT, MF_DT, PAUSE, WAIT, 63 | }; 64 | 65 | struct TxDescriptor { 66 | /// @brief general sender state 67 | DtState state {DtState::IDLE}; 68 | /// @brief control block: previously passed size 69 | datasize_t passed{0}; 70 | /// @brief control block: the whole transmitted size 71 | datasize_t size{0}; 72 | 73 | /// @brief flow control: serial number 74 | uint8_t sn{0}; 75 | // @currblknum - current number of sent CF 76 | 77 | /// @brief flow control: number of correctly received blocks 78 | uint8_t currblknum{0}; 79 | 80 | /// @brief flow control: whole number of blocks for current tx segment 81 | uint8_t segblkcount{25}; 82 | 83 | /// @brief flow control: STmin setting for current tx segment 84 | uint8_t stmin{0}; 85 | }; 86 | 87 | /// @brief general flow control descriptor 88 | TxDescriptor txds{}; 89 | 90 | /// @brief can message buffer 91 | uint8_t can_message[MAX_CANDL] {0}; 92 | 93 | DTimers::Timer N_As_tim{1000, false, false}; 94 | DTimers::Timer N_Bs_tim{1000, false, false}; 95 | // STmin is Cs timer 96 | DTimers::Timer STmin_tim{0, false, false}; 97 | 98 | /// @brief indicates if last part of payload was sent by previous attempt 99 | bool prev_data_sent{true}; 100 | 101 | }; 102 | 103 | -------------------------------------------------------------------------------- /src/example/uds-test-server/cli-client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Menu { 12 | 13 | public: 14 | Menu(const std::string& str) : text(str) {} 15 | 16 | std::string text; 17 | std::vector cmd; 18 | 19 | Menu* SetNext(Menu* next) { 20 | 21 | assert(next != nullptr); 22 | this->next = next; 23 | return this->next; 24 | } 25 | 26 | Menu* SetDown(Menu* d) { 27 | 28 | assert(d != nullptr); 29 | this->down = d; 30 | return d; 31 | } 32 | 33 | Menu* GetNext() { 34 | 35 | return next; 36 | } 37 | 38 | bool IsNext() { 39 | 40 | return next != nullptr; 41 | } 42 | 43 | Menu* GetDown() { 44 | 45 | return down; 46 | } 47 | 48 | bool IsDown() { 49 | 50 | return down != nullptr; 51 | } 52 | 53 | 54 | private: 55 | 56 | Menu* next {nullptr}; 57 | Menu* down {nullptr}; 58 | 59 | }; 60 | 61 | class CliMen { 62 | 63 | public: 64 | CliMen(Menu* m) : root(m) { 65 | 66 | assert(root != nullptr); 67 | } 68 | 69 | public: 70 | void Run() { 71 | 72 | Menu* item = root; 73 | Menu* start = root; 74 | 75 | while (true) { 76 | 77 | if (!deep.empty()) { 78 | start = deep.top(); 79 | } else { 80 | start = root; 81 | } 82 | 83 | item = start; 84 | uint32_t count = 0; 85 | 86 | do { 87 | std::cout << ++count << " : " << item->text << std::endl; 88 | item = item->GetNext(); 89 | } while (item != nullptr); 90 | 91 | std::string in; 92 | std::cout << "Input number > "; 93 | std::cin >> in; 94 | int32_t scaned = -1; 95 | 96 | if ((sscanf(in.c_str(), "%d", &scaned)) == 1) { 97 | // try to find item on current level 98 | if (scaned > 0) { 99 | item = start; 100 | 101 | while (--scaned && item != nullptr) { 102 | item = item->GetNext(); 103 | } 104 | 105 | if (item != nullptr) { 106 | if (item->IsDown()) { 107 | // get down pointer 108 | item = item->GetDown(); 109 | deep.push(item); 110 | } else { 111 | std::cout << "Send command '" << item->text << "' to iso TP" << std::endl << std::endl; 112 | std::lock_guard guard(mtx); 113 | veccmd = &item->cmd; 114 | cmd_ready = true; 115 | } 116 | } 117 | } else if (scaned == 0) { 118 | // exit here 119 | } else { 120 | // print again 121 | } 122 | } else { 123 | // try to go up 124 | if (deep.empty() == false) { 125 | item = deep.top(); 126 | deep.pop(); 127 | } else { 128 | item = root; 129 | } 130 | } 131 | } // while 132 | } 133 | 134 | bool IsCmd() const { 135 | 136 | return cmd_ready; 137 | } 138 | 139 | std::vector GetData() { 140 | 141 | std::lock_guard guard(mtx); 142 | auto retv = *veccmd; 143 | cmd_ready = false; 144 | return retv; 145 | } 146 | 147 | private: 148 | Menu* const root{nullptr}; 149 | std::stack deep; 150 | std::vector* veccmd; 151 | std::mutex mtx; 152 | bool cmd_ready {false}; 153 | }; 154 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | project(${project} LANGUAGES C CXX) 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | set(CMAKE_CXX_STANDARD 11) 7 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 8 | set(THREADS_PREFER_PTHREAD_FLAG ON) 9 | 10 | add_executable(udstogo-test 11 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/docan-receiver.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/docan-sender.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/docan-tp.cpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/pci-helper.cpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/session/session-control.cpp 16 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/session/uds-app-manager.cpp 17 | ${CMAKE_CURRENT_SOURCE_DIR}/etc/timers/d-timer.cpp 18 | ${CMAKE_CURRENT_SOURCE_DIR}/etc/timers/tickerup.cpp 19 | ${CMAKE_CURRENT_SOURCE_DIR}/example/iso-tp-server/serv-factory.cpp 20 | ${CMAKE_CURRENT_SOURCE_DIR}/example/app-helper.cpp 21 | ${CMAKE_CURRENT_SOURCE_DIR}/example/main.cpp 22 | ) 23 | 24 | target_include_directories( 25 | udstogo-test PRIVATE 26 | ${CMAKE_CURRENT_SOURCE_DIR}/etc 27 | ) 28 | 29 | add_executable(ecu-server-test 30 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/docan-receiver.cpp 31 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/docan-sender.cpp 32 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/docan-tp.cpp 33 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/isotp/pci-helper.cpp 34 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/session/session-control.cpp 35 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/session/uds-app-manager.cpp 36 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/session/apps/did-router.cpp 37 | ${CMAKE_CURRENT_SOURCE_DIR}/etc/timers/d-timer.cpp 38 | ${CMAKE_CURRENT_SOURCE_DIR}/etc/timers/tickerup.cpp 39 | ${CMAKE_CURRENT_SOURCE_DIR}/example/uds-test-server/serv-factory.cpp 40 | ${CMAKE_CURRENT_SOURCE_DIR}/example/uds-test-server/session-client.cpp 41 | ${CMAKE_CURRENT_SOURCE_DIR}/example/app-helper.cpp 42 | ${CMAKE_CURRENT_SOURCE_DIR}/example/server-app.cpp 43 | ) 44 | 45 | target_include_directories( 46 | ecu-server-test PRIVATE 47 | ${CMAKE_CURRENT_SOURCE_DIR}/etc 48 | ) 49 | 50 | find_package(Threads REQUIRED) 51 | target_link_libraries(udstogo-test PRIVATE Threads::Threads) 52 | target_link_libraries(ecu-server-test PRIVATE Threads::Threads) 53 | 54 | target_compile_options(ecu-server-test PRIVATE 55 | -fanalyzer 56 | -Wall 57 | -Wextra 58 | -Wpedantic 59 | -Wno-ignored-qualifiers 60 | ) 61 | 62 | target_compile_options(udstogo-test PRIVATE 63 | -fanalyzer 64 | -Wall 65 | -Wextra 66 | -Wpedantic 67 | -Wno-ignored-qualifiers 68 | ) 69 | 70 | 71 | # GoogleTest requires at least C++14 72 | set(CMAKE_CXX_STANDARD 14) 73 | 74 | include(FetchContent) 75 | FetchContent_Declare( 76 | googletest 77 | URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip 78 | ) 79 | FetchContent_MakeAvailable(googletest) 80 | 81 | enable_testing() 82 | 83 | add_executable( 84 | test_run 85 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/session/session-control.cpp 86 | ${CMAKE_CURRENT_SOURCE_DIR}/uds/session/uds-app-manager.cpp 87 | ${CMAKE_CURRENT_SOURCE_DIR}/etc/timers/d-timer.cpp 88 | ${CMAKE_CURRENT_SOURCE_DIR}/etc/timers/tickerup.cpp 89 | ${CMAKE_CURRENT_SOURCE_DIR}/gtests/etc/timer-tests.cpp 90 | ${CMAKE_CURRENT_SOURCE_DIR}/gtests/ophelper-be-test.cpp 91 | ${CMAKE_CURRENT_SOURCE_DIR}/gtests/ophelper-le-test.cpp 92 | ${CMAKE_CURRENT_SOURCE_DIR}/gtests/app-uds-response-allow-test.cpp 93 | ) 94 | 95 | target_link_libraries( 96 | test_run 97 | GTest::gtest_main 98 | ) 99 | 100 | target_include_directories( 101 | test_run PRIVATE 102 | ${CMAKE_CURRENT_SOURCE_DIR}/ 103 | ${CMAKE_CURRENT_SOURCE_DIR}/etc 104 | ) 105 | 106 | include(GoogleTest) 107 | gtest_discover_tests(test_run) 108 | -------------------------------------------------------------------------------- /src/uds/session/uds-app-client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "uds-app-manager.h" 5 | 6 | /// @brief Uds app client interface class 7 | class UdsAppClient { 8 | 9 | public: 10 | /// @brief Constructor 11 | /// @param udsapp Reference to application uds manager 12 | UdsAppClient(UdsAppManager& udsapp) : udsRouter(udsapp) {} 13 | 14 | /// @brief Checks if service can be handled by current instance 15 | /// @param sid Service Id to be handled 16 | /// @param minlenght Minimal payload for requested service 17 | /// @param subfunc Does the service have sub function 18 | /// @return true when instance can handle requested service, otherwise false 19 | virtual bool IsServiceSupported(const sid_t sid, size_t& minlenght, bool& subfunc) = 0; 20 | 21 | /// @brief Callback from application uds manager on session indication event 22 | /// @param inf Indication event descriptor 23 | /// @return Processing result 24 | virtual ProcessResult OnAppIndication(const IndicationInfo& inf) = 0; 25 | 26 | /// @brief Callback from application uds manager on session confirmation event 27 | /// @param res 28 | virtual void OnAppConfirmation(S_Result res) = 0; 29 | 30 | /// @brief Callback from application uds manager on session change event 31 | /// @param isdefault 32 | virtual void OnSessionChange(bool isdefault) { 33 | 34 | (void) isdefault; 35 | } 36 | 37 | protected: 38 | UdsAppManager& udsRouter; 39 | }; 40 | 41 | 42 | /// @brief Proxy uds application handler class, aggregates more than 1 client 43 | /// @tparam N Number of possible uds application clients 44 | template 45 | class MultiServiceManager : public MemKeeper, public UdsAppClient { 46 | 47 | public: 48 | 49 | /// @brief Constructor 50 | /// @param base Reference on uds application manager 51 | MultiServiceManager(UdsAppManager& base) : UdsAppClient(base) {} 52 | 53 | /// @brief Checks if service can be handled by current instance 54 | /// @param sid Service Id to be handled 55 | /// @param minlenght Minimal payload for requested service 56 | /// @param subfunc Does the service have sub function 57 | /// @return true when instance can handle requested service, otherwise false 58 | virtual bool IsServiceSupported(const sid_t sid, size_t& minlenght, bool& subfunc) { 59 | 60 | bool ret = false; 61 | UdsAppClient* service = nullptr; 62 | size_t i = 0u; 63 | 64 | while (this->TryReadElem(i++, service)) { 65 | if (service->IsServiceSupported(sid, minlenght, subfunc)) { 66 | ret = true; 67 | break; 68 | } 69 | } 70 | 71 | return ret; 72 | } 73 | 74 | /// @brief Callback from application uds manager on session indication event 75 | /// @param inf Indication event descriptor 76 | /// @return Processing result 77 | virtual ProcessResult OnAppIndication(const IndicationInfo& inf) { 78 | 79 | ProcessResult procResult; 80 | UdsAppClient* service = nullptr; 81 | size_t i = 0u; 82 | 83 | while (this->TryReadElem(i++, service)) { 84 | procResult = service->OnAppIndication(inf); 85 | 86 | if (procResult != ProcessResult::NOT_HANDLED) { 87 | break; 88 | } 89 | } 90 | 91 | return procResult; 92 | } 93 | 94 | /// @brief Callback from application uds manager on session confirmation event 95 | /// @param res Confirmation result (status) 96 | virtual void OnAppConfirmation(S_Result res) { 97 | 98 | this->StartIteration(); 99 | 100 | while (!this->IsLastIteration()) { 101 | this->IterNextElem()->OnAppConfirmation(res); 102 | } 103 | } 104 | 105 | /// @brief Callback from uds session layer on session change event 106 | /// @param isdefault true when current session is default 107 | virtual void OnSessionChange(bool isdefault) { 108 | 109 | this->StartIteration(); 110 | 111 | while (!this->IsLastIteration()) { 112 | this->IterNextElem()->OnSessionChange(isdefault); 113 | } 114 | } 115 | }; 116 | 117 | -------------------------------------------------------------------------------- /src/uds/session/uds-app-manager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "session-control.h" 4 | #include 5 | #include 6 | 7 | /// @brief Forward declarion for uds request handler 8 | class UdsAppClient; 9 | 10 | class UdsAppManager : public SessionControl { 11 | 12 | public: 13 | /// @brief Constructor 14 | /// @param membuff Pointer to memory for using as response buffer 15 | /// @param capacity Transmit buffer capacity 16 | /// @param sessinstance Reference to the session information instance 17 | UdsAppManager(uint8_t* membuff, datasize_t capacity, const SessionInfo& sessinstance); 18 | 19 | /// @brief Send application response 20 | /// @param data Pointer to payload 21 | /// @param length Payload length 22 | void SendResponse(const uint8_t* data, uint32_t length); 23 | 24 | /// @brief Send negative response for the last requested service 25 | /// @param nrc Negative repsponse code 26 | void SendNegResponse(NRCs nrc); 27 | 28 | /// @brief Send negative response for specific service 29 | /// @param sid Service identificator 30 | /// @param nrc Negative response code 31 | void SendNegResponse(const sid_t sid, const NRCs nrc); 32 | 33 | /// @brief Send negative response code RCRRP 34 | /// @param maxduration Global enhanced mode timeout 35 | /// @param resendperiod Enhanced mode keeping alive message send period 36 | void StartPending(const size_t maxduration, const size_t resendperiod = 2000u); 37 | 38 | /// @brief Set client. Can be called once 39 | /// @param client Pointer to the uds application client 40 | void SetClient(UdsAppClient* client); 41 | 42 | /// @brief Set app manager mode 43 | /// @param isactive when true manager is enabled, otherwise is disabled 44 | void SetActiveMode(bool isactive); 45 | 46 | 47 | /// @brief Notify app manager about session change 48 | /// @param sessionValue New (old) session value 49 | void SetServiceSession(uint8_t sessionValue); 50 | 51 | /// @brief Session state getter 52 | /// @return Reference on app manager session state 53 | const SessionInfo& GetSession() const { 54 | return sessionState; 55 | } 56 | 57 | /// @brief Response buffer for loading response by client 58 | uint8_t* const pubBuff; 59 | /// @brief Response buffer capacity 60 | const datasize_t PubBuffCapacity; 61 | /// @brief Response payload length 62 | datasize_t pubRespLength; 63 | 64 | private: 65 | /// @brief Request context information 66 | IndicationInfo reqContext{}; 67 | /// @brief Reference to session state instance 68 | const SessionInfo& sessionState; 69 | 70 | /// @brief Session layer indication callback 71 | /// @param data Pointer to indication data 72 | /// @param length Length of indication data 73 | /// @param addr Request address 74 | virtual void OnSessIndication(const uint8_t* data, uint32_t length, TargetAddressType addr) override; 75 | 76 | /// @brief Session layer confirmation callback 77 | /// @param res Confirmation result 78 | virtual void OnSessConfirmation(S_Result res) override; 79 | 80 | /// @brief Session layer s3 timer timeout callback 81 | virtual void On_s3_Timeout(); 82 | 83 | /// @brief Internal request handler 84 | /// @return true if request has been handled internally, otherwise false 85 | bool HandleReqInternally(); 86 | 87 | private: 88 | 89 | /// @brief Checks if the response attributes allow to send response on CAN 90 | /// @return false when response must not be sent, otherwise true 91 | bool ResponseAllowed(); 92 | 93 | /// @brief Checks if requested SID is supported by client 94 | /// @return true when client is able to handle service request, otherwise false 95 | bool CheckIfSidSupported(); 96 | 97 | /// @brief General tester present request handler 98 | void SID_TesterPresent(); 99 | 100 | /// @brief Pointer to uds app client 101 | UdsAppClient* clientHandler; 102 | /// @brief Negative output code to be sent as response 103 | NRCs nrcOutCode; 104 | /// @brief Pending mode activation indicator 105 | bool nrcPending; 106 | /// @brief Manager activity flag, manager blocks requests when false 107 | bool isManagerActive; 108 | 109 | }; 110 | 111 | -------------------------------------------------------------------------------- /src/uds/isotp/docan-tp.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "../inc/iso-tp-if.h" 6 | #include "docan-sender.h" 7 | #include "docan-receiver.h" 8 | 9 | class DoCAN_TP : public ICAN_Listener, public IsoTpImpl, public IProcessable { 10 | 11 | public: 12 | DoCAN_TP(uint8_t* memrx, size_t lengthrx, uint8_t* memtx, size_t lengthtx, ICAN_Sender& sender, IsoTpClient& client) : 13 | iso_sender(memtx, lengthtx, *this), 14 | iso_receiver(memrx, lengthrx, *this), 15 | can_sender(sender), 16 | iso_client(client) 17 | {} 18 | 19 | /// @brief Periodic processing 20 | virtual void Process() override; 21 | 22 | /// @brief Function is called on each new CAN frame 23 | /// @param data Pointer to CAN frame data 24 | /// @param length CAN frame payload length 25 | /// @param msgid CAN message ID 26 | virtual void ReadFrame(const uint8_t* data, size_t length, uint32_t msgid) override; 27 | 28 | /// @brief Sencs CAN frame payload on bus 29 | /// @param data Pointer to payload data 30 | /// @param length Payload length 31 | /// @return Sending result 32 | virtual IsoTpResult Request(const uint8_t* data, size_t length) override; 33 | 34 | /// @brief Puts padding byte and sends CAN payload 35 | /// @param data Pointer to payload to be sent 36 | /// @param len Usefull part of payload length 37 | /// @return CAN frame payload length 38 | size_t PduToCan(uint8_t* data, datasize_t len) { 39 | // WARNING: data has to have enough space for padding bytes, 40 | while (len < configDoCAN.candl) { 41 | data[len] = configDoCAN.padding; 42 | ++len; 43 | } 44 | 45 | return can_sender.SendFrame(data, configDoCAN.candl, configDoCAN.resp_id); 46 | } 47 | 48 | /// @brief ISO TP receive event handler 49 | /// @param event Type of receiving event 50 | /// @param result Event result (status) 51 | /// @param data Pointer to the income payload 52 | /// @param length Length of the income payload 53 | void OnIsoRxEvent(N_Event event, N_Result result, const uint8_t* data = nullptr, size_t length = 0); 54 | 55 | /// @brief ISO TP transmitt event handler 56 | /// @param event Type of transmitting event 57 | /// @param result Event result (status) 58 | void OnIsoTxEvent(N_Event event, N_Result result); 59 | 60 | /// @brief Sets ISO TP parameter 61 | /// @param name Parameter name 62 | /// @param value Parameter value 63 | /// @return Setting result 64 | SetParamResult SetParameter(ParName name, uint32_t value); 65 | 66 | /// @brief DoCAN instance static configuration 67 | typedef struct { 68 | /// @brief CAN message Id for the physically addressed CAN requests (income) 69 | uint32_t phys_id; 70 | /// @brief CAN message Id for the functionally addressed CAN requests (income) 71 | uint32_t func_id; 72 | /// @brief CAN message Id for the response messages (outcome) 73 | uint32_t resp_id; 74 | /// @brief CAN message payload capacity 75 | datasize_t candl; 76 | /// @brief CAN message outcome payload padding byte 77 | uint8_t padding; 78 | } configDoCAN_t; 79 | 80 | /// @brief DoCAN static configuration reading export 81 | /// @return Reference to the DoCAN configuration 82 | const configDoCAN_t& Config() const { 83 | return configDoCAN; 84 | } 85 | 86 | private: 87 | /// @brief Instance of ISO TP DoCAN sender 88 | DoCAN_Sender iso_sender; 89 | /// @brief Instance of ISO TP DoCAN receiver 90 | DoCAN_Receiver iso_receiver; 91 | 92 | /// @brief Reference to the physical CAN message sender 93 | ICAN_Sender& can_sender; 94 | /// @brief Reference to the ISO TP client (application level instance) 95 | IsoTpClient& iso_client; 96 | 97 | /// @brief PDU context info (data, length etc) 98 | IsoTpClient::IsoTpInfo pduContext{}; 99 | 100 | /// @brief ISO TP DoCAN configuration 101 | configDoCAN_t configDoCAN{}; 102 | }; 103 | 104 | template class Memgiver> 105 | class DoCAN_TP_Mem : public DoCAN_TP { 106 | 107 | public: 108 | DoCAN_TP_Mem(ICAN_Sender& s, IsoTpClient& c) : DoCAN_TP(rxalloc.ptr(), Rx, rxalloc.ptr(), Tx, s, c) {} 109 | 110 | private: 111 | Memgiver rxalloc; 112 | Memgiver txalloc; 113 | 114 | }; 115 | -------------------------------------------------------------------------------- /src/uds/inc/diag/sidnames.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | using sid_t = uint8_t; 7 | 8 | namespace sidhelper { 9 | 10 | /// @brief All UDS services 11 | /// @brief Diagnostic session control 12 | constexpr sid_t DSC = 0x10; 13 | 14 | /// @brief ECU reset 15 | constexpr sid_t ERES = 0x11; 16 | 17 | /// @brief Security Access 18 | constexpr sid_t SA = 0x27; 19 | 20 | /// @brief Communication control 21 | constexpr sid_t COMC = 0x28; 22 | 23 | /// @brief Tester present 24 | constexpr sid_t TP = 0x3E; 25 | 26 | /// @brief Access timing parameter 27 | constexpr sid_t ATP = 0x83; 28 | 29 | /// @brief Secured data transmission 30 | constexpr sid_t SDT = 0x84; 31 | 32 | /// @brief Control DTC settings 33 | constexpr sid_t CDTC = 0x85; 34 | 35 | /// @brief Response on event 36 | constexpr sid_t ROE = 0x86; 37 | 38 | /// @brief Link control 39 | constexpr sid_t LC = 0x87; 40 | 41 | /// @brief Read data by identifier 42 | constexpr sid_t RDBI = 0x22; 43 | 44 | /// @brief Read memory by address 45 | constexpr sid_t RMBA = 0x23; 46 | 47 | /// @brief Read scaling data by identifier 48 | constexpr sid_t RSDBI = 0x24; 49 | 50 | /// @brief Read data by periodic identifier 51 | constexpr sid_t RDBPI = 0x2A; 52 | 53 | /// @brief Dynamically define data identifier 54 | constexpr sid_t DDDI = 0x2C; 55 | 56 | /// @brief Write data by identifier 57 | constexpr sid_t WDBI = 0x2E; 58 | 59 | /// @brief Write memory by address 60 | constexpr sid_t WMBA = 0x3D; 61 | 62 | /// @brief Clear diagnostic information 63 | constexpr sid_t CDI = 0x14; 64 | 65 | /// @brief Read DTC information 66 | constexpr sid_t RDTC = 0x19; 67 | 68 | /// @brief Input output control by identifier 69 | constexpr sid_t IOCBI = 0x2F; 70 | 71 | /// @brief Routine control 72 | constexpr sid_t RC = 0x31; 73 | 74 | /// @brief Request download 75 | constexpr sid_t RD = 0x34; 76 | 77 | /// @brief Request upload 78 | constexpr sid_t RU = 0x35; 79 | 80 | /// @brief Transfer data 81 | constexpr sid_t TD = 0x36; 82 | 83 | /// @brief Request transfer exit 84 | constexpr sid_t RTE = 0x37; 85 | 86 | /// @brief Negative response 87 | constexpr sid_t NR_SI = 0x7f; 88 | 89 | 90 | /// @brief Service response bit indicator 91 | constexpr sid_t SID_RESPONSE_BIT = (1u << 6u); 92 | 93 | /// @brief Mask for getting service from response value 94 | constexpr sid_t SID_RESPONSE_MASK = ~SID_RESPONSE_BIT; 95 | 96 | // Diagnostic session levels (4) defined by standard (ISO 14299-1:2020) 97 | 98 | /// @brief Default diagnostic session level 99 | constexpr uint8_t DSC_LEV_DEFT = 1u; 100 | /// @brief Programming diagnostic session level 101 | constexpr uint8_t DSC_LEV_PRGM = 2u; 102 | /// @brief Extended diagnostic session level 103 | constexpr uint8_t DSC_LEV_EXTD = 3u; 104 | /// @brief Safety system diagnostic session level 105 | constexpr uint8_t DSC_LEV_SSYS = 4u; 106 | 107 | constexpr sid_t to_response(const sid_t sid) { 108 | 109 | return sid | SID_RESPONSE_BIT; 110 | } 111 | 112 | constexpr sid_t remove_response(const sid_t sid) { 113 | 114 | return (sid & SID_RESPONSE_MASK); 115 | } 116 | 117 | constexpr bool is_response(sid_t sid) { 118 | 119 | return ((sid & SID_RESPONSE_BIT) == SID_RESPONSE_BIT); 120 | } 121 | 122 | constexpr sid_t get_subfunc(const sid_t byte) { 123 | 124 | return (byte & 0x7fu); 125 | } 126 | 127 | constexpr bool is_pos_response_suppress(const sid_t secondbyte) { 128 | 129 | return (secondbyte & 0x80u) == 0x80u; 130 | } 131 | 132 | /// @brief Checks if current session value is default session 133 | /// @param sessvalue current session value 134 | /// @return true if the value is equal to DSC_LEV_DEFT, otherwise false 135 | constexpr bool is_dsc_def_session(uint8_t sessvalue) { 136 | 137 | return sessvalue == DSC_LEV_DEFT; 138 | } 139 | 140 | /// @brief Check if the service identificator can be handled 141 | /// @param sid Service value 142 | /// @return true if requested service can be handled, otherwise false 143 | constexpr bool is_sid_processable(const sid_t sid) { 144 | 145 | return ophelper::is_in_range<0x01, 0x3e>(remove_response(sid)) 146 | || ophelper::is_in_range<0xba, 0xbe>(remove_response(sid)) 147 | || ophelper::is_in_range<0x83, 0x88>(remove_response(sid)) 148 | || (sid == NR_SI); 149 | } 150 | 151 | } // namespace sidhelper 152 | -------------------------------------------------------------------------------- /src/uds/session/session-control.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | /// @brief UDS session layer abstraction 10 | /// Abstract class, must be expand but actual implementation 11 | class SessionControl : public IsoTpClient, public IProcessable { 12 | 13 | public: 14 | /// @brief sets new TP sender 15 | /// @param sender pointer to TP sender instance 16 | void SetIsoTp(IsoTpImpl* sender) { 17 | if (sender != nullptr) { 18 | host = sender; 19 | } 20 | } 21 | 22 | /// @brief General processing handler 23 | virtual void Process(); 24 | 25 | /// @brief ISO-TP event callback 26 | /// @param event iso-tp event type 27 | /// @param res iso-tp processing result 28 | virtual void OnIsoEvent(N_Event event, N_Result res, const IsoTpInfo& info) override; 29 | 30 | protected: 31 | /// @brief Payload sender 32 | /// @param data data pointer 33 | /// @param size data size 34 | /// @param enhanced enhanced session requested (p2 enhanced) 35 | void SendRequest(const uint8_t* data, uint32_t size, bool enhanced = false); 36 | 37 | /// @brief Sets session enhanced timing mode. During this mode NRC 0x78 38 | /// will be periodically sent with interval (ms) during duration (ms) 39 | /// While ETM is active all Data OK_r events will be ignored 40 | /// @param duration duration of etm 41 | /// @param interval interval to send keep-alive 42 | /// @param si service ID 43 | void SetPending(uint32_t duration, uint32_t interval, sid_t si); 44 | 45 | /// @brief Indication callback for implementaion 46 | /// @param data received data pointer 47 | /// @param size received data size 48 | /// @param addr target address 49 | virtual void OnSessIndication(const uint8_t* data, uint32_t size, TargetAddressType addr) = 0; 50 | 51 | /// @brief Confirmation callback for implementation 52 | /// @param res result 53 | virtual void OnSessConfirmation(S_Result res) = 0; 54 | 55 | /// @brief S3 timeout callback for implemention 56 | virtual void On_s3_Timeout() = 0; 57 | 58 | /// @brief Updates session type 59 | /// @param is_default true if default session is requested 60 | void SetSessionMode(bool is_default); 61 | 62 | /// @brief Session state processing 63 | void ProcessSessionMode(); 64 | 65 | /// @brief Session parameters setter 66 | /// @param par parameter type 67 | /// @param val parameter value 68 | SessParamResult SetSessionParam(SessParamType par, uint32_t val); 69 | 70 | /// @brief Timeout values descriptor 71 | typedef struct { 72 | 73 | uint32_t S3_max{5000}; 74 | uint32_t p2_max{250}; 75 | uint32_t p2_enhanced{5000}; 76 | } SessionTimings_t; 77 | 78 | /// @brief Timeouts 79 | SessionTimings_t tims; 80 | 81 | private: 82 | /// @brief Updates session state 83 | /// @param state session type to be set 84 | void SetSessionMode(SessionType state) { 85 | SetSessionMode((state == SessionType::DEFAULT) ? true : false); 86 | } 87 | 88 | /// @brief Stub class to avoid host nullptr value 89 | class EmptyTpSender : public IsoTpImpl { 90 | 91 | public: 92 | virtual IsoTpResult Request(const uint8_t*, size_t) override { 93 | return IsoTpResult::BUSY; 94 | } 95 | }; 96 | 97 | /// @brief Stub tp sender. does nothing 98 | EmptyTpSender fakeTpSender; 99 | 100 | /// @brief Pointer to tp implementation 101 | IsoTpImpl* host{&fakeTpSender}; 102 | 103 | DTimers::Timer p2; 104 | DTimers::Timer S3; 105 | 106 | /// @brief Current session state 107 | SessionType sessType{SessionType::DEFAULT}; 108 | /// @brief Target address type of the current req/resp session 109 | TargetAddressType targetAddress{TargetAddressType::UNKNOWN}; 110 | 111 | /// @brief Enhanced timing mode active status 112 | bool isEtmActive{false}; 113 | 114 | /// @brief ETM pending message 115 | uint8_t etmWaitRespBuff[3] = { 0x7fu, 0u, 0x78u }; 116 | 117 | /// @brief ETM maximum duration, after this time etm mode will be ended (ms) 118 | uint32_t etmDuration{0u}; 119 | 120 | /// @brief ETM pending message sending interval (ms) 121 | uint32_t etmKeepAliveInterval; 122 | 123 | /// @brief ETM interval timer 124 | DTimers::Timer etmTim{0u}; 125 | }; 126 | -------------------------------------------------------------------------------- /src/example/server-app.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "argcollector.h" 8 | #include "uds-test-server/serv-factory.h" 9 | #include "uds-test-server/cli-client.h" 10 | #include "app-helper.h" 11 | 12 | /* ---------------------------------------------------------------------------- */ 13 | std::string cmd; 14 | 15 | // name of socketcan interface for ISO-TP communication test 16 | static std::string ifname = "vcan0"; 17 | 18 | static CliMen& climen = GetClientUds(); 19 | 20 | /* ---------------------------------------------------------------------------- */ 21 | // get ISO tp instance to set its params from arguments in command line 22 | static DoCAN_TP& iso_tp = GetDoCAN(); 23 | // get CAN can_reader to process it in the loop 24 | static SocketCanReader can_reader(iso_tp); 25 | 26 | static void set_do_can_parameters(DoCAN_TP&, argsret& params) { 27 | 28 | uint32_t phys_id = 0x700u; 29 | uint32_t resp_id = 0x701u; 30 | uint32_t func_id = 0x7dfu; 31 | uint32_t stmin = 0u; 32 | uint32_t blksize = 8u; 33 | 34 | for (size_t i = 0; i < params.size(); i++) { 35 | if (params[i].first.compare("-blksize") == 0) { 36 | try_to_set_param(params[i], blksize); 37 | } else if (params[i].first.compare("-phys") == 0) { 38 | try_to_set_param(params[i], phys_id); 39 | } else if (params[i].first.compare("-resp") == 0) { 40 | try_to_set_param(params[i], resp_id); 41 | } else if (params[i].first.compare("-func") == 0) { 42 | try_to_set_param(params[i], func_id); 43 | } else if (params[i].first.compare("-stmin") == 0) { 44 | try_to_set_param(params[i], stmin); 45 | } else if (params[i].first.compare("-iface") == 0) { 46 | ifname = params[i].second; 47 | } 48 | } 49 | 50 | std::cout << "Init iso tp parameters:" << std::endl; 51 | 52 | std::cout << "BLKSIZE = " << (int)blksize << std::endl; 53 | iso_tp.SetParameter(ParName::BLKSZ, blksize); 54 | 55 | std::cout << "STMIN = " << (int)stmin << std::endl; 56 | iso_tp.SetParameter(ParName::ST_MIN, stmin); 57 | 58 | std::cout << std::hex; 59 | std::cout << "PHYS = " << (int)phys_id << std::endl; 60 | iso_tp.SetParameter(ParName::PHYS_ADDR, phys_id); 61 | 62 | std::cout << "RESP = " << (int)resp_id << std::endl; 63 | iso_tp.SetParameter(ParName::RESP_ADDR, resp_id); 64 | 65 | std::cout << "FUNC = " << (int)func_id << std::endl; 66 | std::cout << std::dec; 67 | 68 | iso_tp.SetParameter(ParName::FUNC_ADDR, func_id); 69 | } 70 | 71 | int main(int argc, char** argv) { 72 | 73 | auto params = collectargs(argc, argv); 74 | 75 | std::cout << " ----------- ECU simulation -------------- " << std::endl; 76 | set_do_can_parameters(iso_tp, params); 77 | std::cout << " ----------------------------------------- " << std::endl << std::endl; 78 | 79 | iso_tp.SetParameter(ParName::CANDL, 8); 80 | iso_tp.SetParameter(ParName::PADD_BYTE, 0xcc); 81 | 82 | std::cout << "Can channel name '" << ifname << "'" << std::endl; 83 | 84 | auto sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW); 85 | assert(sockfd > 0); 86 | 87 | struct ifreq can_iface; 88 | strcpy(can_iface.ifr_name, ifname.c_str()); 89 | 90 | assert(ioctl(sockfd, SIOCGIFINDEX, &can_iface) >= 0); 91 | 92 | struct sockaddr_can loc_addr; 93 | bzero(&loc_addr, sizeof(loc_addr)); 94 | loc_addr.can_ifindex = can_iface.ifr_ifindex; 95 | loc_addr.can_family = AF_CAN; 96 | 97 | /* Check MTU of interface */ 98 | assert(ioctl(sockfd, SIOCGIFMTU, &can_iface) >= 0); 99 | assert(fcntl(sockfd, F_SETFL, (fcntl(sockfd, F_GETFL) | O_NONBLOCK)) >= 0); 100 | assert(bind(sockfd, (struct sockaddr*)&loc_addr, sizeof(loc_addr)) >= 0); 101 | 102 | std::cout << "Started succesfully." << std::endl; 103 | 104 | // Bind FD to can_reader 105 | can_reader.SetSocket(sockfd); 106 | // Bind FD to sender 107 | GetCanSender().SetSocket(sockfd); 108 | 109 | std::array buffer; 110 | 111 | for (size_t i = 0; i < buffer.size(); buffer[i] = static_cast(i), i++); 112 | 113 | std::thread th1([&]() { 114 | climen.Run(); 115 | }); 116 | 117 | BuildApp(); 118 | 119 | std::string readcmd; 120 | 121 | while (true) { 122 | GetMainProcHandler().Process(); 123 | can_reader.Process(); 124 | 125 | if (climen.IsCmd()) { 126 | auto payload = climen.GetData(); 127 | iso_tp.Request(payload.data(), payload.size()); 128 | } 129 | 130 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 131 | } 132 | 133 | th1.join(); 134 | std::cout << "Exit ... " << std::endl; 135 | } 136 | -------------------------------------------------------------------------------- /src/example/uds-test-server/serv-factory.cpp: -------------------------------------------------------------------------------- 1 | #include "serv-factory.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | constexpr size_t RxBufferSize = 8192; 15 | constexpr size_t TxBufferSize = 8192; 16 | 17 | SocketCanSender& GetCanSender() { 18 | 19 | static SocketCanSender sender; 20 | return sender; 21 | } 22 | 23 | SessionInfo& GetSessionInfoInstance() { 24 | 25 | static SessionInfo instance; 26 | return instance; 27 | } 28 | 29 | UdsAppManager& GetBaseUdsServer() { 30 | 31 | constexpr size_t MAX_ARRAY = 1024u; 32 | static uint8_t serv_array[MAX_ARRAY] {0}; 33 | static UdsAppManager si_router(serv_array, MAX_ARRAY, GetSessionInfoInstance()); 34 | return si_router; 35 | } 36 | 37 | DoCAN_TP& GetDoCAN() { 38 | 39 | static DoCAN_TP_Mem isotpsource(GetCanSender(), GetBaseUdsServer()); 40 | 41 | return isotpsource; 42 | } 43 | 44 | static ProcRunner<4>& GetProcRunner() { 45 | 46 | static ProcRunner<4> procrunner; 47 | return procrunner; 48 | } 49 | 50 | IProcessable& GetMainProcHandler() { 51 | 52 | return GetProcRunner(); 53 | } 54 | 55 | void BuildApp() { 56 | 57 | static TickerWrapper ticker; 58 | static DSCClient dschandler(GetBaseUdsServer(), GetSessionInfoInstance()); 59 | 60 | static DidKeeper<4> didkeeper; 61 | static TestDidReader didreader(GetSessionInfoInstance()); 62 | static DidRouter didrouter(GetBaseUdsServer(), didkeeper); 63 | 64 | static MultiRoutineHandler<4> rkeeper; 65 | static RoutineRouter baser(GetBaseUdsServer(), rkeeper); 66 | static RotineServ1 r1(baser); 67 | static RotineServ2 r2(baser); 68 | 69 | static MultiServiceManager<3> serviceHandlers(GetBaseUdsServer()); 70 | 71 | serviceHandlers.Add(&dschandler); 72 | serviceHandlers.Add(&didrouter); 73 | serviceHandlers.Add(&baser); 74 | 75 | static ProxyUdsAppClient proxyClient(GetBaseUdsServer(), serviceHandlers); 76 | 77 | GetBaseUdsServer().SetClient(&proxyClient); 78 | 79 | rkeeper.Add(&r1); 80 | rkeeper.Add(&r2); 81 | 82 | GetProcRunner().Add(&ticker); 83 | GetProcRunner().Add(&GetDoCAN()); 84 | GetProcRunner().Add(&GetBaseUdsServer()); 85 | GetProcRunner().Add(&r1); 86 | GetBaseUdsServer().SetIsoTp(&GetDoCAN()); 87 | 88 | didkeeper.Add(&didreader); 89 | } 90 | 91 | CliMen& GetClientUds() { 92 | 93 | static Menu sessctrl = Menu("Session Control"); 94 | static Menu readdid = Menu("Read DID"); 95 | static Menu reqroutine = Menu("Routine request"); 96 | static Menu defsess = Menu("Default"); 97 | static Menu extsess = Menu("Extended"); 98 | static Menu prgsess = Menu("Programming"); 99 | static Menu read22 = Menu("Read 22"); 100 | static Menu readSession = Menu("Read Session"); 101 | static Menu readSecurity = Menu("Read Security"); 102 | static Menu tester = Menu("Tester Present"); 103 | 104 | static Menu routine1 = Menu("Routine #1 (0x0102)"); 105 | static Menu routine2 = Menu("Routine #2 (0xff00)"); 106 | 107 | static Menu securitymenu = Menu("Security control"); 108 | 109 | static Menu securityreq_1 = Menu("Security lev 1"); 110 | static Menu securityreq_2 = Menu("Security lev 2"); 111 | static Menu securityreq_3 = Menu("Security lev 3"); 112 | 113 | defsess.cmd = { 0x10, 0x01 }; 114 | extsess.cmd = { 0x10, 0x03 }; 115 | prgsess.cmd = { 0x10, 0x02 }; 116 | securityreq_1.cmd = { 0x27, 0x01 }; 117 | securityreq_2.cmd = { 0x27, 0x03 }; 118 | securityreq_3.cmd = { 0x27, 0x05 }; 119 | 120 | read22.cmd = { 0x22, 0x22, 0x00 }; 121 | readSession.cmd = { 0x22, 0x00, 0xa0 }; 122 | readSecurity.cmd = { 0x22, 0x00, 0xa1 }; 123 | tester.cmd = { 0x3e, 0x00 }; 124 | 125 | routine1.cmd = { 0x31, 0x01, 0x01, 0x02, 1 }; 126 | routine2.cmd = { 0x31, 0x01, 0xff, 0x00, 1 }; 127 | 128 | sessctrl.SetNext(&readdid)->SetNext(&tester)->SetNext(&reqroutine)->SetNext(&securitymenu); 129 | sessctrl.SetDown(&defsess); 130 | 131 | defsess.SetNext(&extsess); 132 | extsess.SetNext(&prgsess); 133 | 134 | readdid.SetDown(&read22)->SetNext(&readSession)->SetNext(&readSecurity); 135 | readdid.SetNext(&tester); 136 | 137 | reqroutine.SetDown(&routine1); 138 | routine1.SetNext(&routine2); 139 | 140 | securitymenu.SetDown(&securityreq_1)->SetNext(&securityreq_2)->SetNext(&securityreq_3); 141 | 142 | static CliMen client(&sessctrl); 143 | return client; 144 | } 145 | -------------------------------------------------------------------------------- /src/uds/isotp/pci-helper.cpp: -------------------------------------------------------------------------------- 1 | #include "pci-helper.h" 2 | #include "../inc/iso-tp-const.h" 3 | 4 | datasize_t PciHelper::UnpackPciInfo(const uint8_t* data, datasize_t candl, PciMeta& pci) { 5 | 6 | datasize_t ret = 0; 7 | pci.type = DC_FrameType::ERROR; 8 | pci.pcilen = ret; 9 | 10 | if (candl == 0) { 11 | return ret; 12 | } 13 | 14 | pci.type = from_byte(data[0] & 0xf0u); 15 | 16 | switch (pci.type) { 17 | case (DC_FrameType::SF): 18 | if (candl <= CLASSICCAN_DL_MAX) { 19 | // classic SF 20 | ret = 1; 21 | pci.dlen = data[0] & 0x0fu; 22 | 23 | if ((pci.dlen + ret) > candl) { 24 | ret = 0; 25 | pci.type = DC_FrameType::ERROR; 26 | } 27 | } else if (candl <= CANFD_DL_MAX) { 28 | // CAN FD 29 | ret = 2; 30 | pci.dlen = data[1]; 31 | 32 | if (pci.dlen + ret > candl) { 33 | ret = 0; 34 | pci.type = DC_FrameType::ERROR; 35 | } 36 | } else { 37 | ret = 0; 38 | pci.type = DC_FrameType::ERROR; 39 | } 40 | 41 | break; 42 | 43 | case (DC_FrameType::FF): 44 | if (candl < MIN_FF_CANDL) { 45 | ret = 0; 46 | pci.type = DC_FrameType::ERROR; 47 | } else { 48 | ret = 2; 49 | pci.dlen = (((data[0] & 0x0fu) << 8) | (data[1])); 50 | 51 | if (pci.dlen == 0) { 52 | // > 4095 53 | ret = 6; 54 | pci.dlen = (((data[2] & 0xFF) << 24) | 55 | ((data[3] & 0xFF) << 16) | 56 | ((data[4] & 0xFF) << 8) | 57 | ((data[5] & 0xFF) << 0)); 58 | 59 | if (pci.dlen <= 0xfffu) { 60 | // error dlen value, reset it zero to ignore handling 61 | pci.type = DC_FrameType::ERROR; 62 | pci.dlen = 0; 63 | ret = 0; 64 | } 65 | } else if (pci.dlen < MIN_FF_PAYLOAD_SIZE) { 66 | // error, FF min possible length is 8 67 | pci.type = DC_FrameType::ERROR; 68 | pci.dlen = 0; 69 | ret = 0; 70 | } 71 | } 72 | 73 | break; 74 | 75 | case (DC_FrameType::FC): { 76 | uint8_t fs = (data[0] & 0x0fu); 77 | 78 | if (fs > 2 || candl < 3) { 79 | ret = 0; 80 | pci.type = DC_FrameType::ERROR; 81 | } else { 82 | ret = 3; 83 | pci.flowstate = from_byte(fs); 84 | pci.bs = data[1]; 85 | pci.stmin = data[2]; 86 | 87 | if (((pci.stmin > 0x7f) && (pci.stmin <= 0xf0)) || pci.stmin > 0xf9) { 88 | ret = 0; 89 | pci.type = DC_FrameType::ERROR; 90 | } else if (pci.stmin > 0x7f) { 91 | // 1 ms for range 100..900 us 92 | pci.stmin = 1; 93 | } 94 | } 95 | } 96 | 97 | break; 98 | 99 | case (DC_FrameType::CF): 100 | ret = 1; 101 | pci.sn = data[0] & 0x0f; 102 | 103 | break; 104 | 105 | case (DC_FrameType::ERROR): 106 | default: 107 | ret = 0; 108 | pci.type = DC_FrameType::ERROR; 109 | break; 110 | } 111 | 112 | pci.pcilen = ret; 113 | return ret; 114 | } 115 | 116 | datasize_t PciHelper::PackPciForData(uint8_t* data, datasize_t length, datasize_t candl, DC_FrameType& reftype) { 117 | 118 | datasize_t pci_len = 0; 119 | 120 | if (length > 0) { 121 | bool is_can_fd = (candl > CLASSICCAN_DL_MAX); 122 | pci_len = (is_can_fd) ? (2) : (1); 123 | 124 | if ((pci_len + length) <= candl) { 125 | // SF: full length is less or equal to candl 126 | reftype = DC_FrameType::SF; 127 | data[0] = to_byte(reftype); 128 | 129 | if (is_can_fd) { 130 | // CAN FD SF 131 | data[1] = length & 0xFFU; 132 | pci_len = 2; 133 | } else { 134 | // Classic SF 135 | data[0] |= (length & 0x0fu); 136 | pci_len = 1; 137 | } 138 | } else { 139 | // FF 140 | reftype = DC_FrameType::FF; 141 | data[0] = to_byte(reftype); 142 | 143 | if (length <= 0xfff) { 144 | data[0] |= ((length >> 8u) & 0x0fu); 145 | data[1] = length & 0xff; 146 | pci_len = 2; 147 | } else { 148 | // FF > 4095 (0xfff) 149 | data[1] = 0; 150 | data[2] = ((length >> 24) & 0xFF); 151 | data[3] = ((length >> 16) & 0xFF); 152 | data[4] = ((length >> 8) & 0xFF); 153 | data[5] = ((length >> 0) & 0xFF); 154 | pci_len = 6; 155 | } 156 | } 157 | } 158 | 159 | return pci_len; 160 | } 161 | 162 | datasize_t PciHelper::PackFlowControl(uint8_t* data, DC_FlowState state, uint8_t bs, uint8_t stmin) { 163 | 164 | data[0] = to_byte(DC_FrameType::FC) | to_byte(state); 165 | data[1] = bs; 166 | data[2] = stmin; 167 | return 3; 168 | } 169 | 170 | datasize_t PciHelper::PackConseqFrame(uint8_t* data, uint8_t sn) { 171 | 172 | data[0] = static_cast(DC_FrameType::CF) | (sn & 0x0fu); 173 | return 1; 174 | } 175 | -------------------------------------------------------------------------------- /src/uds/isotp/docan-receiver.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "docan-receiver.h" 4 | #include "docan-tp.h" 5 | #include "pci-helper.h" 6 | 7 | void DoCAN_Receiver::ProcessRx() { 8 | 9 | if (rxds.state == RxState::ACTIVE) { 10 | if (Cr_tim.Elapsed()) { 11 | // no consequitive frame in time 12 | rxds.state = RxState::IDLE; 13 | rxds.passed = 0; 14 | rxds.rxsize = 0; 15 | itp.OnIsoRxEvent(N_Event::Conf, N_Result::TIMEOUT_Cr); 16 | } 17 | } 18 | } 19 | 20 | 21 | void DoCAN_Receiver::Receive(const uint8_t* data, datasize_t candl) { 22 | 23 | PciHelper helper; 24 | PciHelper::PciMeta inf; 25 | datasize_t pcioffset = helper.UnpackPciInfo(data, candl, inf); 26 | 27 | assert(pcioffset <= candl && candl < CANFD_DL_MAX); 28 | 29 | if (pcioffset > 0) { 30 | switch (inf.type) { 31 | case (DC_FrameType::FF): 32 | if (rxds.state == RxState::ACTIVE) { 33 | // reception is in progress (ISO 15765-2 tab 23 (p 38)) 34 | itp.OnIsoRxEvent(N_Event::Data, N_Result::UNEXP_PDU); 35 | } 36 | 37 | // start new multi reception (cannot be less than 7 bytes) 38 | if (inf.dlen < MIN_FF_CANDL) { 39 | // Ignore case (ISO 15765-2 9.6.3.2 (p 29)) 40 | } else if (inf.dlen > RXLEN) { 41 | rxds.state = RxState::IDLE; 42 | // send FC with overflow status 43 | auto ret = helper.PackFlowControl(can_message, DC_FlowState::OVERFLOW, 0, 0); 44 | itp.PduToCan(can_message, ret); 45 | } else { 46 | // fine, reception can be processed 47 | auto ret = helper.PackFlowControl(can_message, DC_FlowState::CTS, rxds.blksize, rxds.stmin); 48 | itp.PduToCan(can_message, ret); 49 | 50 | rxds.currblkn = 0; 51 | rxds.rxsize = inf.dlen; 52 | datasize_t cpylen = candl - pcioffset; 53 | memcpy(rxbuff + rxds.passed, data + pcioffset, cpylen); 54 | rxds.passed = cpylen; 55 | rxds.expectsn = 1; 56 | itp.OnIsoRxEvent(N_Event::DataFF, N_Result::OK_r, nullptr, rxds.rxsize); 57 | // start timer 58 | rxds.state = RxState::ACTIVE; 59 | Cr_tim.Restart(); 60 | } 61 | 62 | break; 63 | 64 | case (DC_FrameType::CF): 65 | if (rxds.state != RxState::ACTIVE || pcioffset != 1) { 66 | rxds.state = RxState::IDLE; 67 | itp.OnIsoRxEvent(N_Event::Data, N_Result::UNEXP_PDU); 68 | } else { 69 | if (inf.sn != (rxds.expectsn & 0xfu)) { 70 | // Abort (ISO 15765 9.6.4.4 (p.30)) 71 | rxds.state = RxState::IDLE; 72 | itp.OnIsoRxEvent(N_Event::Data, N_Result::UNEXP_PDU); 73 | } else { 74 | Cr_tim.Restart(); 75 | datasize_t cpylen = candl - pcioffset; 76 | 77 | if ((rxds.passed + cpylen) > rxds.rxsize) { 78 | cpylen = rxds.rxsize - rxds.passed; 79 | } 80 | 81 | assert(cpylen <= candl); 82 | 83 | memcpy(rxbuff + rxds.passed, data + pcioffset, cpylen); 84 | rxds.passed += cpylen; 85 | ++rxds.expectsn; 86 | ++rxds.currblkn; 87 | 88 | if (rxds.passed == rxds.rxsize) { 89 | // reception ended, notify client 90 | rxds.state = RxState::IDLE; 91 | itp.OnIsoRxEvent(N_Event::Data, N_Result::OK_r, rxbuff, rxds.rxsize); 92 | } else if (rxds.currblkn >= rxds.blksize) { 93 | // block ended, send FC 94 | auto ret = helper.PackFlowControl(can_message, DC_FlowState::CTS, rxds.blksize, rxds.stmin); 95 | itp.PduToCan(can_message, ret); 96 | rxds.currblkn = 0; 97 | } 98 | } 99 | } 100 | 101 | break; 102 | 103 | case (DC_FrameType::SF): 104 | if (rxds.state == RxState::ACTIVE) { 105 | // ISO 15765-2 tab 23 (p 38) Notify unexpected pdu, abort multi reception 106 | // and process SF payload 107 | rxds.state = RxState::IDLE; 108 | itp.OnIsoRxEvent(N_Event::Data, N_Result::UNEXP_PDU); 109 | } 110 | 111 | itp.OnIsoRxEvent(N_Event::Data, N_Result::OK_r, data + pcioffset, inf.dlen); 112 | 113 | break; 114 | 115 | default: 116 | if (rxds.state == RxState::ACTIVE) { 117 | // unexpected PDU during active reception 118 | rxds.state = RxState::IDLE; 119 | itp.OnIsoRxEvent(N_Event::Conf, N_Result::UNEXP_PDU); 120 | } 121 | 122 | break; 123 | } 124 | } 125 | } 126 | 127 | SetParamResult DoCAN_Receiver::SetParameter(ParName name, uint32_t v) { 128 | 129 | auto ret = SetParamResult::OK; 130 | 131 | switch (name) { 132 | case (ParName::BLKSZ): 133 | rxds.blksize = v; 134 | break; 135 | 136 | case (ParName::ST_MIN): 137 | rxds.stmin = v; 138 | break; 139 | 140 | case (ParName::Cr_TIM_ms): 141 | Cr_tim.Start(v); 142 | Cr_tim.Stop(); 143 | break; 144 | 145 | default: 146 | ret = SetParamResult::WRONG_PARAMETER; 147 | break; 148 | } 149 | 150 | return ret; 151 | } 152 | -------------------------------------------------------------------------------- /src/uds/session/session-control.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "session-control.h" 3 | #include 4 | 5 | void SessionControl::SendRequest(const uint8_t* data, uint32_t size, bool enhanced) { 6 | 7 | isEtmActive = enhanced; 8 | 9 | if (isEtmActive) { 10 | p2.Start(tims.p2_enhanced); 11 | } else { 12 | // stop p2 timer on response sending 13 | p2.Stop(); 14 | } 15 | 16 | // send response to iso-tp 17 | host->Request(data, size); 18 | } 19 | 20 | void SessionControl::SetPending(uint32_t duration, uint32_t interval, sid_t si) { 21 | 22 | if ((duration >= interval) && (interval > 999u)) { 23 | etmWaitRespBuff[1] = si; 24 | SendRequest(etmWaitRespBuff, 3u, true); 25 | etmTim.Start(interval); 26 | etmKeepAliveInterval = interval; 27 | etmDuration = duration - interval; 28 | } 29 | } 30 | 31 | void SessionControl::Process() { 32 | 33 | // process timers here and notify Diag in case of timeout 34 | ProcessSessionMode(); 35 | 36 | if (isEtmActive) { 37 | // left duration not zero, continue periodic send 38 | if ((etmDuration != 0u) && etmTim.Elapsed()) { 39 | // etm pending message has to be sent 40 | etmDuration = (etmDuration > etmKeepAliveInterval) ? (etmDuration - etmKeepAliveInterval) : 0u; 41 | SendRequest(etmWaitRespBuff, 3, true); 42 | 43 | if (etmDuration == 0) { 44 | // after this p2 timer will handled by general session processing 45 | etmTim.Stop(); 46 | } 47 | } 48 | } 49 | } 50 | 51 | void SessionControl::OnIsoEvent(N_Event event, N_Result res, const IsoTpInfo& info) { 52 | 53 | targetAddress = (info.address == N_TarAddress::TAtype_1_Physical) ? (TargetAddressType::PHYS) : (TargetAddressType::FUNC); 54 | 55 | switch (event) { 56 | case (N_Event::Data): 57 | if (res == N_Result::OK_r) { 58 | if (!isEtmActive) { 59 | // data indication from Transport layer 60 | p2.Start(tims.p2_max); 61 | 62 | if (sessType == SessionType::NONDEFAULT) { 63 | // stop s3 here (to be restart on response conf) 64 | S3.Stop(); 65 | } 66 | 67 | OnSessIndication(info.data, info.length, targetAddress); 68 | } else { 69 | // do not process any request while etm is active 70 | } 71 | } else { 72 | // unexpected result 73 | OnSessConfirmation(S_Result::NOK); 74 | } 75 | 76 | break; 77 | 78 | case (N_Event::DataFF): 79 | if (res == N_Result::OK_r) { 80 | if (sessType == SessionType::NONDEFAULT) { 81 | // stop s3 here to be aviod session stop during long receiving 82 | S3.Stop(); 83 | } 84 | } else { 85 | // should never get here 86 | SetSessionMode(sessType); 87 | assert(false); 88 | } 89 | 90 | break; 91 | 92 | case (N_Event::Conf): 93 | if (res == N_Result::OK_s) { 94 | if (!isEtmActive) { 95 | // update current session state 96 | SetSessionMode(sessType); 97 | } 98 | } else if (res == N_Result::TIMEOUT_Cr) { 99 | // segmented receptions is broken 100 | SetSessionMode(sessType); 101 | } else { 102 | // error occured 103 | } 104 | } 105 | 106 | return; 107 | } 108 | 109 | 110 | void SessionControl::SetSessionMode(bool is_default) { 111 | 112 | if (is_default) { 113 | // default session doesn't use s3 timer, so stop it 114 | S3.Stop(); 115 | sessType = SessionType::DEFAULT; 116 | } else { 117 | if (sessType == SessionType::DEFAULT) { 118 | // initial start of S3 timer 119 | S3.Start(tims.S3_max); 120 | sessType = SessionType::NONDEFAULT; 121 | } else { 122 | // already not default -> subsequent start 123 | S3.Restart(); 124 | } 125 | } 126 | } 127 | 128 | 129 | void SessionControl::ProcessSessionMode() { 130 | 131 | if (sessType != SessionType::DEFAULT) { 132 | if (S3.Elapsed()) { 133 | SetSessionMode(true); 134 | On_s3_Timeout(); 135 | } 136 | } 137 | 138 | if (p2.Elapsed()) { 139 | if (sessType != SessionType::DEFAULT) { 140 | // Restart S3 timer! 141 | S3.Restart(); 142 | } 143 | 144 | if (isEtmActive) { 145 | // no response from handler was sent 146 | isEtmActive = false; 147 | // send common NRC (protocol requirement) 148 | etmWaitRespBuff[2] = NRC_to_byte(NRCs::ENOA); 149 | SendRequest(etmWaitRespBuff, 3u); 150 | // restore original NRC value in etm buffer 151 | etmWaitRespBuff[2] = NRC_to_byte(NRCs::RCRRP); 152 | } 153 | } 154 | } 155 | 156 | SessParamResult SessionControl::SetSessionParam(SessParamType par, uint32_t v) { 157 | 158 | auto ret = SessParamResult::OK; 159 | 160 | switch (par) { 161 | case (SessParamType::S3_TIM): 162 | tims.S3_max = v; 163 | break; 164 | 165 | case (SessParamType::P2_TIM): 166 | tims.p2_max = v; 167 | break; 168 | 169 | case (SessParamType::P2_ENHC): 170 | tims.p2_enhanced = v; 171 | break; 172 | 173 | default: 174 | ret = SessParamResult::ERR; 175 | break; 176 | } 177 | 178 | return ret; 179 | } 180 | 181 | -------------------------------------------------------------------------------- /src/uds/inc/iso-tp-types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "typedefs.h" 7 | 8 | /// @brief Network event result 9 | enum class N_Result : enumbase_t { 10 | // This value means that the service execution has been completed successfully; it can be issued to a 11 | // service user on both the sender and receiver sides. 12 | OK_s, 13 | OK_r, 14 | // This value is issued to the protocol user when the timer N_Ar / N_As has passed its time - out value 15 | // N_Asmax / N_Armax; it can be issued to service users on both the sender and receiver sides. 16 | TIMEOUT_As, 17 | TIMEOUT_Ar, 18 | // This value is issued to the service user when the timer N_Bs has passed its time - out value N_Bsmax; 19 | // it can be issued to the service user on the sender side only. 20 | TIMEOUT_Bs, 21 | // This value is issued to the service user when the timer N_Cr has passed its time - out value N_Crmax; 22 | // it can be issued to the service user on the receiver side only. 23 | TIMEOUT_Cr, 24 | // This value is issued to the service user upon receipt of an unexpected SequenceNumber (PCI.SN) 25 | // value; it can be issued to the service user on the receiver side only. 26 | WRONG_SN, 27 | // This value is issued to the service user when an invalid or unknown FlowStatus value has been 28 | // received in a FlowControl (FC) N_PDU; it can be issued to the service user on the sender side only. 29 | INVALID_FS, 30 | // This value is issued to the service user upon receipt of an unexpected protocol data unit; it can be 31 | // issued to the service user on the receiver side only. 32 | UNEXP_PDU, 33 | // This value is issued to the service user when the receiver has transmitted N_WFTmax FlowControl 34 | // N_PDUs with FlowStatus = WAIT in a row and following this, it cannot meet the performance 35 | // requirement for the transmission of a FlowControl N_PDU with FlowStatus = ClearToSend. It can be 36 | // issued to the service user on the receiver side only. 37 | WFT_OVRN, 38 | // This value is issued to the service user upon receipt of a FlowControl (FC) N_PDU with 39 | // FlowStatus = OVFLW. It indicates that the buffer on the receiver side of a segmented message 40 | // transmission cannot store the number of bytes specified by the FirstFrame DataLength (FF_DL) 41 | // parameter in the FirstFrame and therefore the transmission of the segmented message was 42 | // aborted. It can be issued to the service user on the sender side only. 43 | BUFFER_OVFLW, 44 | // This is the general error value. It shall be issued to the service user when an error has been detected 45 | // by the network layer and no other parameter value can be used to better describe the error. It can 46 | // be issued to the service user on both the sender and receiver sides. 47 | ERROR_s, 48 | ERROR_r 49 | }; 50 | 51 | /// @brief Result of attempt to change parameter 52 | enum class SetParamResult : enumbase_t { 53 | // This value means that the service execution has been completed successfully; it can be issued to a 54 | // service user on both the sender and receiver sides. 55 | OK, 56 | // This value is issued to the service user to indicate that the service did not execute since reception 57 | // of the message identified by was taking place; it can be issued to the service user on the 58 | // receiver side only. 59 | RX_ON, 60 | // This value is issued to the service user to indicate that the service did not execute due to an 61 | // undefined ; it can be issued to a service user on both the receiver and sender sides. 62 | WRONG_PARAMETER, 63 | // This value is issued to the service user to indicate that the service did not execute due to an out - ofrange 64 | // ; it can be issued to a service user on both the receiver and sender sides. 65 | WRONG_VALUE 66 | }; 67 | 68 | /// @brief List of parameters 69 | enum class ParName : enumbase_t { 70 | ST_MIN, 71 | BLKSZ, 72 | PHYS_ADDR, 73 | FUNC_ADDR, 74 | RESP_ADDR, 75 | CANDL, 76 | As_TIM_ms, 77 | Bs_TIM_ms, 78 | Br_TIM_ms, 79 | Cr_TIM_ms, 80 | PADD_BYTE, 81 | }; 82 | 83 | 84 | /// @brief Network event type 85 | enum class N_Event : enumbase_t { 86 | Conf, 87 | Data, 88 | DataFF, 89 | }; 90 | 91 | /// @brief Result of request ot iso-tp 92 | enum class IsoTpResult : enumbase_t { 93 | OK, 94 | BUSY, 95 | WRONG_STATE, 96 | OVERFLOW 97 | }; 98 | 99 | /// @brief List of possible CAN datalength 100 | enum class CanDl : enumbase_t { 101 | CANDL_8 = 8, 102 | CANDL_12 = 12, 103 | CANDL_16 = 16, 104 | CANDL_20 = 20, 105 | CANDL_24 = 24, 106 | CANDL_32 = 32, 107 | CANDL_48 = 48, 108 | CANDL_64 = 64 109 | }; 110 | 111 | /// @brief ISO-TP target address 112 | enum class N_TarAddress { 113 | TAtype_Invalid = 0, 114 | // CAN base format (CLASSICAL CAN, 11-bit) 115 | TAtype_1_Physical, 116 | TAtype_2_Functional, 117 | // CAN FD base format (CAN FD, 11-bit) 118 | TAtype_3_Physical, 119 | TAtype_4_Functional, 120 | // CAN extended format (CLASSICAL CAN, 29-bit) 121 | TAtype_5_Physical, 122 | TAtype_6_Functional, 123 | // CAN FD extended format (CAN FD, 29-bit) 124 | TAtype_7_Physical, 125 | TAtype_8_Functional 126 | }; 127 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UDS TO GO 2 | ## Lightwieght and simple to use UDS/DoCAN stack for MCU and other platforms 3 | 4 | ## Fast start and test 5 | Repo has a test application based on socketCAN system which can be build and run to test and play with ISO-TP 6 | To build and run test app (src/example/main.cpp) you have to have: 7 | - Ubuntu PC (I didn't test it on any other Linux distributives) 8 | - **can-utils** installed on you PC (optional, just to use **isotpdump**) 9 | - **cmake** and C++ development toolkit (c++11 standard mandatory) 10 | 11 | ### Step 1 12 | Install can-utils and build tools if necessary 13 | ### Step 2 14 | Open terminal and go to the source code directory. Make directory for repo 15 | ``` 16 | mkdir uds-to-go 17 | ``` 18 | ### Step 2 19 | Clone repository from github 20 | ``` 21 | git clone https://github.com/astand/uds-to-go.git uds-to-go 22 | ``` 23 | ### Step 3 24 | Go to the repo directory and run build. There are also predefined shell scripts to run 25 | two instances of ISO-TP with test parameters. To use them they have to be executable 26 | ``` 27 | cd uds-to-go 28 | cmake -S src -B build 29 | cmake --build build 30 | chmod u+x start-client.sh && chmod u+x start-serv.sh 31 | ``` 32 | ### Step 4 33 | ISO test app requiers alive socketCAN interface. Test scripts use interface 'vcan0', to start it up run the next commnad: 34 | ``` 35 | sudo ip link add name vcan0 type vcan 36 | sudo ip link set dev vcan0 up 37 | ``` 38 | > To get vcan0 down use command: 39 | > _sudo ip link delete vcan0_ 40 | 41 | ### Step 5 42 | Open three terminals and run next commands in each of them: 43 | ``` 44 | ./start-client.sh 45 | ``` 46 | ``` 47 | ./start-serv.sh 48 | ``` 49 | ``` 50 | isotpdump vcan0 -s 0x700 -d 0x701 -t d 51 | ``` 52 | After that you can send ISO-TP payloads with different length from server to client ang vice versa. 53 | You can also inspect CAN log in the ***isotpdump*** terminal 54 | 55 | _Example of using server-client communication with isotpdump logging_ 56 | ![Drag Racing](docs/test-run-example.png) 57 | 58 | 59 | # Basic code explanation 60 | 61 | The driver was made as light as possible (in a resonable extent). It completely ready to be used on MCU and 62 | any kind of other embedded devices 63 | 64 | The main features: 65 | - No dynamic memory allocation 66 | - No exception throwing 67 | - Possibility to have more than one UDS instances in the same time 68 | 69 | 70 | To integrate UDS driver to your project you have to provide adapters from low-level side 71 | and make base class implementation from the APP side 72 | 73 | ## Low level adaptation 74 | ### Timers 75 | Both layers (network and session) use timers. To process timer you have to call static method of TickerCounter class periodically with frequency 1 Hz. In the example project it has been made by wrapping this call to class ***TickerWrapper*** (_src/example/ticker-wrapper.h_) which method _Process_ is called from main thread. It Processes core tick counter each 1 ms. For MCU implementation you can put this call _Timers::TickerCounter::ProcessTick()_ inside timer IRQ hanlder Attention: timers will work properly only if the size of core timer counter type (32-bit by default) is the same as the system base bit-width 76 | As long as your system bit width is the same as the core timer counter type (32bit by default) everything is ok, timer is safe to be used in multithread environment, because ticker increment is going to be atomic. In other cases it is unsafe to use it with default implementation 77 | ### CAN Sender 78 | You have to implement **_ICAN_Sender_** interface. Its main task is to send CAN frames to diagnostic CAN bus. 79 | There is no difference if you use block approach (direct access to CAN HW and waiting transmittion compeletence) or async approach (with intermediate buffering). You have to return meaningful result. 80 | > In example I use socketCAN wrapper ***CanSender*** (src/example/can-bridge.h) 81 | 82 | Object which implements this interface must be binded to DoCAN_TP object by passing it as the first argument of DoCAN_TP constructor 83 | ### CAN Listener 84 | You have to process **_ICAN_Listenere_** interface of DoCAN_TP object. In exmaple I made ***CanListener*** class (_src/example/can-bridge.h_) which passes all CAN frames from SocetCAN to DoCAN_TP instance which is binded to CanListener by passing a reference to constructor 85 | 86 | ### Processing 87 | Some classes implement interface IProcessable. This is quite useful for making processing more simple in the main thread. DoCAN_TP and SessionControl classes implement this interface. The TimerWrapper also implements this interface. 88 | There is also class **_ProcRunner_** which is able to collect a few IProcessable instances and run them all in main thread 89 | ```c 90 | ProcRunner<4> procrunner; 91 | 92 | procrunner.Add(&ticker); 93 | procrunner.Add(&iso_tp); 94 | procrunner.Add(&listener); 95 | 96 | while (true) 97 | { 98 | procrunner.RunAllProcess(); 99 | ... 100 | sleep(1ms); 101 | } 102 | ``` 103 | This processing aproach is optional. You can use any other way to make periodic processing all these instances. 104 | 105 | From this point you are ready to send and receive ISO TP packets, as it is main in udstogo-test app. 106 | Don't forget to set DoCAN_TP parameters (addresses, timeouts etc). 107 | ## Class Diagram 108 | ![image](docs/class-diagram.png) 109 | -------------------------------------------------------------------------------- /src/example/uds-test-server/session-client.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "session-client.h" 5 | 6 | 7 | bool DSCClient::IsServiceSupported(sid_t sid, size_t& minlength, bool& subfunc) { 8 | 9 | (void) subfunc; 10 | 11 | if (sid == sidhelper::DSC) { 12 | minlength = 2u; 13 | return true; 14 | } else if (sid == sidhelper::SA) { 15 | minlength = 2u; 16 | return true; 17 | } 18 | 19 | return false; 20 | } 21 | 22 | 23 | ProcessResult DSCClient::OnAppIndication(const IndicationInfo& inf) { 24 | 25 | std::cout << "DSC handler : "; 26 | 27 | if (inf.head.SI == sidhelper::DSC) { 28 | std::cout << "SI ok, "; 29 | 30 | if (inf.head.SF > 0 && inf.head.SF <= 3) { 31 | if (sessionInfoContext.currSession == 1u && inf.head.SF == 2u) { 32 | // direct transition from default to programmaing session is not allowed 33 | udsRouter.SendNegResponse(NRCs::SFNSIAS); 34 | } else { 35 | std::cout << "Session ok (" << (int) inf.head.SF << ")" << std::endl; 36 | // session change request ok 37 | udsRouter.pubBuff[2] = ((250 >> 8) & 0xFF); 38 | udsRouter.pubBuff[3] = (250 & 0xFF); 39 | udsRouter.pubBuff[4] = (((5000 / 10) >> 8) & 0xFF); 40 | udsRouter.pubBuff[5] = ((5000 / 10) & 0xFF); 41 | sessionInfoContext.currSession = inf.head.SF; 42 | udsRouter.SetServiceSession(sessionInfoContext.currSession); 43 | udsRouter.SendResponse(udsRouter.pubBuff, 6); 44 | } 45 | } else { 46 | std::cout << "SF bad (" << (int) inf.head.SF << ")" << std::endl; 47 | udsRouter.SendNegResponse(NRCs::SFNS); 48 | } 49 | 50 | return ProcessResult::HANDLED_RESP_OK; 51 | } else if (inf.head.SI == sidhelper::SA) { 52 | return Handle_SA_request(inf); 53 | } else if (inf.head.SI == sidhelper::to_response(sidhelper::SA)) { 54 | return Handle_SA_response(inf); 55 | } 56 | 57 | return ProcessResult::NOT_HANDLED; 58 | } 59 | 60 | 61 | void DSCClient::OnSessionChange(bool isdefault) { 62 | 63 | if (isdefault) { 64 | sessionInfoContext.currSession = 1u; 65 | sessionInfoContext.secLevel = 0u; 66 | } 67 | } 68 | 69 | ProcessResult DSCClient::Handle_SA_request(const IndicationInfo& reqcontext) { 70 | 71 | uint16_t keytocheck = 0u; 72 | // request can only be odd number: 1, 3, 5 etc 73 | uint8_t seclev = reqcontext.data[1] >> 1u; 74 | bool isseedreq = ((reqcontext.data[1] & 0x01u) == 0x01u) ? true : false; 75 | 76 | // if request check if seed can be sent 77 | if (isseedreq) { 78 | const auto sesscontext = udsRouter.GetSession(); 79 | 80 | if (seclev + 1 == 1u && sesscontext.currSession == 1u) { 81 | udsRouter.SendNegResponse(NRCs::SFNSIAS); 82 | } else if (seclev + 1 == 2u && sesscontext.currSession != 2u) { 83 | udsRouter.SendNegResponse(NRCs::SFNSIAS); 84 | } else { 85 | if (udsRouter.GetSession().secLevel != seclev + 1u) { 86 | // security level is always unlocked, send 0u 87 | seedsent = rand() & 0xffffu; 88 | 89 | if (seedsent == 0u) { 90 | seedsent = 0xf0edu; 91 | } 92 | } 93 | 94 | // seed must be sent to the client 95 | HWREGH(udsRouter.pubBuff + 2) = ophelper::to_be_u16(seedsent); 96 | udsRouter.pubRespLength = 4u; 97 | } 98 | } else { 99 | // key must be checked 100 | switch (seclev) { 101 | case (1): 102 | keytocheck = seedsent + 4u; 103 | break; 104 | 105 | case (2): 106 | keytocheck = seedsent + 8u; 107 | break; 108 | 109 | default: 110 | keytocheck = 0xabab; 111 | break; 112 | } 113 | 114 | if (ophelper::from_be_u16(HWREGH(reqcontext.data + 2)) == keytocheck) { 115 | udsRouter.pubRespLength = 2u; 116 | seedsent = 0u; 117 | // unlock security level 118 | sessionInfoContext.secLevel = seclev; 119 | } else { 120 | udsRouter.SendNegResponse(NRCs::SAD); 121 | return ProcessResult::HANDLED_RESP_NO; 122 | } 123 | } 124 | 125 | return ProcessResult::HANDLED_RESP_OK; 126 | } 127 | 128 | ProcessResult DSCClient::Handle_SA_response(const IndicationInfo& reqcontext) { 129 | 130 | // if response the key must be calculated and send back 131 | uint16_t seedvalue = ophelper::from_be_u16(HWREGH(reqcontext.data + 2)); 132 | uint16_t keyvalue = 0u; 133 | bool isready = true; 134 | 135 | uint8_t seclev = (reqcontext.data[1] >> 1u) + 1; 136 | bool isseedreq = ((reqcontext.data[1] & 0x01u) == 0x01u) ? true : false; 137 | 138 | if (seedvalue == 0u) { 139 | // security level is already unlock, do nothing 140 | isready = false; 141 | } else if (isseedreq) { 142 | switch (seclev) { 143 | case (1): 144 | keyvalue = seedvalue + 4; 145 | break; 146 | 147 | case (2): 148 | keyvalue = seedvalue + 8; 149 | break; 150 | 151 | default: 152 | keyvalue = 0x0000; 153 | break; 154 | } 155 | } else { 156 | isready = false; 157 | } 158 | 159 | std::cout << "is ready : " << isready << std::endl; 160 | 161 | // key must be send to server as the second step 162 | if (isready) { 163 | udsRouter.pubBuff[0] = sidhelper::SA; 164 | udsRouter.pubBuff[1] = reqcontext.data[1] + 1; 165 | HWREGH(udsRouter.pubBuff + 2) = ophelper::to_be_u16(keyvalue); 166 | udsRouter.SendResponse(udsRouter.pubBuff, 4); 167 | } 168 | 169 | return ProcessResult::NOT_HANDLED; 170 | } 171 | -------------------------------------------------------------------------------- /src/etc/helpers/IKeeper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "static-allocator.h" 7 | 8 | /// @brief Type pointer redefinition 9 | /// @tparam T target type 10 | template 11 | using Tptr_t = T*; 12 | 13 | /// @brief Class container to keep array of pointers to T 14 | /// @tparam T Type of keeping elements 15 | template 16 | class IKeeper { 17 | 18 | public: 19 | /// @brief Private container pointer-type redifinition 20 | using ElemPtr_t = Tptr_t; 21 | 22 | /// @brief Result of any keeper opearion 23 | enum class KeeperResult { 24 | ERROR, 25 | OK, 26 | }; 27 | 28 | /// @brief Constructor 29 | /// @param mem Pointer to array in memory allocated for keeping elements 30 | /// @param capacity Number of elements in the element array 31 | IKeeper(ElemPtr_t mem[], size_t capacity) : elemArray(mem), ElemMaxNumber(capacity) {} 32 | 33 | /// @brief Adds new element to the next empty position in the array 34 | /// @param elem Pointer to the instance to be adde to the container 35 | /// @param multi Flag to permit adding more than one pointer on the same element 36 | /// @return Result of adding operation 37 | KeeperResult Add(ElemPtr_t elem, bool multi = true) { 38 | size_t addedPosition = ElemMaxNumber; 39 | 40 | for (size_t i = 0; (i < ElemMaxNumber) && (currElemNumber < ElemMaxNumber); i++) { 41 | if ((elemArray[i] == elem) && (multi == false)) { 42 | // element is already in collection, break the loop and return error 43 | break; 44 | } else if (elemArray[i] == nullptr) { 45 | // add new element 46 | elemArray[i] = elem; 47 | currElemNumber++; 48 | addedPosition = i; 49 | break; 50 | } 51 | } 52 | 53 | return (addedPosition == ElemMaxNumber) ? (KeeperResult::ERROR) : (KeeperResult::OK); 54 | } 55 | 56 | /// @brief Removes requested element from the container 57 | /// @param elemToRemove Pointer to the element to remove 58 | /// @return Result of the remove operation 59 | KeeperResult Remove(ElemPtr_t elemToRemove) { 60 | // Removing is not allowed yet 61 | return KeeperResult::ERROR; 62 | } 63 | 64 | /// @brief Resets internal iteration index 65 | void StartIteration() { 66 | iterId = 0; 67 | } 68 | 69 | /// @brief Checks iteration status 70 | /// @return true if the current iteration index is the last one, otherwise false 71 | bool IsLastIteration() { 72 | return (iterId >= currElemNumber); 73 | } 74 | 75 | /// @brief Shifts iteration poisition to the next element 76 | /// @return Pointer to the current iteration element 77 | ElemPtr_t IterNextElem() { 78 | ElemPtr_t ret = nullptr; 79 | 80 | if (iterId < currElemNumber) { 81 | ret = elemArray[iterId]; 82 | iterId++; 83 | } 84 | 85 | assert(ret != nullptr); 86 | 87 | return ret; 88 | } 89 | 90 | /// @brief Tries to read element address from the container 91 | /// @param id Element index 92 | /// @param refout Reference to the pointer on element type 93 | /// @return true if the @refout is set to some element, false when it is set to nullptr 94 | bool TryReadElem(size_t id, ElemPtr_t& refout) const { 95 | refout = ((id < ElemMaxNumber) ? elemArray[id] : nullptr); 96 | return (refout != nullptr) ? true : false; 97 | } 98 | 99 | /// @brief Gets current number of the elements in the container 100 | /// @return Number of elements 101 | size_t Count() const { 102 | return currElemNumber; 103 | } 104 | 105 | /// @brief Gets maximum capacity of the container 106 | /// @return Container capacity in number of elements 107 | size_t Capacity() const { 108 | return ElemMaxNumber; 109 | } 110 | 111 | private: 112 | ElemPtr_t* const elemArray{nullptr}; 113 | const size_t ElemMaxNumber; 114 | size_t currElemNumber{}; 115 | size_t iterId{}; 116 | }; 117 | 118 | /// @brief Template class which combines IKeeper and C functionality itself 119 | /// @details The AsKeeper is a proxy template class for the cases when 120 | /// the type shall provide some interface but needs to keep multiple 121 | /// instances of the type C and perform interfaces calls to these instances 122 | /// @tparam C Type which must be wrapped by AsKeeper 123 | template 124 | class AsKeeper : public IKeeper, public C { 125 | 126 | protected: 127 | /// @brief Constructor 128 | /// @param array Pointer to the array allocated for this container memory 129 | /// @param capacity Number of elements in the allocated array 130 | AsKeeper(C** array, size_t capacity) : IKeeper(array, capacity) {} 131 | }; 132 | 133 | /// @brief IKeeper extension with internal memory allocation 134 | /// @tparam T Container element type 135 | /// @tparam N Container array capacity 136 | template 137 | class MemKeeper : public IKeeper { 138 | 139 | public: 140 | /// @brief Constructor 141 | /// @tparam T Type of container element 142 | /// @tparam N Container capacity 143 | MemKeeper() : IKeeper(reinterpret_cast*>(statAllocator.ptr()), N) {} 144 | 145 | private: 146 | /// @brief Memory for the container 147 | StaticMemAllocator, N> statAllocator; 148 | }; 149 | 150 | /// @brief AsKeeper extension with internal memory allocation 151 | /// @tparam T Type of container element 152 | /// @tparam N Container capacity 153 | template 154 | class MemAsKeeper : public AsKeeper { 155 | 156 | public: 157 | /// @brief Constructor 158 | /// @tparam T Type of container element 159 | /// @tparam N Container capacity 160 | MemAsKeeper() : AsKeeper(reinterpret_cast*>(statAllocator.ptr()), N) {} 161 | 162 | private: 163 | /// @brief Memory for the container 164 | StaticMemAllocator, N> statAllocator; 165 | }; 166 | -------------------------------------------------------------------------------- /src/example/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include "can-bridge.h" 15 | #include "argcollector.h" 16 | #include "iso-tp-server/serv-factory.h" 17 | #include "app-helper.h" 18 | 19 | /* ---------------------------------------------------------------------------- */ 20 | constexpr size_t RxBufferSize = 8192; 21 | constexpr size_t TxBufferSize = 8192; 22 | std::string cmd; 23 | std::mutex mtx; 24 | 25 | // name of socketcan interface for ISO-TP communication test 26 | static std::string ifname = "vcan0"; 27 | 28 | /* ---------------------------------------------------------------------------- */ 29 | static DoCAN_TP& iso_tp = GetDoCAN(); 30 | static SocketCanReader listener(iso_tp); 31 | 32 | static void set_do_can_parameters(DoCAN_TP&, argsret& params) { 33 | 34 | uint32_t phys_id = 0x700u; 35 | uint32_t resp_id = 0x701u; 36 | uint32_t func_id = 0x7dfu; 37 | uint32_t stmin = 0u; 38 | uint32_t blksize = 8u; 39 | 40 | for (size_t i = 0; i < params.size(); i++) { 41 | if (params[i].first.compare("-blksize") == 0) { 42 | try_to_set_param(params[i], blksize); 43 | } else if (params[i].first.compare("-phys") == 0) { 44 | try_to_set_param(params[i], phys_id); 45 | } else if (params[i].first.compare("-resp") == 0) { 46 | try_to_set_param(params[i], resp_id); 47 | } else if (params[i].first.compare("-func") == 0) { 48 | try_to_set_param(params[i], func_id); 49 | } else if (params[i].first.compare("-stmin") == 0) { 50 | try_to_set_param(params[i], stmin); 51 | } else if (params[i].first.compare("-iface") == 0) { 52 | ifname = params[i].second; 53 | } 54 | } 55 | 56 | std::cout << "Init iso tp parameters:" << std::endl; 57 | 58 | std::cout << "BLKSIZE = " << (int)blksize << std::endl; 59 | iso_tp.SetParameter(ParName::BLKSZ, blksize); 60 | 61 | std::cout << "STMIN = " << (int)stmin << std::endl; 62 | iso_tp.SetParameter(ParName::ST_MIN, stmin); 63 | 64 | std::cout << std::hex; 65 | std::cout << "PHYS = " << (int)phys_id << std::endl; 66 | iso_tp.SetParameter(ParName::PHYS_ADDR, phys_id); 67 | 68 | std::cout << "RESP = " << (int)resp_id << std::endl; 69 | iso_tp.SetParameter(ParName::RESP_ADDR, resp_id); 70 | 71 | std::cout << "FUNC = " << (int)func_id << std::endl; 72 | std::cout << std::dec; 73 | 74 | iso_tp.SetParameter(ParName::FUNC_ADDR, func_id); 75 | } 76 | 77 | int main(int argc, char** argv) { 78 | 79 | auto params = collectargs(argc, argv); 80 | 81 | iso_tp.SetParameter(ParName::CANDL, 8); 82 | iso_tp.SetParameter(ParName::PADD_BYTE, 0xcc); 83 | 84 | std::cout << "ISO Tp starting. Binding to '" << ifname << "'" << std::endl; 85 | std::cout << " ----------------------------------------- " << std::endl; 86 | set_do_can_parameters(iso_tp, params); 87 | std::cout << " ----------------------------------------- " << std::endl << std::endl; 88 | auto sockfd = socket(PF_CAN, SOCK_RAW, CAN_RAW); 89 | assert(sockfd > 0); 90 | 91 | struct ifreq can_iface; 92 | strcpy(can_iface.ifr_name, ifname.c_str()); 93 | 94 | assert(ioctl(sockfd, SIOCGIFINDEX, &can_iface) >= 0); 95 | 96 | struct sockaddr_can loc_addr; 97 | bzero(&loc_addr, sizeof(loc_addr)); 98 | loc_addr.can_ifindex = can_iface.ifr_ifindex; 99 | loc_addr.can_family = AF_CAN; 100 | 101 | /* Check MTU of interface */ 102 | assert(ioctl(sockfd, SIOCGIFMTU, &can_iface) >= 0); 103 | assert(fcntl(sockfd, F_SETFL, (fcntl(sockfd, F_GETFL) | O_NONBLOCK)) >= 0); 104 | assert(bind(sockfd, (struct sockaddr*)&loc_addr, sizeof(loc_addr)) >= 0); 105 | 106 | std::cout << "Started succesfully." << std::endl; 107 | std::cout << " ----------------------------------------- " << std::endl << std::endl; 108 | 109 | listener.SetSocket(sockfd); 110 | GetCanSender().SetSocket(sockfd); 111 | 112 | std::array buffer; 113 | 114 | for (size_t i = 0; i < buffer.size(); buffer[i] = static_cast(i), i++); 115 | 116 | std::thread th1([&]() { 117 | std::string readcmd; 118 | std::cout << "Command read thread started... OK" << std::endl; 119 | std::cout << "To send ISO TP packet input number (payload size) and press Enter" << std::endl; 120 | std::cout << "If the target instance is available you will see the result in its output..." << std::endl; 121 | bool run = true; 122 | 123 | while (run) { 124 | std::cin >> readcmd; 125 | 126 | if (readcmd.size() > 0) { 127 | if (readcmd.at(0) == 'e') { 128 | // return from thread (exit) 129 | run = false; 130 | } 131 | 132 | std::lock_guard guard(mtx); 133 | cmd = readcmd; 134 | } 135 | } 136 | }); 137 | 138 | BuildApp(); 139 | 140 | std::string readcmd; 141 | 142 | while (true) { 143 | GetMainProcHandler().Process(); 144 | listener.Process(); 145 | 146 | { 147 | std::lock_guard guard(mtx); 148 | 149 | if (cmd.size() > 0) { 150 | readcmd = cmd; 151 | cmd.clear(); 152 | } 153 | } 154 | 155 | if (readcmd.size() > 0) { 156 | if (readcmd.at(0) == 'e') { 157 | // exit 158 | break; 159 | } else { 160 | uint32_t sendsize = 0u; 161 | 162 | if (sscanf(readcmd.c_str(), "%u", &sendsize) == 1) { 163 | std::cout << " ---> SEND " << sendsize << " bytes payload" << std::endl; 164 | iso_tp.Request(buffer.data(), sendsize); 165 | } 166 | } 167 | 168 | readcmd.clear(); 169 | } 170 | 171 | std::this_thread::sleep_for(std::chrono::milliseconds(1)); 172 | } 173 | 174 | th1.join(); 175 | std::cout << "Exit ... " << std::endl; 176 | } 177 | -------------------------------------------------------------------------------- /src/uds/session/apps/rctrl-router.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | 12 | // SID + SF + RoutineID + routineInfo 13 | static constexpr size_t RoutineHeadSize = 5; 14 | 15 | typedef uint16_t routine_id_t; 16 | 17 | typedef struct { 18 | uint16_t id; 19 | uint8_t type; 20 | uint8_t info; 21 | } RoutineContext_t; 22 | 23 | class RoutineRouter; 24 | 25 | /** 26 | * @brief Interface class for Routine Control clients 27 | * 28 | */ 29 | class RoutineHandler { 30 | 31 | public: 32 | 33 | /** 34 | * @brief Main client callback function. Routine manager will call client 35 | * via this method. Client must check @rid handle request if rid is its responsibility 36 | * 37 | * @param rid code of routine 38 | * @param rtype rtype one of 3 possible values (1 - start, 2 - stop, 3 - get result) 39 | * @param data pointer to data with params for routine execution 40 | * @param size size of data 41 | * @return ProcessResult 42 | */ 43 | virtual ProcessResult OnRoutine(routine_id_t rid, uint8_t rtype, const uint8_t* data, size_t size) = 0; 44 | 45 | protected: 46 | void LoadRoutineContext(routine_id_t rid, uint8_t reqtype) { 47 | rcontext.id = rid; 48 | rcontext.type = reqtype; 49 | rcontext.info = 0; 50 | } 51 | 52 | /** 53 | * @brief client routine info 54 | */ 55 | RoutineContext_t rcontext; 56 | }; 57 | 58 | 59 | /** 60 | * @brief Interface class for providing to clients API 61 | * to response on routine requests and SiClient for routine 62 | * control UDS service 63 | */ 64 | class RoutineRouter : public UdsAppClient { 65 | 66 | public: 67 | RoutineRouter(UdsAppManager& bserver, RoutineHandler& r) : UdsAppClient(bserver), rhandler(r) {} 68 | 69 | /** 70 | * @brief Main response function for clients 71 | * 72 | * @param rid code of routine 73 | * @param rtype one of 3 possible values (1 - start, 2 - stop, 3 - get result) 74 | * @param data pointer to response routine result related data 75 | * @param size length of data 76 | * @return SendResult 77 | */ 78 | SendResult SendRoutineResponse(routine_id_t rid, uint8_t rtype, uint8_t rinfo, 79 | const uint8_t* data, size_t size) { 80 | 81 | if (size + RoutineHeadSize >= udsRouter.PubBuffCapacity) { 82 | return SendResult::OVRF; 83 | } 84 | 85 | // Setup head of routine response 86 | udsRouter.pubBuff[0] = sidhelper::to_response(sidhelper::RC); 87 | udsRouter.pubBuff[1] = rtype; 88 | HWREGH(udsRouter.pubBuff + 2) = ophelper::to_be_u16(rid); 89 | udsRouter.pubBuff[4] = rinfo; 90 | 91 | // Copy response data 92 | memcpy(udsRouter.pubBuff + RoutineHeadSize, data, size); 93 | 94 | // Sending response 95 | udsRouter.pubRespLength = RoutineHeadSize + size; 96 | udsRouter.SendResponse(udsRouter.pubBuff, udsRouter.pubRespLength); 97 | 98 | return SendResult::OK; 99 | } 100 | 101 | SendResult SendRoutineResponse(routine_id_t rid, uint8_t rtype, uint8_t rinfo = 0) { 102 | 103 | return SendRoutineResponse(rid, rtype, rinfo, nullptr, 0); 104 | } 105 | 106 | SendResult SendRoutineResponse(RoutineContext_t& rcon, const uint8_t* data, size_t size) { 107 | 108 | return SendRoutineResponse(rcon.id, rcon.type, rcon.type, data, size); 109 | } 110 | 111 | SendResult SendRoutineResponse(RoutineContext_t& rcon) { 112 | 113 | return SendRoutineResponse(rcon.id, rcon.type, rcon.type, nullptr, 0); 114 | } 115 | 116 | /** 117 | * @brief Negative response function for clients 118 | * 119 | * @param n Negative response code value, cannot be PositiveResponse 120 | * @return SendResult 121 | */ 122 | SendResult SendRoutineNegResponse(NRCs n) { 123 | 124 | if (n != NRCs::PR) { 125 | udsRouter.SendNegResponse(sidhelper::RC, n); 126 | } 127 | 128 | return SendResult::OK; 129 | } 130 | 131 | virtual bool IsServiceSupported(sid_t sid, size_t& minlength, bool& subfunc) override { 132 | 133 | (void) subfunc; 134 | 135 | if (sid == sidhelper::RC) { 136 | minlength = 3u; 137 | return true; 138 | } 139 | 140 | return false; 141 | } 142 | 143 | ProcessResult OnAppIndication(const IndicationInfo& inf) { 144 | 145 | // Minimal service length (4) is tested on the initial service check 146 | if (inf.head.SI != sidhelper::RC) { 147 | return ProcessResult::NOT_HANDLED; 148 | } else if (inf.head.SF == 0 || inf.head.SF > 3) { 149 | udsRouter.SendNegResponse(NRCs::SFNS); 150 | return ProcessResult::HANDLED_RESP_NO; 151 | } 152 | 153 | ProcessResult pres = ProcessResult::NOT_HANDLED; 154 | routine_id_t routine_id = ophelper::from_be_u16(HWREGH(inf.data + 2)); 155 | 156 | pres = rhandler.OnRoutine(routine_id, inf.head.SF, inf.data + 4, inf.size - 4); 157 | 158 | if (pres == ProcessResult::NOT_HANDLED) { 159 | // send ROOR to client, no one routine handler has been found 160 | udsRouter.SendNegResponse(NRCs::ROOR); 161 | } else if (pres == ProcessResult::HANDLED_PENDING) { 162 | udsRouter.StartPending(60000u, 1000u); 163 | } 164 | 165 | return pres; 166 | } 167 | 168 | void OnAppConfirmation(S_Result res) { 169 | 170 | (void) res; 171 | } 172 | 173 | protected: 174 | 175 | RoutineHandler& rhandler; 176 | 177 | }; 178 | 179 | template 180 | class MultiRoutineHandler : public MemAsKeeper { 181 | 182 | public: 183 | MultiRoutineHandler() : MemAsKeeper() {} 184 | 185 | virtual ProcessResult OnRoutine(routine_id_t rid, uint8_t rtype, const uint8_t* data, size_t size) { 186 | auto ret = ProcessResult::NOT_HANDLED; 187 | RoutineHandler* handler {nullptr}; 188 | 189 | for (size_t i = 0; i < this->Count(); i++) { 190 | if (this->TryReadElem(i, handler)) { 191 | ret = handler->OnRoutine(rid, rtype, data, size); 192 | 193 | if (ret != ProcessResult::NOT_HANDLED) { 194 | break; 195 | } 196 | } 197 | } 198 | 199 | return ret; 200 | } 201 | }; 202 | -------------------------------------------------------------------------------- /src/uds/session/uds-app-manager.cpp: -------------------------------------------------------------------------------- 1 | #include "etc/helpers/ophelper.h" 2 | #include "uds-app-manager.h" 3 | #include "uds-app-client.h" 4 | 5 | using namespace sidhelper; 6 | 7 | UdsAppManager::UdsAppManager(uint8_t* membuff, datasize_t capacity, const SessionInfo& sessinstance) : 8 | pubBuff(membuff), 9 | PubBuffCapacity(capacity), 10 | sessionState(sessinstance) { 11 | 12 | assert(pubBuff != nullptr); 13 | assert(PubBuffCapacity != 0); 14 | 15 | SetActiveMode(true); 16 | } 17 | 18 | 19 | void UdsAppManager::SendResponse(const uint8_t* data, uint32_t length) { 20 | 21 | if (length == 0) { 22 | return; 23 | } 24 | 25 | // check address and NRC code 26 | if (ResponseAllowed()) { 27 | SendRequest(data, length, false); 28 | } 29 | 30 | nrcPending = false; 31 | // reset session layer anyway 32 | SetSessionMode(is_dsc_def_session(sessionState.currSession)); 33 | } 34 | 35 | 36 | void UdsAppManager::SendNegResponse(const sid_t sid, const NRCs nrc) { 37 | 38 | pubBuff[0] = NR_SI; 39 | pubBuff[1] = sid; 40 | pubBuff[2] = NRC_to_byte(nrc); 41 | nrcOutCode = nrc; 42 | 43 | SendResponse(pubBuff, 3); 44 | } 45 | 46 | 47 | void UdsAppManager::SendNegResponse(NRCs nrc) { 48 | 49 | SendNegResponse(reqContext.head.SI, nrc); 50 | } 51 | 52 | 53 | void UdsAppManager::SetServiceSession(uint8_t sessionValue) { 54 | 55 | // reset session layer anyway 56 | SetSessionMode(is_dsc_def_session(sessionValue)); 57 | 58 | // all the clients must be informed 59 | assert(clientHandler != nullptr); 60 | clientHandler->OnSessionChange(is_dsc_def_session(sessionValue)); 61 | } 62 | 63 | 64 | void UdsAppManager::OnSessIndication(const uint8_t* payload, uint32_t length, TargetAddressType addr) { 65 | 66 | nrcOutCode = NRCs::PR; 67 | 68 | assert(length != 0u); 69 | 70 | if (!isManagerActive || !is_sid_processable(payload[0])) { 71 | return; 72 | } 73 | 74 | reqContext.addr = addr; 75 | reqContext.data = payload; 76 | reqContext.size = length; 77 | 78 | reqContext.head.SI = reqContext.data[0]; 79 | reqContext.head.SF = get_subfunc(reqContext.data[1]); 80 | // by default request is handled as with SubFunction 81 | reqContext.head.suppressPosResponse = is_pos_response_suppress(reqContext.data[1]); 82 | // set most frequent case 83 | pubBuff[0] = to_response(reqContext.head.SI); 84 | pubBuff[1] = reqContext.data[1]; 85 | pubRespLength = 0; 86 | 87 | bool isrequest = !is_response(reqContext.data[0]); 88 | 89 | if (isrequest) { 90 | // Handle base service functions 91 | if (HandleReqInternally()) { 92 | SendResponse(pubBuff, pubRespLength); 93 | return; 94 | } 95 | } 96 | 97 | assert(clientHandler != nullptr); 98 | ProcessResult clientHandRes = clientHandler->OnAppIndication(reqContext); 99 | 100 | if (clientHandRes == ProcessResult::NOT_HANDLED && isrequest) { 101 | // there was no service to answer. so if the address is physycal 102 | // NRC NRCs::SNS must be sent (ISO 14229-1 table 4 (i)) 103 | if (reqContext.addr == TargetAddressType::PHYS) { 104 | SendNegResponse(NRCs::SNS); 105 | } 106 | } else if (clientHandRes == ProcessResult::HANDLED_RESP_OK) { 107 | SendResponse(pubBuff, pubRespLength); 108 | } else if (clientHandRes == ProcessResult::HANDLED_RESP_NO) { 109 | // reset session layer anyway 110 | SetSessionMode(is_dsc_def_session(sessionState.currSession)); 111 | } 112 | } 113 | 114 | 115 | void UdsAppManager::OnSessConfirmation(S_Result event) { 116 | 117 | assert(clientHandler != nullptr); 118 | clientHandler->OnAppConfirmation(event); 119 | } 120 | 121 | 122 | void UdsAppManager::On_s3_Timeout() { 123 | 124 | assert(clientHandler != nullptr); 125 | clientHandler->OnSessionChange(true); 126 | } 127 | 128 | 129 | bool UdsAppManager::ResponseAllowed() { 130 | 131 | if ((reqContext.addr == TargetAddressType::FUNC) 132 | && (nrcPending == false) 133 | && ((nrcOutCode == NRCs::SNSIAS) 134 | || (nrcOutCode == NRCs::SNS) 135 | || (nrcOutCode == NRCs::SFNS) 136 | || (nrcOutCode == NRCs::SFNSIAS) 137 | || (nrcOutCode == NRCs::ROOR))) { 138 | return false; 139 | } else if (nrcOutCode == NRCs::PR && reqContext.head.suppressPosResponse == true) { 140 | return false; 141 | } else if (nrcOutCode == NRCs::RCRRP) { 142 | reqContext.head.suppressPosResponse = false; 143 | } 144 | 145 | return true; 146 | } 147 | 148 | 149 | void UdsAppManager::StartPending(const size_t maxduration, const size_t resendperiod) { 150 | 151 | nrcPending = true; 152 | SetPending(maxduration, resendperiod, reqContext.head.SI); 153 | } 154 | 155 | 156 | void UdsAppManager::SetClient(UdsAppClient* client) { 157 | 158 | assert(clientHandler == nullptr); 159 | assert(client != nullptr); 160 | clientHandler = client; 161 | } 162 | 163 | 164 | void UdsAppManager::SetActiveMode(bool isactive) { 165 | 166 | isManagerActive = isactive; 167 | } 168 | 169 | 170 | bool UdsAppManager::HandleReqInternally() { 171 | 172 | if (reqContext.head.SI == TP) { 173 | SID_TesterPresent(); 174 | } else if (CheckIfSidSupported()) { 175 | return false; 176 | } 177 | 178 | return true; 179 | } 180 | 181 | 182 | void UdsAppManager::SID_TesterPresent() { 183 | 184 | if (reqContext.head.SF != 0) { 185 | // invalid SF 186 | SendNegResponse(NRCs::SFNS); 187 | } else if (reqContext.size != 2) { 188 | // TesterPresent : total length check 189 | SendNegResponse(NRCs::IMLOIF); 190 | } else { 191 | pubRespLength = 2; 192 | } 193 | } 194 | 195 | 196 | bool UdsAppManager::CheckIfSidSupported() { 197 | 198 | bool ret = false; 199 | bool subfunc = false; 200 | size_t minlength = 0u; 201 | 202 | assert(clientHandler != nullptr); 203 | ret = clientHandler->IsServiceSupported(reqContext.head.SI, minlength, subfunc); 204 | 205 | if (ret) { 206 | if (reqContext.size < minlength) { 207 | SendNegResponse(NRCs::IMLOIF); 208 | ret = false; 209 | } else { 210 | if (!subfunc) { 211 | // no subfunc means - no suppressPosResponse at all 212 | reqContext.head.suppressPosResponse = false; 213 | } 214 | } 215 | } else { 216 | SendNegResponse(NRCs::SNS); 217 | } 218 | 219 | return ret; 220 | } 221 | 222 | -------------------------------------------------------------------------------- /src/uds/isotp/docan-sender.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include "docan-sender.h" 5 | #include "pci-helper.h" 6 | #include "docan-tp.h" 7 | 8 | IsoTpResult DoCAN_Sender::Send(const uint8_t* data, datasize_t length) { 9 | 10 | IsoTpResult ret = CheckTxValid(length); 11 | const datasize_t candl = itp.Config().candl; 12 | 13 | if ((ret == IsoTpResult::OK) && (candl > 0)) { 14 | assert(data != nullptr); 15 | PciHelper helper; 16 | DC_FrameType pci{DC_FrameType::ERROR}; 17 | 18 | auto pci_len = helper.PackPciForData(can_message, length, candl, pci); 19 | assert(pci_len < candl); 20 | 21 | if (pci != DC_FrameType::ERROR) { 22 | txds.size = length; 23 | txds.passed = ((pci_len + length) > candl) ? (candl - pci_len) : length; 24 | 25 | memcpy(can_message + pci_len, data, txds.passed); 26 | 27 | if (IsBusy()) { 28 | // Active transmittion is in progress. Stop it and process current request 29 | txds.state = DtState::IDLE; 30 | } 31 | 32 | if (itp.PduToCan(can_message, pci_len + txds.passed)) { 33 | if (pci == DC_FrameType::FF) { 34 | // FF transmition begins, wait for FC 35 | N_Bs_tim.Restart(); 36 | txds.state = DtState::WAIT; 37 | txds.sn = 0u; 38 | memcpy(txbuff, data, length); 39 | } else if (pci == DC_FrameType::SF) { 40 | // TODO: this is simplification. when it is necessary 41 | // to get true HW CAN node confirmation of sending 42 | // this call should be removed and CAN HW sending provider 43 | // must call it by itself after successful frame acceptance by BUS 44 | // that means that CAN HW sender has to have reference to DoCAN_TP 45 | itp.OnIsoTxEvent(N_Event::Conf, N_Result::OK_s); 46 | } 47 | } 48 | } 49 | } 50 | 51 | return ret; 52 | } 53 | 54 | IsoTpResult DoCAN_Sender::CheckTxValid(datasize_t length) { 55 | 56 | if (length == 0) { 57 | return IsoTpResult::WRONG_STATE; 58 | } else if (length > TXLEN) { 59 | return IsoTpResult::OVERFLOW; 60 | } else { 61 | return IsoTpResult::OK; 62 | } 63 | } 64 | 65 | void DoCAN_Sender::ProcessTx() { 66 | 67 | PciHelper pchelper; 68 | 69 | switch (txds.state) { 70 | case (DtState::WAIT): 71 | if (N_Bs_tim.Elapsed()) { 72 | // check BS timer while wait next FC, timeout leads to stop transmittion 73 | txds.state = DtState::IDLE; 74 | itp.OnIsoTxEvent(N_Event::Conf, N_Result::TIMEOUT_Bs); 75 | } 76 | 77 | break; 78 | 79 | case (DtState::MF_DT): 80 | while (true) { 81 | const datasize_t pci_len = pchelper.PackConseqFrame(can_message, ++txds.sn); 82 | // get the data len 83 | const datasize_t candlen = itp.Config().candl - pci_len; 84 | // get the length of data for putting in frame payload 85 | const datasize_t cpylen = ((txds.passed + candlen) < txds.size) ? candlen : (txds.size - txds.passed); 86 | 87 | if (txds.passed == 0 || prev_data_sent) { 88 | // if previous sending finished or the frist chunk -> copy to CAN message 89 | memcpy(can_message + pci_len, txbuff + txds.passed, cpylen); 90 | } 91 | 92 | // Start As timer just before attempt to send CAN frame 93 | if (!N_As_tim.IsActive()) { 94 | N_As_tim.Restart(); 95 | } 96 | 97 | // if (sender.SendFrame(can_message, candl, itp.Config().resp_id) != 0) 98 | if (itp.PduToCan(can_message, cpylen + pci_len)) { 99 | // CAN sending result it OK, stop As timer 100 | N_As_tim.Stop(); 101 | prev_data_sent = true; 102 | 103 | txds.passed += cpylen; 104 | 105 | if (txds.passed >= txds.size) { 106 | // done, transfer stopped 107 | txds.state = DtState::IDLE; 108 | 109 | // TODO: this is simplification. when it is necessary 110 | // to get true HW CAN node confirmation of sending 111 | // this call should be removed and CAN HW sending provider 112 | // must call it by itself after successful frame acceptance by BUS 113 | // that means that CAN HW sender has to have reference to DoCAN_TP 114 | itp.OnIsoTxEvent(N_Event::Conf, N_Result::OK_s); 115 | break; 116 | } else { 117 | if (++txds.currblknum >= txds.segblkcount) { 118 | txds.state = DtState::WAIT; 119 | break; 120 | } 121 | } 122 | 123 | if (txds.stmin != 0) { 124 | // STmin time have to be waited 125 | txds.state = DtState::PAUSE; 126 | N_As_tim.Stop(); 127 | break; 128 | } 129 | } else { 130 | // senging attempt failed, quit and try next time 131 | prev_data_sent = false; 132 | break; 133 | } 134 | } 135 | 136 | if (N_As_tim.Elapsed()) { 137 | txds.state = DtState::IDLE; 138 | itp.OnIsoTxEvent(N_Event::Conf, N_Result::TIMEOUT_As); 139 | } 140 | 141 | break; 142 | 143 | case (DtState::PAUSE): 144 | if (STmin_tim.Elapsed()) { 145 | txds.state = DtState::MF_DT; 146 | } 147 | 148 | break; 149 | 150 | default: 151 | break; 152 | } 153 | } 154 | 155 | void DoCAN_Sender::OnFlowControl(uint8_t flow_status, uint8_t blks, uint8_t stm) { 156 | 157 | if (txds.state != DtState::WAIT) { 158 | // Unexpected PDU has came 159 | txds.state = DtState::IDLE; 160 | itp.OnIsoTxEvent(N_Event::Conf, N_Result::UNEXP_PDU); 161 | return; 162 | } 163 | 164 | switch (from_byte(flow_status)) { 165 | // CTS - client ready to receive BS messages 166 | case (DC_FlowState::CTS): 167 | N_Bs_tim.Stop(); 168 | txds.currblknum = 0; 169 | txds.segblkcount = blks; 170 | 171 | if (stm < 0x80) { 172 | txds.stmin = stm; 173 | } else if (stm > 0xf0u && stm < 0xfau) { 174 | txds.stmin = 1; 175 | } else { 176 | // if STmin is in reserved value - apply maximum valid value 177 | txds.stmin = 0x7fu; 178 | } 179 | 180 | if (txds.stmin != 0) { 181 | txds.state = DtState::PAUSE; 182 | } else { 183 | txds.state = DtState::MF_DT; 184 | } 185 | 186 | STmin_tim.Start(txds.stmin, true); 187 | break; 188 | 189 | case (DC_FlowState::WAIT): 190 | N_Bs_tim.Restart(); 191 | break; 192 | 193 | case (DC_FlowState::OVERFLOW): 194 | txds.state = DtState::IDLE; 195 | itp.OnIsoTxEvent(N_Event::Conf, N_Result::BUFFER_OVFLW); 196 | break; 197 | 198 | default: 199 | txds.state = DtState::IDLE; 200 | itp.OnIsoTxEvent(N_Event::Conf, N_Result::INVALID_FS); 201 | break; 202 | } 203 | } 204 | 205 | SetParamResult DoCAN_Sender::SetParameter(ParName name, uint32_t v) { 206 | 207 | auto ret = SetParamResult::OK; 208 | 209 | switch (name) { 210 | case (ParName::As_TIM_ms): 211 | N_As_tim.Start(v); 212 | N_As_tim.Stop(); 213 | break; 214 | 215 | case (ParName::Bs_TIM_ms): 216 | N_Bs_tim.Start(v); 217 | N_Bs_tim.Stop(); 218 | break; 219 | 220 | default: 221 | ret = SetParamResult::WRONG_PARAMETER; 222 | break; 223 | } 224 | 225 | return ret; 226 | } 227 | -------------------------------------------------------------------------------- /src/gtests/app-uds-response-allow-test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static uint32_t responseCounter = 0u; 10 | 11 | constexpr sid_t SID_1 = 0x22u; 12 | constexpr sid_t SID1_DATA_PR = 0x33u; 13 | constexpr sid_t SID1_DATA_VTH = 0x34u; 14 | constexpr sid_t SID1_DATA_ROOR = 0xFFu; 15 | 16 | constexpr sid_t SID_2_SNSIAS = 0x32u; 17 | constexpr sid_t SID_SNS = 0x23u; 18 | 19 | constexpr sid_t SID_SF = 0x20u; 20 | 21 | constexpr sid_t SIDSF_SF_OK = 0x07u; 22 | constexpr sid_t SIDSF_SF_SNSIAS = 0x08u; 23 | 24 | constexpr uint8_t SIDSF_SF_PARAM_OK = 0x33u; 25 | constexpr uint8_t SIDSF_SF_PARAM_CNC = 0x34u; 26 | constexpr uint8_t SIDSF_SF_PARAM_PENDING = 0x35u; 27 | 28 | // The frist class must be mock for IsoTpImpl 29 | class MockIsoTp : public IsoTpImpl { 30 | 31 | public: 32 | virtual IsoTpResult Request(const uint8_t* data, size_t length) override { 33 | 34 | ++responseCounter; 35 | return IsoTpResult::OK; 36 | } 37 | }; 38 | 39 | class MockUdsAppClient : public UdsAppClient { 40 | 41 | public: 42 | MockUdsAppClient(UdsAppManager& appManager) : UdsAppClient(appManager) {} 43 | 44 | virtual bool IsServiceSupported(const sid_t sid, size_t& minlenght, bool& subfunc) { 45 | 46 | sid_t sidbyte = sid; 47 | 48 | if (sidbyte == SID_1 || sidbyte == SID_2_SNSIAS) { 49 | minlenght = 3u; 50 | subfunc = false; 51 | } else if (sidbyte == SID_SF) { 52 | minlenght = 3u; 53 | subfunc = true; 54 | } else { 55 | return false; 56 | } 57 | 58 | return true; 59 | } 60 | 61 | virtual ProcessResult OnAppIndication(const IndicationInfo& inf) { 62 | 63 | sid_t sidbyte = inf.head.SI; 64 | size_t retLength = 0u; 65 | 66 | if (sidbyte == SID_1) { 67 | if (inf.data[2] == SID1_DATA_PR) { 68 | retLength = 2u; 69 | } else if (inf.data[2] == SID1_DATA_VTH) { 70 | udsRouter.SendNegResponse(NRCs::VTH); 71 | } else { 72 | udsRouter.SendNegResponse(NRCs::ROOR); 73 | } 74 | } else if (sidbyte == SID_2_SNSIAS) { 75 | udsRouter.SendNegResponse(NRCs::SNSIAS); 76 | 77 | } 78 | 79 | else if (sidbyte == SID_SF) { 80 | if (inf.head.SF == SIDSF_SF_OK) { 81 | if (inf.data[2] == SIDSF_SF_PARAM_OK) { 82 | retLength = 2u; 83 | } else if (inf.data[2] == SIDSF_SF_PARAM_CNC) { 84 | udsRouter.SendNegResponse(NRCs::CNC); 85 | } else if (inf.data[2] == SIDSF_SF_PARAM_PENDING) { 86 | udsRouter.StartPending(10000); 87 | // 78 sent one time 88 | udsRouter.SendNegResponse(NRCs::ROOR); 89 | } else { 90 | udsRouter.SendNegResponse(NRCs::ROOR); 91 | } 92 | } else if (inf.head.SF == SIDSF_SF_SNSIAS) { 93 | udsRouter.SendNegResponse(NRCs::SFNSIAS); 94 | } else { 95 | udsRouter.SendNegResponse(NRCs::SFNS); 96 | } 97 | } else { 98 | return ProcessResult::NOT_HANDLED; 99 | } 100 | 101 | if (retLength == 0u) { 102 | return ProcessResult::HANDLED_RESP_NO; 103 | } else { 104 | udsRouter.pubRespLength = retLength; 105 | return ProcessResult::HANDLED_RESP_OK; 106 | } 107 | } 108 | 109 | virtual void OnAppConfirmation(S_Result) {} 110 | }; 111 | 112 | static MockIsoTp testIsoTp; 113 | 114 | static uint8_t appBuff[32u] = { 0 }; 115 | static SessionInfo testSessionInfo; 116 | 117 | static UdsAppManager testAppManger(appBuff, 32u, testSessionInfo); 118 | static MockUdsAppClient testAppClient(testAppManger); 119 | 120 | 121 | static uint8_t rxArray[8] = { 0 }; 122 | 123 | static IsoTpClient::IsoTpInfo isoContext = { 124 | rxArray, 125 | 0, 126 | N_TarAddress::TAtype_1_Physical 127 | }; 128 | 129 | 130 | void set_payload(uint8_t sid, std::array data, size_t len) { 131 | 132 | assert(len <= 7); 133 | memset(rxArray, 0, 8); 134 | rxArray[0] = sid; 135 | memcpy(rxArray + 1, data.data(), len); 136 | } 137 | 138 | void set_phys_payload(uint8_t sid, std::array data, size_t len) { 139 | 140 | isoContext.address = N_TarAddress::TAtype_1_Physical; 141 | set_payload(sid, data, len); 142 | isoContext.length = 1 + len; 143 | } 144 | 145 | void set_func_payload(uint8_t sid, std::array data, size_t len) { 146 | 147 | set_phys_payload(sid, data, len); 148 | isoContext.address = N_TarAddress::TAtype_2_Functional; 149 | } 150 | 151 | void set_phys_payload_supress(uint8_t sid, std::array data, size_t len) { 152 | 153 | set_phys_payload(sid, data, len); 154 | rxArray[1] |= 0x80u; 155 | } 156 | 157 | void set_func_sub_func_suppres(uint8_t sid, std::array data, size_t len) { 158 | 159 | set_func_payload(sid, data, len); 160 | rxArray[1] |= 0x80u; 161 | } 162 | 163 | static uint32_t response_expected = 0u; 164 | 165 | uint32_t no_response_sent() { 166 | 167 | return response_expected; 168 | } 169 | 170 | uint32_t response_sent() { 171 | 172 | ++response_expected; 173 | return no_response_sent(); 174 | } 175 | 176 | // Demonstrate some basic assertions. 177 | TEST(ResponseUdsAppTest, GeneralTests) { 178 | 179 | EXPECT_EQ(1, 1); 180 | testAppManger.SetClient(&testAppClient); 181 | testAppManger.SetIsoTp(&testIsoTp); 182 | 183 | 184 | // Table 6 shows 6 — Physically possible communication addressed request schemes message with physical without addressing. 185 | 186 | // a) - Server sends a positive response message because the service identifier and all data-parameters 187 | // are supported of the client's request message. 188 | 189 | // b) - Server sends a positive response message because the service identifier and at least one data- 190 | // parameter is supported of the client's request message. 191 | 192 | // expected positive response 193 | set_phys_payload(SID_1, {0x00, SID1_DATA_PR}, 2); 194 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 195 | EXPECT_EQ(responseCounter, response_sent()); 196 | 197 | // c) - Server sends a negative response message (e.g. IMLOIF: 198 | // incorrectMessageLengthOrIncorrectFormat) because the service identifier is supported and at least 199 | // one data-parameter is supported of the client's request message, but some other error occurred 200 | // (e.g. wrong length of the request message) during processing of the service. 201 | 202 | // expected VTH 203 | set_phys_payload(SID_1, {0x00, SID1_DATA_VTH}, 2); 204 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 205 | EXPECT_EQ(responseCounter, response_sent()); 206 | 207 | // d) - Server sends a negative response message with the negative response code ROOR 208 | // (requestOutOfRange) because the service identifier is supported and none of the requested data- 209 | // parameters are supported of the client's request message. 210 | 211 | // expected ROOR 212 | set_phys_payload(SID_1, {0x00, SID1_DATA_ROOR}, 2); 213 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 214 | EXPECT_EQ(responseCounter, response_sent()); 215 | 216 | 217 | // e) - Server sends a negative response message with the negative response code SNS 218 | // (serviceNotSupported) or SNSIAS (serviceNotSupportedInActiveSession) because the service 219 | // identifier is not supported of the client's request message. 220 | 221 | // e.1 - expected SNS 222 | set_phys_payload(SID_SNS, {0x00, SID1_DATA_ROOR}, 2); 223 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 224 | EXPECT_EQ(responseCounter, response_sent()); 225 | 226 | // e.2 - expected SNSIAS 227 | set_phys_payload(SID_2_SNSIAS, {0x00, SID1_DATA_ROOR}, 2); 228 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 229 | EXPECT_EQ(responseCounter, response_sent()); 230 | 231 | // 232 | // Table 7 — Functionally addressed request message without SubFunction parameter and server response behaviour 233 | // 234 | 235 | // a) - Server sends a positive response message because the service identifier and all data-parameters 236 | // are supported of the client's request message. 237 | // b) - Server sends a positive response message because the service identifier and at least one data- 238 | // parameter is supported of the client's request message. 239 | 240 | // expected positive response 241 | set_func_payload(SID_1, {0x00, SID1_DATA_PR}, 2); 242 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 243 | EXPECT_EQ(responseCounter, response_sent()); 244 | 245 | // c) - Server sends a negative response message (e.g. IMLOIF: incorrectMessageLengthOrIncorrectFormat) 246 | // because the service identifier is supported and at least one data-parameter is supported 247 | // of the client's request message, but some other error occurred (e.g. wrong length of the 248 | // request message) during processing of the service. 249 | 250 | // exptected VTH 251 | set_func_payload(SID_1, {0x00, SID1_DATA_VTH}, 2); 252 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 253 | EXPECT_EQ(responseCounter, response_sent()); 254 | 255 | // d) - Server sends no response message because the negative response code ROOR (requestOutOfRange; 256 | // which would occur because the service identifier is supported, but none of the requested data- 257 | // parameters is supported of the client's request) is always suppressed in case of a functionally 258 | // addressed request. 259 | 260 | // expected ROOR 261 | set_func_payload(SID_1, {0x00, SID1_DATA_ROOR}, 2); 262 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 263 | EXPECT_EQ(responseCounter, no_response_sent()); 264 | 265 | 266 | // e) - Server sends no response message because the negative response codes SNS 267 | // (serviceNotSupported) and SNSIAS (serviceNotSupportedInActiveSession), which are identified by 268 | // the server because the service identifier is not supported of the client's request, are always 269 | // suppressed in case of a functionally addressed request. 270 | 271 | // expected SNS 272 | set_func_payload(SID_SNS, {0x00, SID1_DATA_ROOR}, 2); 273 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 274 | EXPECT_EQ(responseCounter, no_response_sent()); 275 | 276 | // expected SNSIAS 277 | set_func_payload(SID_2_SNSIAS, {0x00, SID1_DATA_ROOR}, 2); 278 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 279 | EXPECT_EQ(responseCounter, no_response_sent()); 280 | 281 | // 282 | // Table 4 Physically communication addressed request schemes message with SubFunction addressing response behaviour 283 | // 284 | 285 | // a) 286 | set_phys_payload(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_OK }, 2); 287 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 288 | EXPECT_EQ(responseCounter, response_sent()); 289 | 290 | // b) 291 | set_phys_payload(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_CNC }, 2); 292 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 293 | EXPECT_EQ(responseCounter, response_sent()); 294 | 295 | // c) 296 | set_phys_payload(SID_SF, { SIDSF_SF_OK, 0xffu }, 2); 297 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 298 | EXPECT_EQ(responseCounter, response_sent()); 299 | 300 | // d) 301 | set_phys_payload(SID_SNS, { 0, 0 }, 2); 302 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 303 | EXPECT_EQ(responseCounter, response_sent()); 304 | 305 | // e) 306 | set_phys_payload(SID_SF, { 0, SIDSF_SF_PARAM_OK }, 2); 307 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 308 | EXPECT_EQ(responseCounter, response_sent()); 309 | 310 | // f) 311 | set_phys_payload_supress(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_OK }, 2); 312 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 313 | EXPECT_EQ(responseCounter, no_response_sent()); 314 | 315 | 316 | // g) 317 | set_phys_payload_supress(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_CNC }, 2); 318 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 319 | EXPECT_EQ(responseCounter, response_sent()); 320 | 321 | 322 | // h) 323 | set_phys_payload_supress(SID_SF, { SIDSF_SF_OK, 0xffu }, 2); 324 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 325 | EXPECT_EQ(responseCounter, response_sent()); 326 | 327 | 328 | // i) 329 | set_phys_payload_supress(SID_SNS, { 0, 0 }, 2); 330 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 331 | EXPECT_EQ(responseCounter, response_sent()); 332 | 333 | 334 | // j) 335 | set_phys_payload_supress(SID_SF, { 0, SIDSF_SF_PARAM_OK }, 2); 336 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 337 | EXPECT_EQ(responseCounter, response_sent()); 338 | 339 | // 340 | // Table 5 — Functionally addressed request message with SubFunction parameter and server 341 | // response behaviour 342 | // 343 | 344 | // a) 345 | set_func_payload(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_OK }, 2); 346 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 347 | EXPECT_EQ(responseCounter, response_sent()); 348 | 349 | // b) 350 | set_func_payload(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_CNC }, 2); 351 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 352 | EXPECT_EQ(responseCounter, response_sent()); 353 | 354 | // c) 355 | set_func_payload(SID_SF, { SIDSF_SF_OK, 0xffu }, 2); 356 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 357 | EXPECT_EQ(responseCounter, no_response_sent()); 358 | 359 | // d) 360 | set_func_payload(SID_SNS, { 0, 0 }, 2); 361 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 362 | EXPECT_EQ(responseCounter, no_response_sent()); 363 | 364 | // e) 365 | set_func_payload(SID_SF, { 0, SIDSF_SF_PARAM_OK }, 2); 366 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 367 | EXPECT_EQ(responseCounter, no_response_sent()); 368 | // f) 369 | set_func_sub_func_suppres(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_OK }, 2); 370 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 371 | EXPECT_EQ(responseCounter, no_response_sent()); 372 | 373 | // g) 374 | set_func_sub_func_suppres(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_CNC }, 2); 375 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 376 | EXPECT_EQ(responseCounter, response_sent()); 377 | 378 | // h) 379 | set_func_sub_func_suppres(SID_SF, { SIDSF_SF_OK, 0xffu }, 2); 380 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 381 | EXPECT_EQ(responseCounter, no_response_sent()); 382 | 383 | // i) 384 | set_func_sub_func_suppres(SID_SNS, { 0, 0 }, 2); 385 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 386 | EXPECT_EQ(responseCounter, no_response_sent()); 387 | 388 | // j) 389 | set_func_sub_func_suppres(SID_SF, { 0, SIDSF_SF_PARAM_OK }, 2); 390 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 391 | EXPECT_EQ(responseCounter, no_response_sent()); 392 | 393 | // RCRRP 394 | set_func_sub_func_suppres(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_PENDING }, 2); 395 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 396 | response_sent(); 397 | EXPECT_EQ(responseCounter, response_sent()); 398 | 399 | set_func_sub_func_suppres(SID_SF, { SIDSF_SF_OK, SIDSF_SF_PARAM_OK }, 2); 400 | testAppManger.OnIsoEvent(N_Event::Data, N_Result::OK_r, isoContext); 401 | EXPECT_EQ(responseCounter, no_response_sent()); 402 | 403 | } 404 | --------------------------------------------------------------------------------