diff --git a/.bazelrc b/.bazelrc new file mode 100644 index 0000000..a32f912 --- /dev/null +++ b/.bazelrc @@ -0,0 +1,25 @@ +build --strip=never +build --incompatible_enable_cc_toolchain_resolution + +build --extra_toolchains //toolchain:arm_linux_gcc +build --extra_toolchains //toolchain:arm_none_gcc +build --extra_toolchains //toolchain:ppc_gcc +build --extra_toolchains //toolchain:ppc64_gcc +build --extra_toolchains //toolchain:ppc64le_gcc +build --extra_toolchains //toolchain:x86_64_clang + +build:arm_linux --platforms=//platforms:arm_linux +build:s32k --platforms=//platforms:s32k_evb +build:ppc --platforms=//platforms:ppc +build:ppc64 --platforms=//platforms:ppc64 +build:ppc64le --platforms=//platforms:ppc64le +build:x86_64_clang --platforms=//platforms:x86_64_clang + +test:arm_linux --run_under="qemu-arm -L /usr/arm-linux-gnueabihf/ " +test:ppc --run_under="qemu-ppc -L /usr/powerpc-linux-gnu/ " +test:ppc64 --run_under="qemu-ppc64 -L /usr/powerpc64-linux-gnu/ " +test:ppc64le --run_under="qemu-ppc64le -L /usr/powerpc64le-linux-gnu/ " + + +# parallel tests break on a single vcan interface +test: --local_test_jobs=1 --test_output=all \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..d7e8e12 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,25 @@ +name: unit tests + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: lint + run: CHECK_FORMAT=1 ./run_clang_format.sh + continue-on-error: true + + - name: run unit tests + run: bazel test //test:all --test_tag_filters=-vcan + + diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..5c979d1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,25 @@ +name: release + +on: + push: + tags: [ "v*" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: checkout + uses: actions/checkout@v3 + + - name: build release + run: bazel build //:release + + - name: Release + env: + GITHUB_TOKEN: ${{ secrets.TOKEN }} + run: >- + gh release create ${{ github.ref_name }} + "bazel-bin/iso14229.zip#iso14229.zip" + --generate-notes + --title "${{ github.ref_name }}" diff --git a/.github/workflows/runtests.yml b/.github/workflows/runtests.yml deleted file mode 100644 index c2c8d49..0000000 --- a/.github/workflows/runtests.yml +++ /dev/null @@ -1,18 +0,0 @@ -name: unit tests - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -jobs: - build: - - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: build and run unit test - run: make unit_test diff --git a/.gitignore b/.gitignore index 1eea496..ceb3ac1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ bazel-* +bazel-out/k8-fastbuild/bin .rsync-filter test_iso14229 client @@ -7,4 +8,14 @@ corpus *.profdata *.profraw .gdb_history -fuzzer \ No newline at end of file +fuzzer +*.o +.vscode +**/*.cache +**/build +**/sdkconfig* +release/ +examples/**/iso14229.c +examples/**/iso14229.h +*.pem +*.der diff --git a/.tests.gdbinit b/.tests.gdbinit deleted file mode 100644 index 4fd0acc..0000000 --- a/.tests.gdbinit +++ /dev/null @@ -1,3 +0,0 @@ -# For handy x86 breakpoint setting, drop an `__asm__("int3");` in the source -exec-file python3 -run ./test_iso14229 -s diff --git a/AUTHORS.txt b/AUTHORS.txt new file mode 100644 index 0000000..54e074a --- /dev/null +++ b/AUTHORS.txt @@ -0,0 +1,2 @@ +Nick James Kirkby +Mark Corbin diff --git a/BUILD b/BUILD index 0d4ddb1..8dc4dd7 100644 --- a/BUILD +++ b/BUILD @@ -1,3 +1,4 @@ +load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") package(default_visibility = ["//visibility:public"]) filegroup( @@ -5,51 +6,52 @@ filegroup( srcs = [ "iso14229.c", "iso14229.h", - "iso14229serverbufferedwriter.h", ], ) -cc_test( - name="test", - srcs=[ - ":srcs", - "test_iso14229.c", - ], - copts=[ - "-Wall", - "-Wextra", - "-Wno-missing-field-initializers", - "-Werror", - "-Wno-unused-parameter", - ], - defines=[ - "UDS_TP=UDS_TP_CUSTOM", - "UDS_CUSTOM_MILLIS", +cc_library( + name="iso14229", + srcs = [ + "iso14229.c", + "iso14229.h", ], ) -cc_test( - name = "test_server_bufferedwriter", - srcs = [ - "udsserverbufferedwriter.h", - "test_udsserverbufferedwriter.c", - ], +cc_library( + name="iso14229_2", + srcs=glob(["src/**/*.c", "src/**/*.h"]), + copts=['-Isrc'], ) +refresh_compile_commands( + name = "s32k_refresh_compile_commands", + targets = { + "//examples/s32k144/...": "--config=s32k", + } +) -filegroup( - name="isotp_c_srcs", - srcs=[ - "isotp-c/isotp.c", - "isotp-c/isotp.h", - "isotp-c/isotp_config.h", - "isotp-c/isotp_defines.h", - "isotp-c/isotp_user.h", - ], +py_binary( + name="amalgamate", + srcs=["amalgamate.py"], ) -cc_library( - name="isotp_c", - srcs=[":isotp_c_srcs"], - copts=["-Wno-unused-parameter"], +genrule( + name="amalgamated", + srcs=glob(["src/**/*.c", "src/**/*.h"]), + outs=["iso14229.c", "iso14229.h"], + cmd="$(location //:amalgamate) --out_c $(location //:iso14229.c) --out_h $(location //:iso14229.h) $(SRCS)", + tools=["//:amalgamate"], ) + +genrule( + name="release", + srcs=[ + "iso14229.c", + "iso14229.h", + "README.md", + "LICENSE", + "AUTHORS.txt", + ], + outs = ["iso14229.zip"], + cmd = "mkdir iso14229 && cp -L $(SRCS) iso14229/ && zip -r $(OUTS) iso14229", +) \ No newline at end of file diff --git a/Makefile b/Makefile index 1bcd74f..6786f80 100644 --- a/Makefile +++ b/Makefile @@ -1,45 +1,20 @@ -ifeq "$(TP)" "ISOTP_C" -SRCS += isotp-c/isotp.c examples/isotp-c_on_socketcan.c -CFLAGS += -DUDS_TP=UDS_TP_ISOTP_C -endif +all: iso14229.c iso14229.h + mkdir -p release + cp iso14229.c iso14229.h release/ -cxx: CFLAGS+=-DUDS_TP=UDS_TP_CUSTOM -cxx: Makefile iso14229.c iso14229.h - $(CXX) iso14229.c $(CFLAGS) -c - -unit_test: CFLAGS+=-DUDS_TP=UDS_TP_CUSTOM -DUDS_CUSTOM_MILLIS -unit_test: Makefile iso14229.h iso14229.c test_iso14229.c - $(CC) iso14229.c test_iso14229.c $(CFLAGS) $(LDFLAGS) -o test_iso14229 - $(RUN) ./test_iso14229 - -client: CFLAGS+=-g -DUDS_DBG_PRINT=printf -client: examples/client.c examples/uds_params.h iso14229.h iso14229.c Makefile $(SRCS) - $(CC) iso14229.c $(SRCS) $< $(CFLAGS) -o $@ - -server: CFLAGS+=-g -DUDS_DBG_PRINT=printf -server: examples/server.c examples/uds_params.h iso14229.h iso14229.c Makefile $(SRCS) - $(CC) iso14229.c $(SRCS) $< $(CFLAGS) -o $@ - -test_examples: test_examples.py - $(RUN) ./test_examples.py - -uds_prefix: CFLAGS+=-DUDS_TP=UDS_TP_CUSTOM -DUDS_CUSTOM_MILLIS -uds_prefix: iso14229.c iso14229.h - $(CC) iso14229.c $(CFLAGS) -c -o /tmp/x.o && nm /tmp/x.o | grep ' T ' | grep -v 'UDS' ; test $$? = 1 +clean: + rm -rf isotp_c_wrapped.c isotp_c_wrapped.h iso14229.c iso14229.h release/ -test_qemu: Makefile iso14229.h iso14229.c test_iso14229.c test_qemu.py - $(RUN) ./test_qemu.py +isotp_c_wrapped.c: $(shell find src/tp/isotp-c -name '*.c') + echo '#if defined(UDS_ISOTP_C)' >> $@ ; for f in $^; do cat $$f >> $@; done ; echo '#endif' >> $@ -test: cxx unit_test test_examples uds_prefix test_qemu +isotp_c_wrapped.h: $(shell find src/tp/isotp-c -name '*.h') + echo '#if defined(UDS_ISOTP_C)' >> $@ ; for f in $^; do cat $$f >> $@; done ; echo '#endif' >> $@ -fuzz: CC=clang-14 -fuzz: ASAN = -fsanitize=fuzzer,signed-integer-overflow,address,undefined -fprofile-instr-generate -fcoverage-mapping -fuzz: OPTS = -g -DUDS_TP=UDS_TP_CUSTOM -DUDS_CUSTOM_MILLIS -fuzz: iso14229.c iso14229.h fuzz_server.c Makefile - $(CC) $(OPTS) $(WARN) $(INCS) $(TFLAGS) $(ASAN) fuzz_server.c iso14229.c -o fuzzer - $(RUN) ./fuzzer corpus +iso14229.c: $(shell find src -name '*.c') isotp_c_wrapped.c + echo '#include "iso14229.h"' > $@ ; for f in $^; do echo; echo '#ifdef UDS_LINES'; echo "#line 1 \"$$f\""; echo '#endif'; cat $$f | sed -e 's,#include ".*,,'; done >> $@ -clean: - rm -f client server test_iso14229 iso14229.o +iso14229.h: $(shell find src -name '*.h') isotp_c_wrapped.h + ( echo '#ifndef ISO14229_H'; echo '#define ISO14229_H'; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/version.h src/sys.h src/sys_arduino.h src/sys_unix.h src/sys_win32.h src/sys_esp32.h src/config.h src/util.h src/tp.h src/uds.h src/client.h src/server.h isotp_c_wrapped.h src/tp/*.h |sed -e 's,#include ".*,,' -e 's,^#pragma once,,' ; echo '#endif'; echo '#ifdef __cplusplus'; echo '}'; echo '#endif';) > $@ -.phony: clean test_examples \ No newline at end of file +.phony: clean diff --git a/README.md b/README.md index 9033bd0..0f35866 100644 --- a/README.md +++ b/README.md @@ -5,61 +5,36 @@

-

- 简体中文 -

+iso14229 is an implementation of UDS (ISO14229) targeting embedded systems. It is tested with [`isotp-c`](https://github.com/lishen2/isotp-c) as well as [linux kernel](https://github.com/linux-can/can-utils/blob/master/include/linux/can/isotp.h) ISO15765-2 (ISO-TP) transport layer implementations. -iso14229 is a server and client session-layer implementation of (ISO14229-1:2013) targeting embedded systems. It is tested with [`isotp-c`](https://github.com/lishen2/isotp-c) as well as [linux kernel](https://github.com/linux-can/can-utils/blob/master/include/linux/can/isotp.h) ISO15765-2 (ISO-TP) transport layer implementations. +API status: **not yet stable**. -API status: **stabilizing** +## Using this library -## quickstart: server +1. Download `iso14229.zip` from the [releases page](https://github.com/driftregion/iso14229/releases), copy `iso14229.c` and `iso14229.h` into your source tree and build. +2. Look at the [examples](./examples) -```c -#include "iso14229.h" - -static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - switch (ev) { - case UDS_SRV_EVT_EcuReset: { // 0x10 - UDSECUResetArgs_t *r = (UDSECUResetArgs_t *)arg; - printf("got ECUReset request of type %x\n", r->type); - return kPositiveResponse; - default: - return kServiceNotSupported; - } - } -} - -int main() { - UDSServer_t server; - UDSServerConfig_t cfg = { - .fn = &fn, - }; - UDSServerInit(&server, &cfg); - for (;;) { - UDSServerPoll(&server); - } -} -``` +## Build Systems -## quickstart: client - -```c -// see examples/client.c -``` +iso14229 is designed to build on any platform. ## Preprocessor Defines | Define | Description | Valid values | | - | - | - | -| `UDS_ARCH` | Select a porting target | `UDS_ARCH_CUSTOM`, `UDS_ARCH_UNIX` | -| `UDS_TP` | Select a transport layer | `UDS_TP_ISOTP_C`, `UDS_TP_ISOTP_SOCKET` | -| `UDS_CUSTOM_MILLIS` | Use your own `millis()` implementation | defined or not defined | +| `-DUDS_TP_ISOTP_C` | build the isotp-c transport layer (recommended for bare-metal systems) | on/off | +| `-DUDS_TP_ISOTP_SOCK` | build the isotp socket transport layer (recommended for linux) | on/off| +| `-DUDS_TP_ISOTP_C_SOCKETCAN` | build the isotp-c transport layer with socketcan support (linux-only) | on/off| +| `UDS_...` | Additional configuration options | see [`src/config.h`](src/config.h) | +| `UDS_SYS` | Selects target system | see [`src/sys.h`](src/sys.h) | -Features: -- all memory allocation is static -- architecture-independent. tested on arm, x86-64, ppc, ppc64. see [test_qemu.py](./test_qemu.py) -- has many existing unit-tests and tests are easy to extend +## Features + +- entirely static memory allocation. (no `malloc`, `calloc`, ...) +- highly portable and tested + - architectures: arm, x86-64, ppc, ppc64, risc + - systems: linux, Windows, esp32, Arduino, NXP s32k + - transports: isotp-c, linux isotp sockets ## supported functions (server and client ) @@ -91,11 +66,15 @@ Features: | 0x85 | control DTC setting | ✅ | | 0x86 | response on event | ❌ | +## Running Tests + +See [test_all.sh](./test_all.sh) and [test/README.md](test/README.md) + # Documentation ## Server Events -see `enum UDSServerEvent` in [iso14229.h](./iso14229.h) +see `enum UDSServerEvent` in [src/uds.h](src/uds.h) ### `UDS_SRV_EVT_DiagSessCtrl` (0x10) @@ -352,50 +331,17 @@ contributions are welcome - [`isotp-c`](https://github.com/lishen2/isotp-c) which this project embeds -# License - -MIT - # Changelog -## 0.0.0 -- initial release - -## 0.1.0 -- Add client -- Add server SID 0x27 SecurityAccess -- API changes - -## 0.2.0 -- removed all instances of `__attribute__((packed))` -- refactored server download functional unit API to simplify testing -- refactored tests - - ordered by service - - documented macros -- removed middleware -- simplified server routine control API -- removed redundant function `iso14229ServerEnableService` -- updated example - -## 0.3.0 -- added `iso14229ClientRunSequenceBlocking(...)` -- added server and client examples -- simplified test flow, deleted opaque macros and switch statements -- flattened client and server main structs -- simplified usage by moving isotp-c initialization parameters into server/client config structs -- remove redundant buffers in server - +## 0.7.1 +- amalgamated sources into `iso14229.c` and `iso14229.h` to ease integration -## 0.4.0 -- refactor RDBIHandler to pass a function pointer that implements safe memmove rather than requiring the user to keep valid data around for an indefinite time or risking a buffer overflow. -- Prefer fixed-width. Avoid using `enum` types as return types and in structures. -- Transport layer is now pluggable and supports the linux kernel ISO-TP driver in addition to `isotp-c`. See [examples](./examples/README.md). - -## 0.5.0 -- usability: refactored into a single .c/.h module -- usability: default transport layer configs are now built-in -- API cleanup: use `UDS` prefix on all exported functions -- API cleanup: use a single callback function for all server events +## 0.7.0 +- test refactoring. theme: test invariance across different transports and processor architectures +- breaking API changes: + - overhauled transport layer implementation + - simplified client and server init + - `UDS_ARCH_` renamed to `UDS_SYS_` ## 0.6.0 - breaking API changes: @@ -408,185 +354,40 @@ MIT - added `UDS_SRV_EVT_DoScheduledReset` - improve client error handling ---- - -# Design Docs - -## ISO-TP interface - -`iso14229` supports opaque transports. Use `Iso14229TpHandle_t` to wrap a transport. - - -### PDU transmission complete - -ISO14229-1 2013 6.1 describes a request-confirmation primitive to "indicate that the date passed in the service request primitive is successfully sent on the vehicle communication bus the diagnostic tester is connected to" - - -#### ISOTP-C -- **polling** -- `IsoTpLink.send_status` is either `IDLE`, `INPROGRESS`, or `ERROR` - -#### PCAN-ISO-TP -- **polling** - - https://www.peak-system.com/PCAN-ISO-TP-API.372.0.html - - `PCAN-ISO-TP_2016.h` contains a function for reading the transport status which includes `PCANTP_ISOTP_MSGTYPE_FLAG_INDICATION_TX` - -#### linux kernel isotp driver -- **blocking** -- https://github.com/hartkopp/can-isotp/issues/27 (Get status of transmission?) -- https://github.com/hartkopp/can-isotp/issues/51 - - -If you're using the linux kernel driver, then you have threads and can use the excellent `python-udsoncan` to implement a client. - ----- -- "The Functional addressing is applied only to single frame transmission" -- Specification of Diagnostic Communication (Diagnostic on CAN - Network Layer) -- - -```plantuml -@startuml - -@enduml -``` - -## Client State Machine - -```plantuml -@startuml -title 客户端请求状态机 -note as N1 -enum { - kNoError=0, - kErrBadRequest, - kErrP2Timeout, -} ClientErr; - -static inline bool isRequestComplete() {return state==Idle;} - -while (Idle != client->state) { - receiveCAN(client); - UDSClientPoll(client); -} -end note - -state Idle -state Sending -state Sent -state SentAwaitResponse -state ProcessResponse -Idle: if (ISOTP_RET_OK == isotp_receive(...)) // Error -ProcessResponse: isotp_receive() -ProcessResponse: _ClientValidateResponse(...) -ProcessResponse: _ClientHandleResponse(...) - -Sending --> Sent: 传输层完成传输 - -Sent --> Idle : suppressPositiveResponse -Sending --> SentAwaitResponse: !suppressPositiveResponse -SentAwaitResponse -> Idle: 响应收到了 ||\np2 超时 -SentAwaitResponse --> ProcessResponse : ISOTP_RECEIVE_STATUS_FULL == link->receive_status -ProcessResponse --> Idle - -[*] -> Idle -Idle -> Sending : _SendRequest() - -@enduml -``` +## 0.5.0 +- usability: refactored into a single .c/.h module +- usability: default transport layer configs are now built-in +- API cleanup: use `UDS` prefix on all exported functions +- API cleanup: use a single callback function for all server events -```plantuml -@startuml -title Request Lifecycle -alt normal - alt positive response - client --> client: Sending - client -> server : *Any* Service - client --> client: SentAwaitResponse: set p2 - alt 0x78 requestCorrectlyReceived-ResponsePending - server -> client : 0x3F 0x78 - client -->server : txLink idle - client --> client: SentAwaitResponse: set p2star - end - server -> client : Positive Service Response - client --> client: Idle - else negative response - server -> client !! : Negative Service Response - client --> client: Idle: RequestErrorNegativeResponse - else SID mismatch - server -> client !! : Mismatched Service Response - client --> client: Idle: RequestErrorResponseSIDMismatch - end -else unexpected response - server -> client !! : Unexpected Response - client --> client: Idle: RequestErrorUnsolicitedResponse -end -@enduml -``` +## 0.4.0 +- refactor RDBIHandler to pass a function pointer that implements safe memmove rather than requiring the user to keep valid data around for an indefinite time or risking a buffer overflow. +- Prefer fixed-width. Avoid using `enum` types as return types and in structures. +- Transport layer is now pluggable and supports the linux kernel ISO-TP driver in addition to `isotp-c`. See [examples](./examples/README.md). +## 0.3.0 +- added `iso14229ClientRunSequenceBlocking(...)` +- added server and client examples +- simplified test flow, deleted opaque macros and switch statements +- flattened client and server main structs +- simplified usage by moving isotp-c initialization parameters into server/client config structs +- remove redundant buffers in server -```plantuml -@startuml -' !pragma useVerticalIf on -title 客户端请求流程 -start - -:clientSendRequest(); -if (验证参数) then (对) -:ok; -else (不对) -:foo; -detach -endif - -:clearRequestContext(); -if (等待UDS访问) then (访问接收了,进入UDS会话) -else (时间超过20ms) -@enduml -``` +## 0.2.0 +- removed all instances of `__attribute__((packed))` +- refactored server download functional unit API to simplify testing +- refactored tests + - ordered by service + - documented macros +- removed middleware +- simplified server routine control API +- removed redundant function `iso14229ServerEnableService` +- updated example -## Server 0x78 requestCorrectlyReceived-ResponsePending - -```plantuml -@startuml -client -> server : *Any* Service -server -> userServiceHandler: handler(args) -note right: Doing this will take a long time\nso I return 0x78 -userServiceHandler -> server: 0x78 -server -> client : 0x3F 0x78 -client -->server : txLink idle -server -> userServiceHandler: handler(args) -note right: actually call the long-running service -... p2* > t > p2 ... -userServiceHandler -> server : Service Response -server -> client : Service Response -@enduml -``` +## 0.1.0 +- Add client +- Add server SID 0x27 SecurityAccess +- API changes -```plantuml -@startuml -' !pragma useVerticalIf on -title 0x78流程(写flash) -start - -:BufferedWriterWrite(BufferedWriter *self, const uint8_t *ibuf, uint32_t size, bool RCRRP); - -if (RCRRP) then (true) -:write to flash; -else (false) -endif -if (iBufIdx == size) then (true) - :write to pageBuffer; - :iBufIdx = 0; - :return kBufferedWriterWritePending; - :0x78 RCRRP; - detach; -else (false) - :memmove(pageBuffer + pageBufIdx, iBuf + iBufIdx, size - iBufIdx); - :write to pageBuffer; - :iBufIdx += size; - :0x01 PositiveResponse; - :0x78 RCRRP; - detach -endif - -@enduml -``` +## 0.0.0 +- initial release diff --git a/README_zh.md b/README_zh.md deleted file mode 100644 index 1da6f11..0000000 --- a/README_zh.md +++ /dev/null @@ -1,554 +0,0 @@ -# iso14229 - -

-Build Status - -

- -iso14229是个针对嵌入式系统的UDS(ISO14229-1:2013)服务器和客户端会话层实现。为了让你能够更快上手、本库已包含[`isotp-c`](https://github.com/lishen2/isotp-c)以及linux内核ISO15765-2 (ISO-TP)传输层实现。 - -API状态: **未稳定** - -## 快速上手: 服务器 - -```c -#include "iso14229.h" - -static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - switch (ev) { - case UDS_SRV_EVT_EcuReset: { // 0x10 - UDSECUResetArgs_t *r = (UDSECUResetArgs_t *)arg; - printf("got ECUReset request of type %x\n", r->type); - return kPositiveResponse; - default: - return kServiceNotSupported; - } - } -} - -int main() { - UDSServer_t server; - UDSServerConfig_t cfg = { - .fn = &fn, - }; - UDSServerInit(&server, &cfg); - for (;;) { - UDSServerPoll(&server); - } -} -``` - -## 快速上手: 客户端 - -```c -// 参考 examples/client.c -``` - -特点: -- 静态内存分配 -- 独立于处理器架构 测试了: arm, x86-64, ppc, ppc64。参考[test_qemu.py](./test_qemu.py) -- 单元测试又多又容易扩展 - -## 支持服务(服务器和客户端) - -| SID | 英文名 | 支持 | -| - | - | - | -| 0x10 | diagnostic session control | ✅ | -| 0x11 | ECU reset | ✅ | -| 0x14 | clear diagnostic information | ❌ | -| 0x19 | read DTC information | ❌ | -| 0x22 | read data by identifier | ✅ | -| 0x23 | read memory by address | ❌ | -| 0x24 | read scaling data by identifier | ❌ | -| 0x27 | security access | ✅ | -| 0x28 | communication control | ✅ | -| 0x2A | read periodic data by identifier | ❌ | -| 0x2C | dynamically define data identifier | ❌ | -| 0x2E | write data by identifier | ✅ | -| 0x2F | input control by identifier | ❌ | -| 0x31 | routine control | ✅ | -| 0x34 | request download | ✅ | -| 0x35 | request upload | ✅ | -| 0x36 | transfer data | ✅ | -| 0x37 | request transfer exit | ✅ | -| 0x38 | request file transfer | ❌ | -| 0x3D | write memory by address | ❌ | -| 0x3E | tester present | ✅ | -| 0x83 | access timing parameter | ❌ | -| 0x84 | secured data transmission | ❌ | -| 0x85 | control DTC setting | ✅ | -| 0x86 | response on event | ❌ | - -# 文档 - -## 服务器事件 - -参考[iso14229.h](./iso14229.h) `enum UDSServerEvent` - -### `UDS_SRV_EVT_DiagSessCtrl` (0x10) 会话控制 - -#### 参数 - -```c -typedef struct { - const enum UDSDiagnosticSessionType type; /**< 请求会话类型 */ - uint16_t p2_ms; /**< 可选返回值: p2时间设置 */ - uint32_t p2_star_ms; /**< 可选返回值: p2*时间设置 */ -} UDSDiagSessCtrlArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 允许进入请求中的会话类型 | -| `0x12` | `kSubFunctionNotSupported` | 本器不支持请求中的会话类型 | -| `0x22` | `kConditionsNotCorrect` | 本器当前无法进入请求的会话类型 | - -### `UDS_SRV_EVT_ECUReset` (0x11) ECU复位 - -#### 参数 - -```c -typedef struct { - const enum UDSECUResetType type; /**< 请求的复位类型 */ - uint8_t powerDownTime; /**< 可选返回值: 通知客户端离关机的时间(0-254秒) 255表示无法判断具体关机时间*/ -} UDSECUResetArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 允许复位 | -| `0x12` | `kSubFunctionNotSupported` | 本器不支持请求的复位类型 | -| `0x22` | `kConditionsNotCorrect` | 本器目前无法复位 | -| `0x33` | `kSecurityAccessDenied` | 当前安全级别不允许复位 | - -### `UDS_SRV_EVT_ReadDataByIdent` (0x22) 读取数据 - -#### 参数 - -```c -typedef struct { - const uint16_t dataId; /*! 数据标识符 */ - /*! 函数:拷贝数据到服务器发送缓冲器. 成功会返回`kPositiveResponse`。若数据大小超过了服务器发送缓冲器的大小、会返回`kResponseTooLong`。*/ - const uint8_t (*copy)(UDSServer_t *srv, const void *src, - uint16_t count); -} UDSRDBIArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 允许读取 (注意调用`copy(...)`) | -| `0x14` | `kResponseTooLong` | 响应大小超过发送缓冲器大小 | -| `0x31` | `kRequestOutOfRange` | 请求的标识符不支持 | -| `0x33` | `kSecurityAccessDenied` | 当前安全级别不允许读取请求的数据标识符 | - -### `UDS_SRV_EVT_SecAccessRequestSeed`, `UDS_SRV_EVT_SecAccessValidateKey` (0x27) 安全访问 - -#### 参数 - -```c -typedef struct { - const uint8_t level; /*! 请求安全级别 */ - const uint8_t *const dataRecord; /*! 请求数据 */ - const uint16_t len; /*! 请求数据大小 */ - /*! 函数:拷贝数据到服务器发送缓冲器. 成功会返回`kPositiveResponse`。若数据大小超过了服务器发送缓冲器的大小、会返回`kResponseTooLong`。*/ - uint8_t (*copySeed)(UDSServer_t *srv, const void *src, - uint16_t len); -} UDSSecAccessRequestSeedArgs_t; - -typedef struct { - const uint8_t level; /*! 请求安全访问级别 */ - const uint8_t *const key; /*! 客户端发过来的密钥 */ - const uint16_t len; /*! 密钥大小 */ -} UDSSecAccessValidateKeyArgs_t; -``` -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 肯定响应 | -| `0x12` | `kSubFunctionNotSupported` | 该安全级别未支持 | -| `0x22` | `kConditionsNotCorrect` | 当前无法处理该请求 | -| `0x31` | `kRequestOutOfRange` | `dataRecord`的数据无效 | -| `0x35` | `kInvalidKey` | 密钥对不上 | -| `0x36` | `kExceededNumberOfAttempts` | 密码错误太多 | -| `0x37` | `kRequiredTimeDelayNotExpired` | 不晓得 | - -### `UDS_SRV_EVT_CommCtrl` (0x28) 通讯控制 - -#### 参数 - -```c -typedef struct { - enum UDSCommunicationControlType ctrlType; - enum UDSCommunicationType commType; -} UDSCommCtrlArgs_t; -``` -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 肯定响应 | -| `0x12` | `kSubFunctionNotSupported` | 请求中的通讯控制类型未支持 | -| `0x22` | `kConditionsNotCorrect` | 当前无法开启/关闭请求中的通讯控制类型| -| `0x31` | `kRequestOutOfRange` | 请求中的通讯控制类型或者通讯类型有错 | - -### `UDS_SRV_EVT_WriteDataByIdent` (0x2E) 写入数据 - -#### 参数 - -```c -typedef struct { - const uint16_t dataId; /*! 数据标识符 */ - const uint8_t *const data; /*! 数据 */ - const uint16_t len; /*! 数据大小 */ -} UDSWDBIArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 写入成功 | -| `0x22` | `kConditionsNotCorrect` | 当前无法写入 | -| `0x31` | `kRequestOutOfRange` | 数据标识符不支持或者数据内容有错 | -| `0x33` | `kSecurityAccessDenied` | 当前安全级别不允许写入请求中的数据标识符 | -| `0x72` | `kGeneralProgrammingFailure` | 写入内存失败 | - -### `UDS_SRV_EVT_RoutineCtrl` (0x31) 例程控制 - -#### 参数 - -```c -typedef struct { - const uint8_t ctrlType; /*! 例程控制类型 */ - const uint16_t id; /*! 例程标识符 */ - const uint8_t *optionRecord; /*! 客户端可选数据 */ - const uint16_t len; /*! 数据大小 */ - /*! 函数:拷贝数据到服务器发送缓冲器. 成功会返回`kPositiveResponse`。若数据大小超过了服务器发送缓冲器的大小、会返回`kResponseTooLong`。*/ - uint8_t (*copyStatusRecord)(UDSServer_t *srv, const void *src, - uint16_t len); -} UDSRoutineCtrlArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 肯定响应 | -| `0x22` | `kConditionsNotCorrect` | 当前无法操作 | -| `0x24` | `kRequestSequenceError` | 要求停止但未开始。要求开始但已开始(可选)。没有结果因为例程从来没有开始。| -| `0x31` | `kRequestOutOfRange` | 请求中的例程标识符未支持或者`optionRecord`无效 | -| `0x33` | `kSecurityAccessDenied` | 当前安全访问级别不允许请求的操作 | -| `0x72` | `kGeneralProgrammingFailure` | 内部内存操作失败(如:擦除flash) | - - -### `UDS_SRV_EVT_RequestDownload` (0x34) 请求下载 - -#### 参数 - -```c -typedef struct { - const void *addr; /*! 请求下载地址 */ - const size_t size; /*! 请求下载大小 */ - const uint8_t dataFormatIdentifier; /*! 可选:数据格式标识符 */ - uint16_t maxNumberOfBlockLength; /*! 返回值: 通知客户端能接受的`TransferData`长度是多长 */ -} UDSRequestDownloadArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 肯定响应 | -| `0x22` | `kConditionsNotCorrect` | 当前无法操作 | -| `0x31` | `kRequestOutOfRange` | `dataFormatIdentifier`,`addr`或者`size`无效。 | -| `0x33` | `kSecurityAccessDenied` | 当前安全访问级别不允许请求的操作 | -| `0x34` | `kAuthenticationRequired` | 客户端权利不足 | -| `0x70` | `kUploadDownloadNotAccepted` | 因故障无法下载 | - -### `UDS_SRV_EVT_TransferData` (0x36) 传输数据 - -#### 参数 - -```c -typedef struct { - const uint8_t *const data; /*! 数据 */ - const uint16_t len; /*! 数据大小 */ - /*! 函数:拷贝数据到服务器发送缓冲器. 成功会返回`kPositiveResponse`。若数据大小超过了服务器发送缓冲器的大小、会返回`kResponseTooLong`。*/ - uint8_t (*copyResponse)( - UDSServer_t *srv, const void *src, - uint16_t len); -} UDSTransferDataArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 肯定响应 | -| `0x31` | `kRequestOutOfRange` | `data`内容无效或者大小不对 | -| `0x72` | `kGeneralProgrammingFailure` | 写入内存失败 | -| `0x92` | `kVoltageTooHigh` | 无法写flash:电压过高 | -| `0x93` | `kVoltageTooLow` | 无法写flash:电压过低 | - -### `UDS_SRV_EVT_RequestTransferExit` (0x37) 请求传输结束 - -#### 参数 - -```c -typedef struct { - const uint8_t *const data; /*! 数据 */ - const uint16_t len; /*! 数据大小 */ - /*! 函数:拷贝数据到服务器发送缓冲器. 成功会返回`kPositiveResponse`。若数据大小超过了服务器发送缓冲器的大小、会返回`kResponseTooLong`。*/ - uint8_t (*copyResponse)(UDSServer_t *srv, const void *src, - uint16_t len); -} UDSRequestTransferExitArgs_t; -``` - -#### 支持返回值 - -| 值 | 枚举 | 意义 | -| - | - | - | -| `0x00` | `kPositiveResponse` | 肯定响应 | -| `0x31` | `kRequestOutOfRange` | `data`内容无效或者大小不对 | -| `0x72` | `kGeneralProgrammingFailure` | 完成数据传输失败 | - -## 例子 - -[examples/README.md](examples/README.md) - -### 运行测试 - -```sh -make test -``` - -## qemu - -```sh -CC=powerpc-linux-gnu-gcc make test_bin -qemu-ppc -L /usr/powerpc-linux-gnu test_bin -``` -## wine - -```sh -CC=x86_64-w64-mingw32-gcc make test_bin -wine test_bin.exe -``` - - -# 贡献 - -欢迎提交贡献 - - -# 感谢 - -- [`isotp`](https://github.com/lishen2/isotp-c) which this project embeds - -# 许可 - -MIT - -# 变更记录 - -## 0.0.0 -- 初次发布 - -## 0.1.0 -- 加客户端 -- 加服务器SID 0x27安全访问 -- API更改 - -## 0.2.0 -- 删除所有`__attribute__((packed))` -- 为了简化测试、重构服务器下载功能单元 -- 重构测试 - - 按服务排列 - - 给宏定义写文档 -- 删掉了中间件 -- 简化了服务器例程控制API -- 删掉了重复函数`udsServerEnableService` -- 更新例子 - -## 0.3.0 -- 加`udsClientRunSequenceBlocking(...)` -- 加了服务器和客户端例子 -- 简化测试流程、删掉了过分模糊宏定义和switch结构 -- 服务器和客户端结构体简化:尽量用一层深度 -- 简化使用、放isotp-c初始化参数到服务器/客户端配置里面 -- 删除重复服务器缓冲器 - -## 0.4.0 -- 重构RDBIHandler:用安全memmove -- 尽可能不用enum在结构体里面 -- 传输层可插件。现在支持linux内核ISO-TP驱动。`isotp-c`同时也支持。看看例子 [examples](./examples/README.md) - -## 0.5.0 -- 可用性: 重构成单个.c/.h模块 -- 可用性: 默认传输层配置现在自带 -- API整理: 用`UDS`前缀在所有导出函数上 -- API整理: 服务器事件用单个回调函数 - -## 0.6.0 -- API更改: - - `UDSClientErr_t`合并到`UDSErr_t` - - `TP_SEND_INPROGRESS`改名为`UDS_TP_SEND_IN_PROGRESS` - - 重构了`UDSTpHandle_t` - - `UDS_TP_LINUX_SOCKET`改名为`UDS_TP_ISOTP_SOCKET` -- 增加了服务器fuzz测试以及qemu测试 -- 整理例子测试,例子增加了isotp-c/socketcan传输 -- 增加了`UDS_SRV_EVT_DoScheduledReset`服务器事件 ---- - - -# 开发者文档 - -## 客户端请求状态机 - -```plantuml -@startuml -title 客户端请求状态机 -note as N1 -enum { - kNoError=0, - kErrBadRequest, - kErrP2Timeout, -} ClientErr; - -static inline bool isRequestComplete() {return state==Idle;} - -while (Idle != client->state) { - receiveCAN(client); - UDSClientPoll(client); -} -end note - -state Idle -state Sending -state Sent -state SentAwaitResponse -state ProcessResponse -Idle: if (ISOTP_RET_OK == isotp_receive(...)) // Error -ProcessResponse: isotp_receive() -ProcessResponse: _ClientValidateResponse(...) -ProcessResponse: _ClientHandleResponse(...) - -Sending --> Sent: 传输层完成传输 - -Sent --> Idle : suppressPositiveResponse -Sending --> SentAwaitResponse: !suppressPositiveResponse -SentAwaitResponse -> Idle: 响应收到了 ||\np2 超时 -SentAwaitResponse --> ProcessResponse : ISOTP_RECEIVE_STATUS_FULL == link->receive_status -ProcessResponse --> Idle - -[*] -> Idle -Idle -> Sending : _SendRequest() - -@enduml -``` - -```plantuml -@startuml -title Request Lifecycle -alt normal - alt positive response - client --> client: Sending - client -> server : *Any* Service - client --> client: SentAwaitResponse: set p2 - alt 0x78 requestCorrectlyReceived-ResponsePending - server -> client : 0x3F 0x78 - client -->server : txLink idle - client --> client: SentAwaitResponse: set p2star - end - server -> client : Positive Service Response - client --> client: Idle - else negative response - server -> client !! : Negative Service Response - client --> client: Idle: RequestErrorNegativeResponse - else SID mismatch - server -> client !! : Mismatched Service Response - client --> client: Idle: RequestErrorResponseSIDMismatch - end -else unexpected response - server -> client !! : Unexpected Response - client --> client: Idle: RequestErrorUnsolicitedResponse -end -@enduml -``` - - -```plantuml -@startuml -' !pragma useVerticalIf on -title 客户端请求流程 -start - -:clientSendRequest(); -if (验证参数) then (对) -:ok; -else (不对) -:foo; -detach -endif - -:clearRequestContext(); -if (等待UDS访问) then (访问接收了,进入UDS会话) -else (时间超过20ms) -@enduml -``` - -## 服务器 0x78 requestCorrectlyReceived-ResponsePending - - -```plantuml -@startuml -client -> server : *Any* Service -server -> userServiceHandler: handler(args) -note right: Doing this will take a long time\nso I return 0x78 -userServiceHandler -> server: 0x78 -server -> client : 0x3F 0x78 -client -->server : txLink idle -server -> userServiceHandler: handler(args) -note right: actually call the long-running service -... p2* > t > p2 ... -userServiceHandler -> server : Service Response -server -> client : Service Response -@enduml -``` - -```plantuml -@startuml -' !pragma useVerticalIf on -title 0x78流程(写flash) -start - -:BufferedWriterWrite(BufferedWriter *self, const uint8_t *ibuf, uint32_t size, bool RCRRP); - -if (RCRRP) then (true) -:write to flash; -else (false) -endif -if (iBufIdx == size) then (true) - :write to pageBuffer; - :iBufIdx = 0; - :return kBufferedWriterWritePending; - :0x78 RCRRP; - detach; -else (false) - :memmove(pageBuffer + pageBufIdx, iBuf + iBufIdx, size - iBufIdx); - :write to pageBuffer; - :iBufIdx += size; - :0x01 PositiveResponse; - :0x78 RCRRP; - detach -endif - -@enduml -``` diff --git a/WORKSPACE b/WORKSPACE index e69de29..0e5a5d5 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -0,0 +1,35 @@ + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "hedron_compile_commands", + url = "https://github.com/hedronvision/bazel-compile-commands-extractor/archive/e16062717d9b098c3c2ac95717d2b3e661c50608.tar.gz", + strip_prefix = "bazel-compile-commands-extractor-e16062717d9b098c3c2ac95717d2b3e661c50608", + sha256 = "ed5aea1dc87856aa2029cb6940a51511557c5cac3dbbcb05a4abd989862c36b4" +) +load("@hedron_compile_commands//:workspace_setup.bzl", "hedron_compile_commands_setup") +hedron_compile_commands_setup() + +http_archive( + name = "cmocka", + url = "https://cmocka.org/files/1.1/cmocka-1.1.7.tar.xz", + sha256 = "810570eb0b8d64804331f82b29ff47c790ce9cd6b163e98d47a4807047ecad82", + build_file = "//test:cmocka.BUILD", + strip_prefix = "cmocka-1.1.7", +) + +http_archive( + name = "s32k_sdk", + url = "https://www.keil.com/pack/Keil.S32_SDK_DFP.1.5.0.pack", + sha256 = "95f7649aee66deb656cc999f300663544113245aecd407cfc90548377355353b", + type = "zip", + build_file = "//platforms:s32k_sdk.BUILD", +) + +http_archive( + name = "CMSIS", + build_file = "//platforms:cmsis.BUILD", + sha256 = "14b366f2821ee5d32f0d3bf48ef9657ca45347261d0531263580848e9d36f8f4", + urls = ["http://www.keil.com/pack/ARM.CMSIS.5.9.0.pack"], + type = ".zip", +) \ No newline at end of file diff --git a/amalgamate.py b/amalgamate.py new file mode 100644 index 0000000..60dff98 --- /dev/null +++ b/amalgamate.py @@ -0,0 +1,109 @@ +import os +import re +import argparse +import glob + +out_dir = os.getenv("BUILD_WORKSPACE_DIRECTORY", os.getcwd()) +iso14229_h = os.path.join(out_dir, "iso14229.h") +iso14229_c = os.path.join(out_dir, "iso14229.c") + +parser = argparse.ArgumentParser() +parser.add_argument("--out_c", help="output c file", default=iso14229_c) +parser.add_argument("--out_h", help="output h file", default=iso14229_h) +parser.add_argument("srcs", nargs="*") +args = parser.parse_args() +srcs = {os.path.basename(src): src for src in args.srcs} + + +def strip_includes(src): + return re.sub(r'#include ".*', "", src, flags=re.MULTILINE) + +isotp_c_wrapped_c = "#if defined(UDS_ISOTP_C)\n" + \ + strip_includes(open("src/tp/isotp-c/isotp.c").read()) + \ + "#endif\n" + +isotp_c_wrapped_h = "#if defined(UDS_ISOTP_C)\n" + \ + "\n".join([strip_includes(open("src/tp/isotp-c/" + h).read()) for h in [ + "isotp_config.h", + "isotp_defines.h", + "isotp.h", + ]]) + \ + "#endif\n" + +with open(args.out_c, "w") as f: + f.write("#include \"iso14229.h\"\n") + for src in [ + "src/client.c", + "src/server.c", + "src/tp.c", + "src/util.c", + "src/tp/isotp_c.c", + "src/tp/isotp_c_socketcan.c", + "src/tp/isotp_sock.c", + "src/tp/mock.c", + ]: + f.write("\n") + f.write("#ifdef UDS_LINES\n") + f.write(f'#line 1 "{src}"' + "\n") + f.write("#endif\n") + with open(src) as src_file: + f.write(strip_includes(src_file.read())) + f.write("\n") + + f.write(isotp_c_wrapped_c) + f.write("\n") + + +with open(args.out_h, "w") as f: + f.write("#ifndef ISO14229_H\n") + f.write("#define ISO14229_H\n") + f.write("\n") + f.write("#ifdef __cplusplus\n") + f.write("extern \"C\" {\n") + f.write("#endif\n") + f.write("\n") + for src in [ + "src/version.h", + "src/sys.h", + "src/sys_arduino.h", + "src/sys_unix.h", + "src/sys_win32.h", + "src/sys_esp32.h", + "src/config.h", + "src/util.h", + "src/tp.h", + "src/uds.h", + "src/client.h", + "src/server.h", + ]: + f.write("\n") + with open(src) as src_file: + f.write(strip_includes(src_file.read())) + f.write("\n") + + f.write(isotp_c_wrapped_h) + + + for src in [ + "src/tp/isotp_c.h", + "src/tp/isotp_c_socketcan.h", + "src/tp/isotp_sock.h", + "src/tp/mock.h", + ]: + f.write("\n") + with open(src) as src_file: + f.write(strip_includes(src_file.read())) + f.write("\n") + + f.write("\n") + f.write("#ifdef __cplusplus\n") + f.write("}\n") + f.write("#endif\n") + f.write("\n") + f.write("#endif\n") + +# os.chmod(iso14229_h, 0o444) +# os.chmod(iso14229_c, 0o444) + +if __name__ == "__main__": + print(f"amalgamated source files written to {args.out_c} and {args.out_h}") \ No newline at end of file diff --git a/examples/README.md b/examples/README.md deleted file mode 100644 index 9ecb1a0..0000000 --- a/examples/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# 例子 / Examples - -这个例子用linux socketcan. Windows用户可以用个linux虚拟机。WSL应该也可以。 - -This example uses linux socketcan. Windows users can run it in a linux VM. It might work under WSL. - - -# socketcan 虚拟接口设置 / virtual interface setup -```sh -# 设置虚拟socketCAN接口 -# setup a virtual socketCAN interface -sudo ip link add name vcan0 type vcan -sudo ip link set vcan0 up -``` -# 构建例子 / building the example -``` -make example -``` - -# 在vcan0接口上运行例子服务器 / run the server on vcan0 -```sh -./server vcan0 -# 服务器会一直跑。用 ctrl+c 推出 -# The server will run continuously. Use ctrl+c to exit -``` - -# 在vcan0接口上运行客户端 (打开另一个终端) / run the client on vcan0 (open another shell) -```sh -./client vcan0 -# 客户端跑完流程后会自动退出 -# The client will exit after it has executed its sequence -``` - -# 用candump看看输出 -```sh -> candump -tz vcan0 - (000.000000) vcan0 111 [4] 01 02 03 04 - (000.000028) vcan0 111 [4] 01 02 03 04 - (000.000090) vcan0 701 [8] 02 11 01 00 00 00 00 00 # 0x11 ECU复位请求 - (000.010199) vcan0 700 [8] 02 51 01 00 00 00 00 00 # 0x11 ECU复位肯定响应 - (000.010213) vcan0 701 [8] 05 22 00 01 00 08 00 00 # 0x22 RDBI请求 - (000.020318) vcan0 700 [8] 10 1B 62 00 01 00 00 08 # ISO-TP流控框 - (000.020326) vcan0 701 [8] 30 08 00 00 00 00 00 00 # 0x22 RDBI请求 - (000.030416) vcan0 700 [8] 21 49 27 6D 20 61 20 55 # 0x22 RDBI响应 (1) - (000.040674) vcan0 700 [8] 22 44 53 20 73 65 72 76 # 0x22 RDBI响应 (2) - (000.050829) vcan0 700 [8] 23 65 72 20 20 20 20 00 # 0x22 RDBI响应 (3) - (000.051509) vcan0 701 [8] 02 10 03 00 00 00 00 00 # 0x10 会话控制 - (000.072713) vcan0 700 [8] 03 7F 10 33 00 00 00 00 # 0x10 会话控制否定响应 - (000.072979) vcan0 701 [8] 02 11 04 00 00 00 00 00 # 0x11 ECU复位请求 - (000.124015) vcan0 700 [8] 03 51 04 FF 00 00 00 00 # 0x11 ECU复位肯定响应 - -``` - - -# 也可以用python-udsoncan实现客户端 / You can also use python-udsoncan to implement a client - -```sh -# 在另外一个终端,安装python依赖性 -# In another shell, install the required python packages -pip3 install -r example/requirements.txt - -# 然后运行客户端 -# then run the client -./example/client.py vcan0 -``` diff --git a/examples/arduino_server/README.md b/examples/arduino_server/README.md new file mode 100644 index 0000000..901c6b1 --- /dev/null +++ b/examples/arduino_server/README.md @@ -0,0 +1,7 @@ +# Required Hardware + +- [Arduino MKR-WIFI 1010](https://store-usa.arduino.cc/products/arduino-mkr-wifi-1010) +- [MKR CAN Shield](https://store.arduino.cc/products/arduino-mkr-can-shield) + +Note that the labeling of CAN-H and CAN-L on silkscreen on the MKR CAN Shield is contradictory on the top and bottom of the board. The top is correct. + diff --git a/examples/arduino_server/main/iso14229.c b/examples/arduino_server/main/iso14229.c new file mode 120000 index 0000000..fa2cd67 --- /dev/null +++ b/examples/arduino_server/main/iso14229.c @@ -0,0 +1 @@ +../../../bazel-bin/iso14229.c \ No newline at end of file diff --git a/examples/arduino_server/main/iso14229.h b/examples/arduino_server/main/iso14229.h new file mode 120000 index 0000000..c026bd1 --- /dev/null +++ b/examples/arduino_server/main/iso14229.h @@ -0,0 +1 @@ +../../../bazel-bin/iso14229.h \ No newline at end of file diff --git a/examples/arduino_server/main/main.ino b/examples/arduino_server/main/main.ino new file mode 100644 index 0000000..0bc348f --- /dev/null +++ b/examples/arduino_server/main/main.ino @@ -0,0 +1,107 @@ +#include +#include "iso14229.h" +#include +#include + +UDSServer_t srv; +UDSISOTpC_t tp; + +int send_can(const uint32_t arb_id, const uint8_t *data, const uint8_t size, void *ud) { + CAN.beginPacket(arb_id); + CAN.write(data, size); + CAN.endPacket(); + return size; +} + +static void CANRecv(UDSISOTpC_t *tp) { + assert(tp); + uint8_t buf[8]; + int len = CAN.parsePacket(); + if (len) { + if (len > 8) { + Serial.println("CAN packet too long, truncating"); + len = 8; + } + CAN.readBytes(buf, len); + UDS_DBG_PRINT("can recv\n"); + UDS_DBG_PRINTHEX(buf, len); + if (CAN.packetId() == tp->phys_sa) { + UDS_DBG_PRINT("phys frame received\n"); + isotp_on_can_message(&tp->phys_link, buf, len); + } else if (CAN.packetId() == tp->func_sa) { + if (ISOTP_RECEIVE_STATUS_IDLE != tp->phys_link.receive_status) { + UDS_DBG_PRINT("func frame received but cannot process because link is not idle"); + return; + } + isotp_on_can_message(&tp->func_link, buf, len); + } + } +} + +extern "C" int print_impl(const char *fmt, ...); + +const UDSISOTpCConfig_t tp_cfg = { + .source_addr=0x7E8, + .target_addr=0x7E0, + .source_addr_func=0x7DF, + .target_addr_func=UDS_TP_NOOP_ADDR, + .isotp_user_send_can=send_can, + .isotp_user_get_ms=UDSMillis, + .isotp_user_debug=NULL, + .user_data=NULL, +}; + +int print_impl(const char *fmt, ...) { + char buf[256]; + va_list args; + va_start(args, fmt); + int ret = vsnprintf(buf, sizeof(buf), fmt, args); + Serial.print(buf); + va_end(args); + return ret; +} + +uint8_t fn(UDSServer_t *srv, int ev, const void *arg) { + Serial.print("Got event "); + Serial.println(ev); + switch(ev) { + case UDS_SRV_EVT_Err: + { + UDSErr_t *p_err = (UDSErr_t *)arg; + Serial.print("Err: "); + Serial.println(*p_err); + break; + } + } +} + +void setup() { + Serial.begin(9600); + while (!Serial); + + if(!UDSServerInit(&srv)) { + Serial.println("UDSServerInit failed"); + while(1); + } + + if (!UDSISOTpCInit(&tp, &tp_cfg)) { + Serial.println("UDSISOTpCInit failed"); + while(1); + } + + srv.tp = &tp.hdl; + srv.fn = fn; + + // start the CAN bus at 500 kbps + if (!CAN.begin(500E3)) { + Serial.println("Starting CAN failed!"); + while (1); + } + + Serial.println("Arduino UDS Server: Setup Complete"); +} + +void loop() { + UDSServerPoll(&srv); + CANRecv(&tp); +} diff --git a/examples/client.c b/examples/client.c deleted file mode 100644 index e9aff27..0000000 --- a/examples/client.c +++ /dev/null @@ -1,143 +0,0 @@ -#include "../iso14229.h" -#include "uds_params.h" -#include -#include -#include -#include -#include -#if UDS_TP == UDS_TP_ISOTP_C -#include "../isotp-c/isotp.h" -#include "isotp-c_on_socketcan.h" -#endif - -static UDSSeqState_t sendHardReset(UDSClient_t *client) { - uint8_t resetType = kHardReset; - printf("%s: sending ECU reset type: %d\n", __func__, resetType); - UDSSendECUReset(client, resetType); - return UDSSeqStateGotoNext; -} - -static UDSSeqState_t awaitPositiveResponse(UDSClient_t *client) { - if (client->err) { - return client->err; - } - if (kRequestStateIdle == client->state) { - printf("got positive response\n"); - return UDSSeqStateGotoNext; - } else { - return UDSSeqStateRunning; - } -} - -static UDSSeqState_t awaitResponse(UDSClient_t *client) { - if (kRequestStateIdle == client->state) { - printf("got response\n"); - return UDSSeqStateGotoNext; - } else { - return UDSSeqStateRunning; - } -} - -static UDSSeqState_t requestSomeData(UDSClient_t *client) { - printf("%s: calling ReadDataByIdentifier\n", __func__); - static const uint16_t didList[] = {0x0001, 0x0008}; - UDSSendRDBI(client, didList, 2); - return UDSSeqStateGotoNext; -} - -static UDSSeqState_t printTheData(UDSClient_t *client) { - uint8_t buf[21]; - uint16_t offset = 0; - int err; - - printf("Unpacked:\n"); - err = UDSUnpackRDBIResponse(client, 0x0001, buf, DID_0x0001_LEN, &offset); - if (err) { - client->err = err; - return UDSSeqStateDone; - } - printf("DID 0x%04x: %d\n", 0x0001, buf[0]); - - err = UDSUnpackRDBIResponse(client, 0x0008, buf, DID_0x0008_LEN, &offset); - if (err) { - client->err = err; - return UDSSeqStateDone; - } - printf("DID 0x%04x: %s\n", 0x0008, buf); - return UDSSeqStateGotoNext; -} - -static UDSSeqState_t enterDiagnosticSession(UDSClient_t *client) { - const uint8_t session = kExtendedDiagnostic; - UDSSendDiagSessCtrl(client, session); - printf("%s: entering diagnostic session %d\n", __func__, session); - return UDSSeqStateGotoNext; -} - -static UDSSeqState_t terminateServerProcess(UDSClient_t *client) { - printf("%s: requesting server shutdown...\n", __func__); - client->options |= UDS_SUPPRESS_POS_RESP; - UDSSendRoutineCtrl(client, kStartRoutine, RID_TERMINATE_PROCESS, NULL, 0); - return UDSSeqStateGotoNext; -} - -static int SleepMillis(uint32_t tms) { - struct timespec ts; - int ret; - ts.tv_sec = tms / 1000; - ts.tv_nsec = (tms % 1000) * 1000000; - do { - ret = nanosleep(&ts, &ts); - } while (ret && errno == EINTR); - return ret; -} - -// clang-format off -/** - * @brief 流程定义 Sequence Definition - */ -static UDSClientCallback callbacks[] = { - requestSomeData, - awaitPositiveResponse, - printTheData, - - enterDiagnosticSession, - awaitResponse, - - // terminateServerProcess, - NULL, -}; -// clang-format on - -int main(int ac, char **av) { - UDSClient_t client; - UDSClientConfig_t cfg = { -#if UDS_TP == UDS_TP_ISOTP_SOCKET - .if_name = "vcan0", -#endif - .phys_recv_id = 0x7E8, - .phys_send_id = 0x7E0, - .func_send_id = 0x7DF, - }; - - if (UDSClientInit(&client, &cfg)) { - exit(-1); - } - - client.cbList = callbacks; - client.cbIdx = 0; - - printf("running sequence. . .\n"); - int running = 1; - do { - running = UDSClientPoll(&client); -#if UDS_TP == UDS_TP_ISOTP_C - SocketCANRecv((UDSTpIsoTpC_t *)client.tp, cfg.phys_recv_id); -#endif - SleepMillis(1); - } while (running); - - printf("sequence completed %ld callbacks with client err: %d\n", client.cbIdx, client.err); - UDSClientDeInit(&client); - return 0; -} diff --git a/examples/client.py b/examples/client.py deleted file mode 100755 index 7d885c2..0000000 --- a/examples/client.py +++ /dev/null @@ -1,106 +0,0 @@ -#!/usr/bin/env python3 - -from can.interfaces.socketcan import SocketcanBus - -import isotp - -from udsoncan.client import Client -from udsoncan.connections import PythonIsoTpConnection -from udsoncan.services import * - -SRV_SEND_ID = 0x7E8 -SRV_PHYS_RECV_ID = 0x7E0 -SRV_FUNC_RECV_ID = 0x7DF - - -def security_algo(level, seed, params): - print(f"level: {level}, seed: {seed}, params: {params}") - return bytes([0]) - - -UDSONCAN_CLIENT_CONFIG = { - "exception_on_negative_response": True, - "exception_on_invalid_response": True, - "exception_on_unexpected_response": True, - "security_algo": security_algo, - "security_algo_params": None, - "tolerate_zero_padding": True, - "ignore_all_zero_dtc": True, - "dtc_snapshot_did_size": 2, # Not specified in standard. 2 bytes matches other services format. - "server_address_format": None, # 8,16,24,32,40 - "server_memorysize_format": None, # 8,16,24,32,40 - "data_identifiers": { - 0x0000: "B", - 0x0001: "b", - 0x0002: "H", - 0x0003: "h", - 0x0004: "I", - 0x0005: "i", - 0x0006: "Q", - 0x0007: "q", - 0x0008: "20B", - }, - "input_output": {}, - "request_timeout": 5, - "p2_timeout": 1.5, - "p2_star_timeout": 5, - "standard_version": 2013, # 2006, 2013, 2020 - "use_server_timing": False, - "exception_on_negative_response": False, -} - -if __name__ == "__main__": - with Client( - conn=PythonIsoTpConnection( - isotp_layer=isotp.CanStack( - bus=(SocketcanBus(channel="vcan0")), - address=isotp.Address(rxid=SRV_SEND_ID, txid=SRV_PHYS_RECV_ID), - params={ - "tx_data_min_length": 8, - "blocksize": 0, - "squash_stmin_requirement": True, - }, - ) - ), - config=UDSONCAN_CLIENT_CONFIG, - ) as client: - - response = client.ecu_reset(ECUReset.ResetType.hardReset) - print(f"Received: {response}") - - response = client.change_session( - DiagnosticSessionControl.Session.extendedDiagnosticSession - ) - print(f"Received: {response}") - - response = client.unlock_security_access(1) - print(f"Received: {response}") - - response = client.change_session( - DiagnosticSessionControl.Session.extendedDiagnosticSession - ) - print(f"Received: {response}") - - response = client.read_data_by_identifier([0x0008]) - print(f"Received: {response}") - print( - f"rdbi data: {bytes(response.service_data.values[0x0008]).decode('ascii')}" - ) - - response = client.read_data_by_identifier([0x0001]) - print(f"Received: {response}") - print(f"rdbi data: {response.service_data.values[0x0001]}") - - response = client.write_data_by_identifier(0x0001, 2) - print(f"Received: {response}") - - response = client.read_data_by_identifier([0x0001]) - print(f"Received: {response}") - print(f"rdbi data: {response.service_data.values[0x0001]}") - - response = client.write_data_by_identifier(0x0001, 0) - print(f"Received: {response}") - - response = client.read_data_by_identifier([0x0001]) - print(f"Received: {response}") - print(f"rdbi data: {response.service_data.values[0x0001]}") diff --git a/examples/esp32_server/CMakeLists.txt b/examples/esp32_server/CMakeLists.txt new file mode 100644 index 0000000..a246da0 --- /dev/null +++ b/examples/esp32_server/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(esp32_server) diff --git a/examples/esp32_server/README.md b/examples/esp32_server/README.md new file mode 100644 index 0000000..2808133 --- /dev/null +++ b/examples/esp32_server/README.md @@ -0,0 +1,9 @@ +# Required Hardware + +- [ESP32-C3-32S](https://docs.ai-thinker.com/_media/esp32/docs/esp-c3-32s-kit-v1.0_specification.pdf) +- [Waveshare SN65HVD230 CAN Board](https://www.waveshare.com/sn65hvd230-can-board.htm) + +# Setup + +1. download and install `esp-idf` +2. connect CAN board to ESP32 \ No newline at end of file diff --git a/examples/esp32_server/main/CMakeLists.txt b/examples/esp32_server/main/CMakeLists.txt new file mode 100644 index 0000000..6f7ca4c --- /dev/null +++ b/examples/esp32_server/main/CMakeLists.txt @@ -0,0 +1,6 @@ +idf_component_register( + SRCS "main.c" + "iso14229.c" + "iso14229.h" + INCLUDE_DIRS "." +) \ No newline at end of file diff --git a/examples/esp32_server/main/iso14229.c b/examples/esp32_server/main/iso14229.c new file mode 120000 index 0000000..fa2cd67 --- /dev/null +++ b/examples/esp32_server/main/iso14229.c @@ -0,0 +1 @@ +../../../bazel-bin/iso14229.c \ No newline at end of file diff --git a/examples/esp32_server/main/iso14229.h b/examples/esp32_server/main/iso14229.h new file mode 120000 index 0000000..c026bd1 --- /dev/null +++ b/examples/esp32_server/main/iso14229.h @@ -0,0 +1 @@ +../../../bazel-bin/iso14229.h \ No newline at end of file diff --git a/examples/esp32_server/main/main.c b/examples/esp32_server/main/main.c new file mode 100644 index 0000000..feb2dbd --- /dev/null +++ b/examples/esp32_server/main/main.c @@ -0,0 +1,113 @@ +#include "driver/gpio.h" +#include "iso14229.h" +#include +#include + +#define CAN_RX_PIN GPIO_NUM_7 +#define CAN_TX_PIN GPIO_NUM_6 +#define RED_LED_PIN GPIO_NUM_3 +#define GREEN_LED_PIN GPIO_NUM_4 +#define BLUE_LED_PIN GPIO_NUM_5 + +const char *TAG = "UDS"; + +static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS(); +static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); +static const twai_general_config_t g_config = {.mode = TWAI_MODE_NORMAL, + .tx_io = CAN_TX_PIN, + .rx_io = CAN_RX_PIN, + .clkout_io = TWAI_IO_UNUSED, + .bus_off_io = TWAI_IO_UNUSED, + .tx_queue_len = 50, + .rx_queue_len = 50, + .alerts_enabled = + TWAI_ALERT_RX_DATA | TWAI_ALERT_BUS_OFF, + .clkout_divider = 0, + .intr_flags = ESP_INTR_FLAG_LEVEL1}; + +static UDSServer_t srv; +static UDSISOTpC_t tp; + +static int send_can(const uint32_t arbitration_id, const uint8_t *data, const uint8_t size, + void *user_data) { + twai_message_t tx_msg; + tx_msg.identifier = arbitration_id; + tx_msg.data_length_code = size; + memmove(tx_msg.data, data, size); + if (ESP_OK == twai_transmit(&tx_msg, 0)) { + return size; + } else { + return -1; + } +} + +static const UDSISOTpCConfig_t tp_cfg = { + .source_addr=0x7E8, + .target_addr=0x7E0, + .source_addr_func=0x7DF, + .target_addr_func=UDS_TP_NOOP_ADDR, + .isotp_user_send_can=send_can, + .isotp_user_get_ms=UDSMillis, + .isotp_user_debug=NULL, + .user_data=NULL, +}; + +static uint8_t fn(UDSServer_t *srv, int evt, const void *data) { + ESP_LOGI(TAG, "received event %d", evt); + switch (evt) { + case UDS_SRV_EVT_WriteDataByIdent: { + UDSWDBIArgs_t *r = (UDSWDBIArgs_t *)data; + switch (r->dataId) { + case 0x0001: + ESP_LOGI(TAG, "received 0x0001"); + gpio_set_level(RED_LED_PIN, r->data[0] & 0x01); + gpio_set_level(GREEN_LED_PIN, r->data[0] & 0x02); + gpio_set_level(BLUE_LED_PIN, r->data[0] & 0x04); + break; + default: + ESP_LOGI(TAG, "received unknown data id 0x%04x", r->dataId); + break; + } + } + } + return 0; +} + +void app_main(void) { + ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); + ESP_ERROR_CHECK(twai_start()); + + gpio_config_t io_conf; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (1ULL << RED_LED_PIN) | (1ULL << GREEN_LED_PIN) | (1ULL << BLUE_LED_PIN); + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + gpio_config(&io_conf); + + + ESP_ERROR_CHECK(UDSServerInit(&srv)); + ESP_ERROR_CHECK(UDSISOTpCInit(&tp, &tp_cfg)); + srv.fn = fn; + srv.tp = &tp.hdl; + + for (;;) { + twai_message_t rx_msg; + if (twai_receive(&rx_msg, 0) == ESP_OK) { + if (rx_msg.identifier == tp.phys_sa) { + isotp_on_can_message(&tp.phys_link, rx_msg.data, rx_msg.data_length_code); + } else if (rx_msg.identifier == tp.func_sa) { + if (ISOTP_RECEIVE_STATUS_IDLE != tp.phys_link.receive_status) { + ESP_LOGI(TAG, "func frame received but cannot process because link is not idle"); + continue; + } + isotp_on_can_message(&tp.func_link, rx_msg.data, rx_msg.data_length_code); + } else { + ESP_LOGI(TAG, "received unknown can id 0x%03lx", rx_msg.identifier); + } + } + + UDSServerPoll(&srv); + } + +} \ No newline at end of file diff --git a/examples/isotp-c_on_socketcan.c b/examples/isotp-c_on_socketcan.c deleted file mode 100644 index e65e903..0000000 --- a/examples/isotp-c_on_socketcan.c +++ /dev/null @@ -1,90 +0,0 @@ -#include "../iso14229.h" -#include "../isotp-c/isotp.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static int sockfd = 0; -static bool HasSetup = false; - -static void SetupOnce() { - if (HasSetup) { - return; - } - UDS_DBG_PRINT("setting up CAN\n"); - struct sockaddr_can addr; - struct ifreq ifr; - if ((sockfd = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)) < 0) { - perror("socket"); - exit(-1); - } - strcpy(ifr.ifr_name, "vcan0"); - ioctl(sockfd, SIOCGIFINDEX, &ifr); - memset(&addr, 0, sizeof(addr)); - addr.can_family = AF_CAN; - addr.can_ifindex = ifr.ifr_ifindex; - if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - perror("bind"); - exit(-1); - } - HasSetup = true; -} - -uint32_t isotp_user_get_us() { return UDSMillis() * 1000; } - -void isotp_user_debug(const char *message, ...) {} - -int isotp_user_send_can(const uint32_t arbitration_id, const uint8_t *data, const uint8_t size) { - SetupOnce(); - struct can_frame frame = {0}; - frame.can_id = arbitration_id; - frame.can_dlc = size; - memmove(frame.data, data, size); - if (write(sockfd, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) { - perror("Write err"); - return ISOTP_RET_ERROR; - } - return ISOTP_RET_OK; -} - -static void printhex(const uint8_t *addr, int len) { - for (int i = 0; i < len; i++) { - printf("%02x,", addr[i]); - } - printf("\n"); -} - -void SocketCANRecv(UDSTpIsoTpC_t *tp, int phys_recv_id, int func_recv_id) { - assert(tp); - SetupOnce(); - struct can_frame frame = {0}; - int nbytes = 0; - for (;;) { - nbytes = read(sockfd, &frame, sizeof(struct can_frame)); - if (nbytes < 0) { - if (EAGAIN == errno || EWOULDBLOCK == errno) { - break; - } else { - perror("read"); - } - } else if (nbytes == 0) { - break; - } else { - if (frame.can_id == phys_recv_id) { - UDS_DBG_PRINT("phys recvd can\n"); - UDS_DBG_PRINTHEX(frame.data, frame.can_dlc); - isotp_on_can_message(&tp->phys_link, frame.data, frame.can_dlc); - } else if (frame.can_id == func_recv_id) { - UDS_DBG_PRINT("func recvd can\n"); - UDS_DBG_PRINTHEX(frame.data, frame.can_dlc); - isotp_on_can_message(&tp->func_link, frame.data, frame.can_dlc); - } - } - } -} \ No newline at end of file diff --git a/examples/isotp-c_on_socketcan.h b/examples/isotp-c_on_socketcan.h deleted file mode 100644 index 68f9264..0000000 --- a/examples/isotp-c_on_socketcan.h +++ /dev/null @@ -1,3 +0,0 @@ -#include "../iso14229.h" - -void SocketCANRecv(UDSTpIsoTpC_t *tp, unsigned int recv_id); \ No newline at end of file diff --git a/examples/linux_server/Makefile b/examples/linux_server/Makefile new file mode 100644 index 0000000..10db91c --- /dev/null +++ b/examples/linux_server/Makefile @@ -0,0 +1,10 @@ +SRCS += iso14229.c main.c +HDRS += iso14229.h +TARGET = server +CFLAGS = -DUDS_TP_ISOTP_SOCK=1 -g + +all: $(SRCS) $(HDRS) + $(CC) $(CFLAGS) $(SRCS) -o $(TARGET) + +clean: + rm -f $(TARGET) diff --git a/examples/linux_server/main.c b/examples/linux_server/main.c new file mode 100644 index 0000000..c8182f4 --- /dev/null +++ b/examples/linux_server/main.c @@ -0,0 +1,63 @@ +#include "iso14229.h" +#include +#include +#include +#include +#include +#include +#include + +static UDSServer_t srv; +static UDSTpIsoTpSock_t tp; +static bool done = false; + +void sigint_handler(int signum) { + printf("SIGINT received\n"); + done = true; +} + +static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + switch (ev) { + default: + printf("Unhandled event: %d\n", ev); + return kServiceNotSupported; + } +} + +static int sleep_ms(uint32_t tms) { + struct timespec ts; + int ret; + ts.tv_sec = tms / 1000; + ts.tv_nsec = (tms % 1000) * 1000000; + do { + ret = nanosleep(&ts, &ts); + } while (ret && errno == EINTR); + return ret; +} + +int main(int ac, char **av) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigint_handler; + sigaction(SIGINT, &sa, NULL); + + if (UDSTpIsoTpSockInitServer(&tp, "vcan0", 0x7E0, 0x7E8, 0x7DF)) { + fprintf(stderr, "UDSTpIsoTpSockInitServer failed\n"); + exit(-1); + } + + if (UDSServerInit(&srv)) { + fprintf(stderr, "UDSServerInit failed\n"); + } + + srv.tp = (UDSTpHandle_t *)&tp; + srv.fn = fn; + + printf("server up, polling . . .\n"); + while (!done) { + UDSServerPoll(&srv); + sleep_ms(1); + } + printf("server exiting\n"); + return 0; +} diff --git a/examples/linux_server_0x27/Makefile b/examples/linux_server_0x27/Makefile new file mode 100644 index 0000000..35fb955 --- /dev/null +++ b/examples/linux_server_0x27/Makefile @@ -0,0 +1,24 @@ +SRCS += iso14229.c +HDRS += iso14229.h +TARGETS = server client +CFLAGS = -DUDS_TP_ISOTP_SOCK -DUDS_LINES -g +MBEDTLS ?= /usr/local +LDFLAGS = -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509 + + +all: $(TARGETS) private_key.pem public_key.pem + +server: $(SRCS) $(HDRS) server.c Makefile + $(CC) $(CFLAGS) $(SRCS) server.c $(LDFLAGS) -o server + +client: $(SRCS) $(HDRS) client.c Makefile + $(CC) $(CFLAGS) $(SRCS) client.c $(LDFLAGS) -o client + +private_key.pem: + openssl genrsa -out private_key.pem 4096 + +public_key.pem: private_key.pem + openssl rsa -in private_key.pem -pubout -outform PEM -out public_key.pem + +clean: + rm -f $(TARGETS) private_key.pem public_key.pem diff --git a/examples/linux_server_0x27/README.md b/examples/linux_server_0x27/README.md new file mode 100644 index 0000000..c8d9ece --- /dev/null +++ b/examples/linux_server_0x27/README.md @@ -0,0 +1,17 @@ +# Running the example + +```sh +apt install libmbedtls-dev +sudo ip link add name vcan0 type vcan +sudo ip link set vcan0 up + +make + +./server + +./client +``` + +# Acknowledgement + +This example is based on Martin Thompson's paper "UDS Security Access for Constraint ECUs" https://doi.org/10.4271/2022-01-0132 \ No newline at end of file diff --git a/examples/linux_server_0x27/client.c b/examples/linux_server_0x27/client.c new file mode 100644 index 0000000..d3e8eed --- /dev/null +++ b/examples/linux_server_0x27/client.c @@ -0,0 +1,160 @@ +#include "iso14229.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static int SleepMillis(uint32_t tms) { + struct timespec ts; + int ret; + ts.tv_sec = tms / 1000; + ts.tv_nsec = (tms % 1000) * 1000000; + do { + ret = nanosleep(&ts, &ts); + } while (ret && errno == EINTR); + return ret; +} + +int sign(const uint8_t *seed, size_t seed_len, uint8_t* key, size_t key_len) { + int ret = 0; + mbedtls_pk_context pk; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + const char *pers = "rsa_sign"; + mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)pers, strlen(pers)); + + mbedtls_pk_init(&pk); + + const char *private_key_pem = "private_key.pem"; + + if (mbedtls_pk_parse_keyfile(&pk, private_key_pem, NULL) != 0) { + mbedtls_printf("Failed to parse private key\n"); + ret = -1; + goto exit; + } + + // Verify that the loaded key is an RSA key + if (mbedtls_pk_get_type(&pk) != MBEDTLS_PK_RSA) { + mbedtls_printf("Loaded key is not an RSA key\n"); + ret = -1; + goto exit; + } + + mbedtls_rsa_context *rsa = mbedtls_pk_rsa(pk); + + // // Allocate buffer for the signature + size_t sig_len = mbedtls_rsa_get_len(rsa); + if (sig_len != key_len) { + fprintf(stderr, "sig_len: %ld != %ld\n", sig_len, sizeof(key)); + ret = -1; + goto exit; + } + + // Perform RSA signing operation + if (mbedtls_rsa_rsassa_pkcs1_v15_sign(rsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, + MBEDTLS_MD_SHA256, seed_len, seed, key) != 0) { + mbedtls_printf("Failed to sign data\n"); + ret = -1; + goto exit; + } + + // 'key' now contains the RSA signature + + exit: + mbedtls_rsa_free(rsa); + mbedtls_ctr_drbg_free(&ctr_drbg); + mbedtls_entropy_free(&entropy); + return ret; +} + + +int main(int ac, char **av) { + UDSClient_t client; + UDSTpIsoTpSock_t tp; + UDSErr_t err = 0; + + if (UDSTpIsoTpSockInitClient(&tp, "vcan0", 0x7E8, 0x7E0, 0x7DF)) { + fprintf(stderr, "UDSTpIsoTpSockInitClient failed\n"); + exit(-1); + } + + if (UDSClientInit(&client)) { + exit(-1); + } + + client.tp = (UDSTpHandle_t *)&tp; + + // Request seed + err = UDSSendSecurityAccess(&client, 3, NULL, 0); + if (err) { + fprintf(stderr, "UDSSendSecurityAccess failed: %d\n", err); + exit(-1); + } + + while (UDSClientPoll(&client)); + + if (client.err) { + fprintf(stderr, "UDSSendSecurityAccess failed: %d\n", client.err); + exit(-1); + } + + struct SecurityAccessResponse sar = {0}; + err = UDSUnpackSecurityAccessResponse(&client, &sar); + if (err) { + fprintf(stderr, "UDSUnpackSecurityAccessResponse failed: %d\n", err); + exit(-1); + } + + printf("seed: "); + for (int i = 0; i < sar.securitySeedLength; i++) { + printf("%02X ", sar.securitySeed[i]); + } + + // Check if all bytes in the seed are 0 + bool all_zero = true; + for (int i = 0; i < sar.securitySeedLength; i++) { + if (sar.securitySeed[i] != 0) { + all_zero = false; + break; + } + } + + if (all_zero) { + fprintf(stderr, "seed is all zero, already unlocked\n"); + return 0; + } + + + uint8_t key[512] = {0}; + + if (sign(sar.securitySeed, sar.securitySeedLength, key, sizeof(key))) { + fprintf(stderr, "sign failed\n"); + exit(-1); + } + + + err = UDSSendSecurityAccess(&client, 4, key, sizeof(key)); + if (err) { + fprintf(stderr, "UDSSendSecurityAccess failed: %d\n", err); + exit(-1); + } + + while (UDSClientPoll(&client)); + + + return 0; +} diff --git a/examples/linux_server_0x27/server.c b/examples/linux_server_0x27/server.c new file mode 100644 index 0000000..dd52e91 --- /dev/null +++ b/examples/linux_server_0x27/server.c @@ -0,0 +1,128 @@ +#include "iso14229.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static UDSServer_t srv; +static UDSTpIsoTpSock_t tp; +static bool done = false; +static uint8_t seed[32] = {0}; + +void sigint_handler(int signum) { + printf("SIGINT received\n"); + done = true; +} + +int rsa_verify(const uint8_t *key, size_t key_len, bool *valid) { + int ret = 0; + mbedtls_pk_context pk; + const char *pubkey = "public_key.pem"; + mbedtls_pk_init(&pk); + + if ((ret = mbedtls_pk_parse_public_keyfile(&pk, pubkey) != 0)) { + mbedtls_printf(" failed\n ! Could not read key from '%s'\n", pubkey); + mbedtls_printf(" ! mbedtls_pk_parse_public_keyfile returned %d\n\n", ret); + goto exit; + } + + if ((ret = mbedtls_pk_verify(&pk, MBEDTLS_MD_SHA256, seed, sizeof(seed), + key, key_len)) != 0) { + mbedtls_printf(" failed\n ! mbedtls_pk_verify returned %d\n\n", ret); + goto exit; + } + + exit: + *valid = (ret == 0); + // print the mbedtls error code + if (ret != 0) { + char buf[128]; + mbedtls_strerror(ret, buf, sizeof(buf)); + printf("mbedtls error: %s\n", buf); + } + return ret; +} + +static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + switch (ev) { + case UDS_SRV_EVT_SecAccessRequestSeed: { + UDSSecAccessRequestSeedArgs_t *req = (UDSSecAccessRequestSeedArgs_t *)arg; + // use urandom to generate a random seed + FILE *f = fopen("/dev/urandom", "r"); + if (!f) { + fprintf(stderr, "Failed to open /dev/urandom\n"); + return kGeneralReject; + } + fread(seed, sizeof(seed), 1, f); + fclose(f); + return req->copySeed(srv, seed, sizeof(seed)); + } + case UDS_SRV_EVT_SecAccessValidateKey: { + UDSSecAccessValidateKeyArgs_t *req = (UDSSecAccessValidateKeyArgs_t *)arg; + bool valid = false; + + if (0 != rsa_verify(req->key, req->len, &valid)) { + printf("rsa_verify failed\n"); + return kGeneralReject; + } else { + if (valid) { + printf("Security level %d unlocked\n", req->level); + return kPositiveResponse; + } else { + return kSecurityAccessDenied; + } + } + } + default: + printf("Unhandled event: %d\n", ev); + return kServiceNotSupported; + } +} + +static int sleep_ms(uint32_t tms) { + struct timespec ts; + int ret; + ts.tv_sec = tms / 1000; + ts.tv_nsec = (tms % 1000) * 1000000; + do { + ret = nanosleep(&ts, &ts); + } while (ret && errno == EINTR); + return ret; +} + +int main(int ac, char **av) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigint_handler; + sigaction(SIGINT, &sa, NULL); + + if (UDSTpIsoTpSockInitServer(&tp, "vcan0", 0x7E0, 0x7E8, 0x7DF)) { + fprintf(stderr, "UDSTpIsoTpSockInitServer failed\n"); + exit(-1); + } + + if (UDSServerInit(&srv)) { + fprintf(stderr, "UDSServerInit failed\n"); + } + + srv.tp = (UDSTpHandle_t *)&tp; + srv.fn = fn; + + printf("server up, polling . . .\n"); + while (!done) { + UDSServerPoll(&srv); + sleep_ms(1); + } + printf("server exiting\n"); + return 0; +} diff --git a/examples/requirements.txt b/examples/requirements.txt deleted file mode 100644 index b852065..0000000 --- a/examples/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -python-can==4.0.0 -can-isotp==1.7 -udsoncan==1.14 \ No newline at end of file diff --git a/examples/s32k144_server/.gdbinit b/examples/s32k144_server/.gdbinit new file mode 100644 index 0000000..bc865f1 --- /dev/null +++ b/examples/s32k144_server/.gdbinit @@ -0,0 +1,7 @@ +target remote localhost:3333 +set architecture armv7e-m +# file bazel-bin/examples/s32k144/main + +define dumpflash + dump binary memory dump.bin 0x0 0x80000 +end \ No newline at end of file diff --git a/examples/s32k144_server/BUILD b/examples/s32k144_server/BUILD new file mode 100644 index 0000000..a407dd5 --- /dev/null +++ b/examples/s32k144_server/BUILD @@ -0,0 +1,233 @@ +load("//toolchain:objcopy.bzl", "objcopy") + +cc_binary( + name = "main", + srcs = [ + "main.c", + "bsp.c", + "bsp.h", + "//:srcs", + ], + deps = [ + ":runtime", + ], + defines = [ + "UDS_CUSTOM_MILLIS", + ] +) + +objcopy( + src = "main", + out = "main.hex", + args = "-O ihex", +) + +cc_library( + name = "runtime", + srcs = [ + "@s32k_sdk//:platform/devices/S32K144/startup/system_S32K144.c", + "@s32k_sdk//:platform/devices/S32K144/startup/system_S32K144.h", + "@s32k_sdk//:platform/devices/S32K144/include/S32K144.h", + "@s32k_sdk//:platform/devices/S32K144/include/S32K144_features.h", + "@s32k_sdk//:platform/devices/callbacks.h", + "@s32k_sdk//:platform/devices/device_registers.h", + "@s32k_sdk//:platform/devices/devassert.h", + "@s32k_sdk//:platform/devices/startup.h", + "@s32k_sdk//:platform/devices/startup.c", + "@s32k_sdk//:platform/devices/status.h", + "@s32k_sdk//:platform/devices/common/s32_core_cm4.h", + "@s32k_sdk//:rtos/osif/osif.h", + "@s32k_sdk//:rtos/osif/osif_baremetal.c", + + "@s32k_sdk//:platform/drivers/inc/adc_driver.h", + "@s32k_sdk//:platform/drivers/inc/clock.h", + "@s32k_sdk//:platform/drivers/inc/clock_manager.h", + "@s32k_sdk//:platform/drivers/inc/cmp_driver.h", + "@s32k_sdk//:platform/drivers/inc/crc_driver.h", + "@s32k_sdk//:platform/drivers/inc/csec_driver.h", + "@s32k_sdk//:platform/drivers/inc/edma_driver.h", + "@s32k_sdk//:platform/drivers/inc/eim_driver.h", + # "@s32k_sdk//:platform/drivers/inc/enet_driver.h", + "@s32k_sdk//:platform/drivers/inc/erm_driver.h", + "@s32k_sdk//:platform/drivers/inc/ewm_driver.h", + "@s32k_sdk//:platform/drivers/inc/flash_driver.h", + # "@s32k_sdk//:platform/drivers/inc/flash_mx25l6433f_driver.h", + "@s32k_sdk//:platform/drivers/inc/flexcan_driver.h", + "@s32k_sdk//:platform/drivers/inc/flexio.h", + "@s32k_sdk//:platform/drivers/inc/flexio_i2c_driver.h", + "@s32k_sdk//:platform/drivers/inc/flexio_i2s_driver.h", + "@s32k_sdk//:platform/drivers/inc/flexio_spi_driver.h", + "@s32k_sdk//:platform/drivers/inc/flexio_uart_driver.h", + "@s32k_sdk//:platform/drivers/inc/ftm_common.h", + "@s32k_sdk//:platform/drivers/inc/ftm_ic_driver.h", + "@s32k_sdk//:platform/drivers/inc/ftm_mc_driver.h", + "@s32k_sdk//:platform/drivers/inc/ftm_oc_driver.h", + "@s32k_sdk//:platform/drivers/inc/ftm_pwm_driver.h", + "@s32k_sdk//:platform/drivers/inc/ftm_qd_driver.h", + "@s32k_sdk//:platform/drivers/inc/interrupt_manager.h", + "@s32k_sdk//:platform/drivers/inc/lin_driver.h", + "@s32k_sdk//:platform/drivers/inc/lpi2c_driver.h", + "@s32k_sdk//:platform/drivers/inc/lpit_driver.h", + "@s32k_sdk//:platform/drivers/inc/lpspi_master_driver.h", + "@s32k_sdk//:platform/drivers/inc/lpspi_shared_function.h", + "@s32k_sdk//:platform/drivers/inc/lpspi_slave_driver.h", + "@s32k_sdk//:platform/drivers/inc/lptmr_driver.h", + "@s32k_sdk//:platform/drivers/inc/lpuart_driver.h", + "@s32k_sdk//:platform/drivers/inc/mpu_driver.h", + "@s32k_sdk//:platform/drivers/inc/pdb_driver.h", + "@s32k_sdk//:platform/drivers/inc/pins_driver.h", + "@s32k_sdk//:platform/drivers/inc/power_manager.h", + # "@s32k_sdk//:platform/drivers/inc/quadspi_driver.h", + "@s32k_sdk//:platform/drivers/inc/rtc_driver.h", + # "@s32k_sdk//:platform/drivers/inc/sai_driver.h", + "@s32k_sdk//:platform/drivers/inc/trgmux_driver.h", + "@s32k_sdk//:platform/drivers/inc/wdog_driver.h", + + # s32k144 has no phy + # "@s32k_sdk//:platform/drivers/src/phy/phy_enet_access.c", + # "@s32k_sdk//:platform/drivers/src/phy/phy_generic.c", + # "@s32k_sdk//:platform/drivers/src/phy/phy_shared.h", + # "@s32k_sdk//:platform/drivers/src/phy/phy_hw_access.h", + # "@s32k_sdk//:platform/drivers/src/phy/phy_tja110x.c", + # "@s32k_sdk//:platform/drivers/src/phy/phy.c", + # "@s32k_sdk//:platform/drivers/src/phy/phy_shared.c", + + "@s32k_sdk//:platform/drivers/src/lpuart/lpuart_irq.c", + "@s32k_sdk//:platform/drivers/src/lpuart/lpuart_hw_access.c", + "@s32k_sdk//:platform/drivers/src/lpuart/lin_lpuart_driver.h", + "@s32k_sdk//:platform/drivers/src/lpuart/lpuart_irq.h", + "@s32k_sdk//:platform/drivers/src/lpuart/lpuart_hw_access.h", + "@s32k_sdk//:platform/drivers/src/lpuart/lpuart_driver.c", + "@s32k_sdk//:platform/drivers/src/lpuart/lin_lpuart_driver.c", + "@s32k_sdk//:platform/drivers/src/clock/S32K1xx/clock_S32K1xx.c", + "@s32k_sdk//:platform/drivers/src/clock/S32K1xx/scg_hw_access.h", + "@s32k_sdk//:platform/drivers/src/clock/S32K1xx/smc_hw_access.h", + "@s32k_sdk//:platform/drivers/src/clock/S32K1xx/pmc_hw_access.h", + "@s32k_sdk//:platform/drivers/src/clock/S32K1xx/pcc_hw_access.h", + "@s32k_sdk//:platform/drivers/src/clock/S32K1xx/clock_S32K1xx.h", + "@s32k_sdk//:platform/drivers/src/clock/S32K1xx/sim_hw_access.h", + "@s32k_sdk//:platform/drivers/src/flexcan/flexcan_irq.c", + "@s32k_sdk//:platform/drivers/src/flexcan/flexcan_irq.h", + "@s32k_sdk//:platform/drivers/src/flexcan/flexcan_hw_access.h", + "@s32k_sdk//:platform/drivers/src/flexcan/flexcan_hw_access.c", + "@s32k_sdk//:platform/drivers/src/flexcan/flexcan_driver.c", + "@s32k_sdk//:platform/drivers/src/flexio/flexio_hw_access.h", + "@s32k_sdk//:platform/drivers/src/flexio/flexio_spi_driver.c", + "@s32k_sdk//:platform/drivers/src/flexio/flexio_common.c", + "@s32k_sdk//:platform/drivers/src/flexio/flexio_uart_driver.c", + "@s32k_sdk//:platform/drivers/src/flexio/flexio_i2s_driver.c", + "@s32k_sdk//:platform/drivers/src/flexio/flexio_common.h", + "@s32k_sdk//:platform/drivers/src/flexio/flexio_i2c_driver.c", + # "@s32k_sdk//:platform/drivers/src/sai/sai_driver.c", + # "@s32k_sdk//:platform/drivers/src/sai/sai_hw_access.h", + "@s32k_sdk//:platform/drivers/src/interrupt/interrupt_manager.c", + "@s32k_sdk//:platform/drivers/src/wdog/wdog_hw_access.h", + "@s32k_sdk//:platform/drivers/src/wdog/wdog_hw_access.c", + "@s32k_sdk//:platform/drivers/src/wdog/wdog_driver.c", + # "@s32k_sdk//:platform/drivers/src/enet/enet_irq.c", + # "@s32k_sdk//:platform/drivers/src/enet/enet_driver.c", + # "@s32k_sdk//:platform/drivers/src/enet/enet_hw_access.h", + # "@s32k_sdk//:platform/drivers/src/enet/enet_hw_access.c", + "@s32k_sdk//:platform/drivers/src/csec/csec_hw_access.h", + "@s32k_sdk//:platform/drivers/src/csec/csec_driver.c", + "@s32k_sdk//:platform/drivers/src/csec/csec_hw_access.c", + "@s32k_sdk//:platform/drivers/src/ewm/ewm_hw_access.h", + "@s32k_sdk//:platform/drivers/src/ewm/ewm_driver.c", + "@s32k_sdk//:platform/drivers/src/pdb/pdb_driver.c", + "@s32k_sdk//:platform/drivers/src/pdb/pdb_hw_access.c", + "@s32k_sdk//:platform/drivers/src/pdb/pdb_hw_access.h", + "@s32k_sdk//:platform/drivers/src/rtc/rtc_driver.c", + "@s32k_sdk//:platform/drivers/src/rtc/rtc_irq.c", + "@s32k_sdk//:platform/drivers/src/rtc/rtc_hw_access.c", + "@s32k_sdk//:platform/drivers/src/rtc/rtc_hw_access.h", + "@s32k_sdk//:platform/drivers/src/cmp/cmp_driver.c", + "@s32k_sdk//:platform/drivers/src/cmp/cmp_hw_access.c", + "@s32k_sdk//:platform/drivers/src/cmp/cmp_hw_access.h", + # "@s32k_sdk//:platform/drivers/src/quadspi/quadspi_hw_access.h", + # "@s32k_sdk//:platform/drivers/src/quadspi/quadspi_driver.c", + "@s32k_sdk//:platform/drivers/src/lpspi/lpspi_master_driver.c", + "@s32k_sdk//:platform/drivers/src/lpspi/lpspi_shared_function.c", + "@s32k_sdk//:platform/drivers/src/lpspi/lpspi_irq.c", + "@s32k_sdk//:platform/drivers/src/lpspi/lpspi_hw_access.h", + "@s32k_sdk//:platform/drivers/src/lpspi/lpspi_hw_access.c", + "@s32k_sdk//:platform/drivers/src/lpspi/lpspi_slave_driver.c", + "@s32k_sdk//:platform/drivers/src/lpit/lpit_driver.c", + "@s32k_sdk//:platform/drivers/src/lpit/lpit_hw_access.h", + "@s32k_sdk//:platform/drivers/src/eim/eim_hw_access.c", + "@s32k_sdk//:platform/drivers/src/eim/eim_hw_access.h", + "@s32k_sdk//:platform/drivers/src/eim/eim_driver.c", + "@s32k_sdk//:platform/drivers/src/lptmr/lptmr_hw_access.h", + "@s32k_sdk//:platform/drivers/src/lptmr/lptmr_hw_access.c", + "@s32k_sdk//:platform/drivers/src/lptmr/lptmr_driver.c", + "@s32k_sdk//:platform/drivers/src/flash/flash_driver.c", + # "@s32k_sdk//:platform/drivers/src/flash_mx25l6433f/flash_mx25l6433f_regs.h", + # "@s32k_sdk//:platform/drivers/src/flash_mx25l6433f/flash_mx25l6433f_driver.c", + "@s32k_sdk//:platform/drivers/src/edma/edma_irq.c", + "@s32k_sdk//:platform/drivers/src/edma/edma_irq.h", + "@s32k_sdk//:platform/drivers/src/edma/edma_hw_access.h", + "@s32k_sdk//:platform/drivers/src/edma/edma_driver.c", + "@s32k_sdk//:platform/drivers/src/edma/edma_hw_access.c", + "@s32k_sdk//:platform/drivers/src/crc/crc_hw_access.c", + "@s32k_sdk//:platform/drivers/src/crc/crc_driver.c", + "@s32k_sdk//:platform/drivers/src/crc/crc_hw_access.h", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_qd_driver.c", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_mc_driver.c", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_hw_access.h", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_ic_driver.c", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_common.c", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_pwm_driver.c", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_hw_access.c", + "@s32k_sdk//:platform/drivers/src/ftm/ftm_oc_driver.c", + "@s32k_sdk//:platform/drivers/src/mpu/mpu_driver.c", + "@s32k_sdk//:platform/drivers/src/mpu/mpu_hw_access.h", + "@s32k_sdk//:platform/drivers/src/mpu/mpu_hw_access.c", + "@s32k_sdk//:platform/drivers/src/lpi2c/lpi2c_irq.h", + "@s32k_sdk//:platform/drivers/src/lpi2c/lpi2c_hw_access.c", + "@s32k_sdk//:platform/drivers/src/lpi2c/lpi2c_irq.c", + "@s32k_sdk//:platform/drivers/src/lpi2c/lpi2c_hw_access.h", + "@s32k_sdk//:platform/drivers/src/lpi2c/lpi2c_driver.c", + "@s32k_sdk//:platform/drivers/src/erm/erm_driver.c", + "@s32k_sdk//:platform/drivers/src/erm/erm_hw_access.h", + "@s32k_sdk//:platform/drivers/src/erm/erm_hw_access.c", + "@s32k_sdk//:platform/drivers/src/power/S32K1xx/power_scg_hw_access.h", + "@s32k_sdk//:platform/drivers/src/power/S32K1xx/power_manager_S32K1xx.c", + "@s32k_sdk//:platform/drivers/src/power/S32K1xx/power_smc_hw_access.h", + "@s32k_sdk//:platform/drivers/src/power/S32K1xx/power_rcm_hw_access.h", + "@s32k_sdk//:platform/drivers/src/power/S32K1xx/power_manager_S32K1xx.h", + "@s32k_sdk//:platform/drivers/src/power/S32K1xx/power_smc_hw_access.c", + "@s32k_sdk//:platform/drivers/src/power/power_manager.c", + "@s32k_sdk//:platform/drivers/src/lin/lin_irq.c", + "@s32k_sdk//:platform/drivers/src/lin/lin_common.c", + "@s32k_sdk//:platform/drivers/src/lin/lin_driver.c", + "@s32k_sdk//:platform/drivers/src/adc/adc_hw_access.h", + "@s32k_sdk//:platform/drivers/src/adc/adc_driver.c", + "@s32k_sdk//:platform/drivers/src/trgmux/trgmux_hw_access.h", + "@s32k_sdk//:platform/drivers/src/trgmux/trgmux_hw_access.c", + "@s32k_sdk//:platform/drivers/src/trgmux/trgmux_driver.c", + "@s32k_sdk//:platform/drivers/src/pins/pins_driver.c", + "@s32k_sdk//:platform/drivers/src/pins/pins_gpio_hw_access.h", + "@s32k_sdk//:platform/drivers/src/pins/pins_port_hw_access.h", + "@s32k_sdk//:platform/drivers/src/pins/pins_port_hw_access.c", + + + "@s32k_sdk//:platform/devices/S32K144/startup/gcc/startup_S32K144.S", + ], + deps = [ + "@s32k_sdk//:platform/devices/S32K144/linker/gcc/S32K144_64_flash.ld", + "@s32k_sdk//:include", + "@CMSIS//:include", + ], + linkopts = [ + "-T $(location @s32k_sdk//:platform/devices/S32K144/linker/gcc/S32K144_64_flash.ld)", + "-lc", + "-lnosys", + "-Wl,--defsym=__Vectors=__isr_vector", + # force the symbol CAN0_Error_IRQHandler to be included + "-Wl,--undefined=CAN0_Error_IRQHandler", + ], + defines = [ + "CPU_S32K144HFT0VLLT", + "DEV_ERROR_DETECT=1U", + ], + target_compatible_with = ["//platforms:s32k"], +) \ No newline at end of file diff --git a/examples/s32k144_server/README.md b/examples/s32k144_server/README.md new file mode 100644 index 0000000..808e2a0 --- /dev/null +++ b/examples/s32k144_server/README.md @@ -0,0 +1,35 @@ +I'm so sorry that you too must futz with Freescale / NXP's byzantine drivers! + +This demo uses the [S32K144-EVB](https://www.keil.arm.com/boards/nxp-s32k144-evb-rev-a-ee981eb/features/) + + +# Building + +This uses `gcc-arm-none-eabi`, make sure it's installed first. + +```sh +sudo apt install gcc-arm-none-eabi +bazel build --config=s32k //examples/s32k144:main +``` + + +# CMSIS-DAP + +1. update programming interface from "OpenSDA" to CMSIS-DAP [1](https://community.nxp.com/t5/S32K/S32K144evb-with-CMSIS-DAP/td-p/1227900) [2](https://developer.arm.com/documentation/kan299/latest/) +2. install `pyocd` +3. `pyocd pack install S32K144UAxxxLLx` +3. update `/etc/udev/rules.d/50-cmsis-dap.rules` with the following: + +``` +# c251:f002 S32K144-EVB +SUBSYSTEM=="usb", ATTR{idVendor}=="c251", ATTR{idProduct}=="f002", MODE:="666" +``` + +``` +pyocd gdbserver --persist -Otarget_override=S32K144UAxxxLLx +``` + +# Physical Setup + +- The S32K144-EVB needs an external 12V supply to power the CAN transciever + diff --git a/examples/s32k144_server/bsp.c b/examples/s32k144_server/bsp.c new file mode 100644 index 0000000..ed888e3 --- /dev/null +++ b/examples/s32k144_server/bsp.c @@ -0,0 +1,685 @@ +#include "pins_driver.h" +#include "clock.h" +#include "edma_driver.h" +#include "flexcan_driver.h" +#include "system_S32K144.h" + +/*! @brief User number of configured pins */ +#define NUM_OF_CONFIGURED_PINS 12 + +/*! @brief User configuration structure */ +const static pin_settings_config_t g_pin_mux_InitConfigArr[NUM_OF_CONFIGURED_PINS] = { + { + .base = PORTD, + .pinPortIdx = 0u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTD, + .direction = GPIO_OUTPUT_DIRECTION, + .digitalFilter = false, + .initValue = 0u, + }, + { + .base = PORTE, + .pinPortIdx = 5u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_ALT5, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = NULL, + .digitalFilter = false, + }, + { + .base = PORTE, + .pinPortIdx = 4u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_ALT5, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = NULL, + .digitalFilter = false, + }, + { + .base = PORTD, + .pinPortIdx = 16u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTD, + .direction = GPIO_OUTPUT_DIRECTION, + .digitalFilter = false, + .initValue = 0u, + }, + { + .base = PORTD, + .pinPortIdx = 15u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTD, + .direction = GPIO_OUTPUT_DIRECTION, + .digitalFilter = false, + .initValue = 0u, + }, + { + .base = PORTC, + .pinPortIdx = 3u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTC, + .direction = GPIO_OUTPUT_DIRECTION, + .digitalFilter = false, + .initValue = 0u, + }, + { + .base = PORTC, + .pinPortIdx = 2u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTC, + .direction = GPIO_OUTPUT_DIRECTION, + .digitalFilter = false, + .initValue = 0u, + }, + { + .base = PORTC, + .pinPortIdx = 1u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTC, + .direction = GPIO_OUTPUT_DIRECTION, + .digitalFilter = false, + .initValue = 0u, + }, + { + .base = PORTC, + .pinPortIdx = 0u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTC, + .direction = GPIO_OUTPUT_DIRECTION, + .digitalFilter = false, + .initValue = 0u, + }, + { + .base = PORTC, + .pinPortIdx = 14u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_PIN_DISABLED, + .pinLock = false, + .intConfig = PORT_INT_RISING_EDGE, + .clearIntFlag = false, + .gpioBase = NULL, + .digitalFilter = false, + }, + { + .base = PORTC, + .pinPortIdx = 13u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_INT_RISING_EDGE, + .clearIntFlag = false, + .gpioBase = PTC, + .direction = GPIO_INPUT_DIRECTION, + .digitalFilter = false, + }, + { + .base = PORTC, + .pinPortIdx = 12u, + .pullConfig = PORT_INTERNAL_PULL_NOT_ENABLED, + .passiveFilter = false, + .driveSelect = PORT_LOW_DRIVE_STRENGTH, + .mux = PORT_MUX_AS_GPIO, + .pinLock = false, + .intConfig = PORT_DMA_INT_DISABLED, + .clearIntFlag = false, + .gpioBase = PTC, + .direction = GPIO_INPUT_DIRECTION, + .digitalFilter = false, + }, +}; + +#define LED_PORT PORTD +#define GPIO_PORT PTD +#define PCC_INDEX PCC_PORTD_INDEX +#define LED0 15U +#define LED1 16U +#define LED2 0U + +#define BTN_GPIO PTC +#define BTN1_PIN 13U +#define BTN2_PIN 12U +#define BTN_PORT PORTC +#define BTN_PORT_IRQn PORTC_IRQn + +/* + * @brief Function which configures the LEDs and Buttons + */ +static void GPIOInit(void) { + /* Output direction for LEDs */ + PINS_DRV_SetPinsDirection(GPIO_PORT, (1 << LED2) | (1 << LED1) | (1 << LED0)); + + /* Set Output value LEDs */ + // PINS_DRV_ClearPins(GPIO_PORT, 1 << LED1); + PINS_DRV_SetPins(GPIO_PORT, (1 << LED2) | (1 << LED1) | (1 << LED0)); + + /* Setup button pin */ + PINS_DRV_SetPinsDirection(BTN_GPIO, ~((1 << BTN1_PIN) | (1 << BTN2_PIN))); + + /* Setup button pins interrupt */ + PINS_DRV_SetPinIntSel(BTN_PORT, BTN1_PIN, PORT_INT_RISING_EDGE); + PINS_DRV_SetPinIntSel(BTN_PORT, BTN2_PIN, PORT_INT_RISING_EDGE); + + // /* Install buttons ISR */ + // INT_SYS_InstallHandler(BTN_PORT_IRQn, &buttonISR, NULL); + + // /* Enable buttons interrupt */ + // INT_SYS_EnableIRQ(BTN_PORT_IRQn); +} + +#define INST_CANCOM1 (0U) +static flexcan_state_t canCom1_State; + +const flexcan_user_config_t canCom1_InitConfig0 = { + .fd_enable = true, + .pe_clock = FLEXCAN_CLK_SOURCE_OSC, + .max_num_mb = 16, + .num_id_filters = FLEXCAN_RX_FIFO_ID_FILTERS_8, + .is_rx_fifo_needed = false, + .flexcanMode = FLEXCAN_NORMAL_MODE, + .payload = FLEXCAN_PAYLOAD_SIZE_16, + .bitrate = {.propSeg = 7, .phaseSeg1 = 4, .phaseSeg2 = 1, .preDivider = 0, .rJumpwidth = 1}, + .bitrate_cbt = {.propSeg = 7, .phaseSeg1 = 4, .phaseSeg2 = 1, .preDivider = 0, .rJumpwidth = 1}, + .transfer_type = FLEXCAN_RXFIFO_USING_INTERRUPTS, + .rxFifoDMAChannel = 0U}; + +#define TX_MAILBOX (1UL) +#define TX_MSG_ID (1UL) +#define RX_MAILBOX (0UL) +#define RX_MSG_ID (2UL) +static flexcan_msgbuff_t recvBuff; +typedef enum { LED0_CHANGE_REQUESTED = 0x00U, LED1_CHANGE_REQUESTED = 0x01U } can_commands_list; + +uint8_t ledRequested = (uint8_t)LED0_CHANGE_REQUESTED; +uint8_t callback_test = 0; + +void flexcan0_Callback(uint8_t instance, flexcan_event_type_t eventType, uint32_t buffIdx, + flexcan_state_t *flexcanState) { + + (void)flexcanState; + (void)instance; + + switch (eventType) { + case FLEXCAN_EVENT_RX_COMPLETE: + callback_test |= 0x1; // set bit0 to to evidence RX was complete + if (buffIdx == RX_MAILBOX) { + if ((recvBuff.data[0] == LED0_CHANGE_REQUESTED) && recvBuff.msgId == RX_MSG_ID) { + /* Toggle output value LED1 */ + PINS_DRV_TogglePins(GPIO_PORT, (1 << LED0)); + } else if ((recvBuff.data[0] == LED1_CHANGE_REQUESTED) && recvBuff.msgId == RX_MSG_ID) { + /* Toggle output value LED0 */ + PINS_DRV_TogglePins(GPIO_PORT, (1 << LED1)); + } + /* Start receiving data in RX_MAILBOX again. */ + FLEXCAN_DRV_Receive(INST_CANCOM1, RX_MAILBOX, &recvBuff); + } + + break; + case FLEXCAN_EVENT_RXFIFO_COMPLETE: + break; + case FLEXCAN_EVENT_DMA_COMPLETE: + break; + case FLEXCAN_EVENT_TX_COMPLETE: + callback_test |= 0x2; // set bit1 to to evidence TX was complete + PINS_DRV_SetPins(GPIO_PORT, 1 << LED2); + break; + default: + break; + } +} + +void flexcan0_ErrorCallback(uint8_t instance, flexcan_event_type_t eventType, + flexcan_state_t *flexcanState) { + volatile uint32_t error; + + (void)flexcanState; + (void)instance; + + switch (eventType) { + case FLEXCAN_EVENT_ERROR: + callback_test |= 0x4; // set bit2 to to evidence error ISR hit + + error = FLEXCAN_DRV_GetErrorStatus(INST_CANCOM1); + + if (error & 0x4) // if BOFFINT was set + { + callback_test |= 0x8; // set bit3 to to evidence bus off ISR hit + + // abort TX MB, after bus off recovery message is not send + FLEXCAN_DRV_AbortTransfer(INST_CANCOM1, TX_MAILBOX); + + PINS_DRV_ClearPins(GPIO_PORT, 1 << LED2); + } + + break; + + default: + break; + } +} + +/* + * @brief: Send data via CAN to the specified mailbox with the specified message id + * @param mailbox : Destination mailbox number + * @param messageId : Message ID + * @param data : Pointer to the TX data + * @param len : Length of the TX data + * @return : None + */ +status_t SendCANData(uint32_t mailbox, uint32_t messageId, uint8_t *data, uint32_t len) { + /* Set information about the data to be sent + * - 1 byte in length + * - Standard message ID + * - Bit rate switch enabled to use a different bitrate for the data segment + * - Flexible data rate enabled + * - Use zeros for FD padding + */ + flexcan_data_info_t dataInfo = {.data_length = len, + .msg_id_type = FLEXCAN_MSG_ID_STD, + .enable_brs = true, + .fd_enable = false, + .fd_padding = 0U}; + + /* Configure TX message buffer with index TX_MSG_ID and TX_MAILBOX*/ + FLEXCAN_DRV_ConfigTxMb(INST_CANCOM1, mailbox, &dataInfo, messageId); + + /* Execute send non-blocking */ + status_t status = FLEXCAN_DRV_Send(INST_CANCOM1, mailbox, &dataInfo, messageId, data); + return status; +} + +int BSPSendCAN(uint32_t id, uint8_t *data, uint32_t len) { + return SendCANData(TX_MAILBOX, id, data, len); +} + +/* + * @brief Initialize FlexCAN driver and configure the bit rate + */ +void FlexCANInit(void) { + /* + * Initialize FlexCAN driver + * - 8 byte payload size + * - FD enabled + * - Bus clock as peripheral engine clock + */ + status_t status = FLEXCAN_DRV_Init(INST_CANCOM1, &canCom1_State, &canCom1_InitConfig0); + DEV_ASSERT(STATUS_SUCCESS == status); + FLEXCAN_DRV_InstallEventCallback(INST_CANCOM1, (flexcan_callback_t)flexcan0_Callback, + (void *)NULL); + FLEXCAN_DRV_InstallErrorCallback(INST_CANCOM1, (flexcan_error_callback_t)flexcan0_ErrorCallback, + (void *)NULL); +} + +/*! @brief Count of user configuration structures */ +#define CLOCK_MANAGER_CONFIG_CNT 1U + +/*! @brief Count of peripheral clock user configurations */ +#define NUM_OF_PERIPHERAL_CLOCKS_0 13U + +/*! @brief Count of user Callbacks */ +#define CLOCK_MANAGER_CALLBACK_CNT 0U + +peripheral_clock_config_t peripheralClockConfig0[NUM_OF_PERIPHERAL_CLOCKS_0] = { + { + .clockName = DMAMUX0_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = FlexCAN0_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = FlexCAN1_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = FlexCAN2_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = FTFC0_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = LPSPI0_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_SIRC_DIV2, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = LPSPI1_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_SIRC_DIV2, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = LPSPI2_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_SIRC_DIV2, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = PORTA_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = PORTB_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = PORTC_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = PORTD_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, + { + .clockName = PORTE_CLK, + .clkGate = true, + .clkSrc = CLK_SRC_OFF, + .frac = MULTIPLY_BY_ONE, + .divider = DIVIDE_BY_ONE, + }, +}; + +/* ************************************************************************* + * Configuration structure for Clock Configuration 0 + * ************************************************************************* */ +/*! @brief User Configuration structure clockMan1_InitConfig0 */ +clock_manager_user_config_t clockMan1_InitConfig0 = { + /*! @brief Configuration of SIRC */ + .scgConfig = + { + .sircConfig = + { + .initialize = true, /*!< Initialize */ + /* SIRCCSR */ + .enableInStop = true, /*!< SIRCSTEN */ + .enableInLowPower = true, /*!< SIRCLPEN */ + .locked = false, /*!< LK */ + /* SIRCCFG */ + .range = SCG_SIRC_RANGE_HIGH, /*!< RANGE - High range (8 MHz) */ + /* SIRCDIV */ + .div1 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< SIRCDIV1 */ + .div2 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< SIRCDIV2 */ + }, + .fircConfig = + { + .initialize = true, /*!< Initialize */ + /* FIRCCSR */ + .regulator = true, /*!< FIRCREGOFF */ + .locked = false, /*!< LK */ + /* FIRCCFG */ + .range = SCG_FIRC_RANGE_48M, /*!< RANGE */ + /* FIRCDIV */ + .div1 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< FIRCDIV1 */ + .div2 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< FIRCDIV2 */ + }, + .rtcConfig = + { + .initialize = true, /*!< Initialize */ + .rtcClkInFreq = 0U, /*!< RTC_CLKIN */ + }, + .soscConfig = + { + .initialize = true, /*!< Initialize */ + .freq = 8000000U, /*!< Frequency */ + /* SOSCCSR */ + .monitorMode = SCG_SOSC_MONITOR_DISABLE, /*!< SOSCCM */ + .locked = false, /*!< LK */ + /* SOSCCFG */ + .extRef = SCG_SOSC_REF_OSC, /*!< EREFS */ + .gain = SCG_SOSC_GAIN_LOW, /*!< HGO */ + .range = SCG_SOSC_RANGE_HIGH, /*!< RANGE */ + /* SOSCDIV */ + .div1 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< SOSCDIV1 */ + .div2 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< SOSCDIV2 */ + }, + .spllConfig = + { + .initialize = true, /*!< Initialize */ + /* SPLLCSR */ + .monitorMode = SCG_SPLL_MONITOR_DISABLE, /*!< SPLLCM */ + .locked = false, /*!< LK */ + /* SPLLCFG */ + .prediv = (uint8_t)SCG_SPLL_CLOCK_PREDIV_BY_1, /*!< PREDIV */ + .mult = (uint8_t)SCG_SPLL_CLOCK_MULTIPLY_BY_28, /*!< MULT */ + .src = 0U, /*!< SOURCE */ + /* SPLLDIV */ + .div1 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< SPLLDIV1 */ + .div2 = SCG_ASYNC_CLOCK_DIV_BY_1, /*!< SPLLDIV2 */ + }, + .clockOutConfig = + { + .initialize = true, /*!< Initialize */ + .source = SCG_CLOCKOUT_SRC_FIRC, /*!< SCG CLKOUTSEL */ + }, + .clockModeConfig = + { + .initialize = true, /*!< Initialize */ + .rccrConfig = /*!< RCCR - Run Clock Control Register */ + { + .src = SCG_SYSTEM_CLOCK_SRC_FIRC, /*!< SCS */ + .divCore = SCG_SYSTEM_CLOCK_DIV_BY_1, /*!< DIVCORE */ + .divBus = SCG_SYSTEM_CLOCK_DIV_BY_2, /*!< DIVBUS */ + .divSlow = SCG_SYSTEM_CLOCK_DIV_BY_2, /*!< DIVSLOW */ + }, + .vccrConfig = /*!< VCCR - VLPR Clock Control Register */ + { + .src = SCG_SYSTEM_CLOCK_SRC_SIRC, /*!< SCS */ + .divCore = SCG_SYSTEM_CLOCK_DIV_BY_2, /*!< DIVCORE */ + .divBus = SCG_SYSTEM_CLOCK_DIV_BY_1, /*!< DIVBUS */ + .divSlow = SCG_SYSTEM_CLOCK_DIV_BY_4, /*!< DIVSLOW */ + }, + .hccrConfig = /*!< HCCR - HSRUN Clock Control Register */ + { + .src = SCG_SYSTEM_CLOCK_SRC_SYS_PLL, /*!< SCS */ + .divCore = SCG_SYSTEM_CLOCK_DIV_BY_1, /*!< DIVCORE */ + .divBus = SCG_SYSTEM_CLOCK_DIV_BY_2, /*!< DIVBUS */ + .divSlow = SCG_SYSTEM_CLOCK_DIV_BY_4, /*!< DIVSLOW */ + }, + }, + }, + .pccConfig = + { + .peripheralClocks = + peripheralClockConfig0, /*!< Peripheral clock control configurations */ + .count = NUM_OF_PERIPHERAL_CLOCKS_0, /*!< Number of the peripheral clock control + configurations */ + }, + .simConfig = + { + .clockOutConfig = /*!< Clock Out configuration. */ + { + .initialize = true, /*!< Initialize */ + .enable = false, /*!< CLKOUTEN */ + .source = SIM_CLKOUT_SEL_SYSTEM_SCG_CLKOUT, /*!< CLKOUTSEL */ + .divider = SIM_CLKOUT_DIV_BY_1, /*!< CLKOUTDIV */ + }, + .lpoClockConfig = /*!< Low Power Clock configuration. */ + { + .initialize = true, /*!< Initialize */ + .enableLpo1k = true, /*!< LPO1KCLKEN */ + .enableLpo32k = true, /*!< LPO32KCLKEN */ + .sourceLpoClk = SIM_LPO_CLK_SEL_LPO_128K, /*!< LPOCLKSEL */ + .sourceRtcClk = SIM_RTCCLK_SEL_SOSCDIV1_CLK, /*!< RTCCLKSEL */ + }, + .platGateConfig = /*!< Platform Gate Clock configuration. */ + { + .initialize = true, /*!< Initialize */ + .enableMscm = true, /*!< CGCMSCM */ + .enableMpu = true, /*!< CGCMPU */ + .enableDma = true, /*!< CGCDMA */ + .enableErm = true, /*!< CGCERM */ + .enableEim = true, /*!< CGCEIM */ + }, + + .qspiRefClkGating = /*!< Quad Spi Internal Reference Clock Gating. */ + { + .enableQspiRefClk = false, /*!< Qspi reference clock gating */ + }, + .tclkConfig = /*!< TCLK CLOCK configuration. */ + { + .initialize = true, /*!< Initialize */ + .tclkFreq[0] = 0U, /*!< TCLK0 */ + .tclkFreq[1] = 0U, /*!< TCLK1 */ + .tclkFreq[2] = 0U, /*!< TCLK2 */ + }, + .traceClockConfig = /*!< Debug trace Clock Configuration. */ + { + .initialize = true, /*!< Initialize */ + .divEnable = true, /*!< TRACEDIVEN */ + .source = CLOCK_TRACE_SRC_CORE_CLK, /*!< TRACECLK_SEL */ + .divider = 0U, /*!< TRACEDIV */ + .divFraction = false, /*!< TRACEFRAC */ + }, + }, + .pmcConfig = + { + .lpoClockConfig = /*!< Low Power Clock configuration. */ + { + .initialize = true, /*!< Initialize */ + .enable = true, /*!< Enable/disable LPO */ + .trimValue = 0, /*!< Trimming value for LPO */ + }, + }, +}; + +/*! @brief Array of pointers to User configuration structures */ +clock_manager_user_config_t const *g_clockManConfigsArr[] = {&clockMan1_InitConfig0}; +/*! @brief Array of pointers to User defined Callbacks configuration structures */ +clock_manager_callback_user_config_t *g_clockManCallbacksArr[] = {(void *)0}; +/* END clockMan1. */ + +void BSPInit(void) { + S32_SysTick->CSR = S32_SysTick_CSR_ENABLE(0u); + S32_SysTick->RVR = S32_SysTick_RVR_RELOAD(SystemCoreClock / 1000u); + // /* only initialize CVR on the first entry, to not cause time drift */ + S32_SysTick->CVR = S32_SysTick_CVR_CURRENT(0U); + S32_SysTick->CSR = + S32_SysTick_CSR_ENABLE(1u) | S32_SysTick_CSR_TICKINT(1u) | S32_SysTick_CSR_CLKSOURCE(1u); + + CLOCK_SYS_Init(g_clockManConfigsArr, CLOCK_MANAGER_CONFIG_CNT, g_clockManCallbacksArr, + CLOCK_MANAGER_CALLBACK_CNT); + CLOCK_SYS_UpdateConfiguration(0U, CLOCK_MANAGER_POLICY_FORCIBLE); + + PINS_DRV_Init(NUM_OF_CONFIGURED_PINS, g_pin_mux_InitConfigArr); + + GPIOInit(); + FlexCANInit(); +} + +int BSPSetLED(uint8_t led, bool value) { + if (led > 2) { + return -1; + } + int led_shift = LED0; + + switch (led) { + case 0: + led_shift = LED0; + break; + case 1: + led_shift = LED1; + break; + case 2: + led_shift = LED2; + break; + default: + return -1; + } + + if (value) { + PINS_DRV_SetPins(GPIO_PORT, 1 << led_shift); + } else { + PINS_DRV_ClearPins(GPIO_PORT, 1 << led_shift); + } + return 0; +} + +uint32_t UDSMillis(void) { return OSIF_GetMilliseconds(); } diff --git a/examples/s32k144_server/bsp.h b/examples/s32k144_server/bsp.h new file mode 100644 index 0000000..eea031a --- /dev/null +++ b/examples/s32k144_server/bsp.h @@ -0,0 +1,11 @@ +#ifndef BSP_H +#define BSP_H + +#include +#include + +void BSPInit(void); +int BSPSetLED(uint8_t led, bool value); +int BSPSendCAN(uint32_t id, uint8_t *data, uint32_t len); + +#endif diff --git a/examples/s32k144_server/main.c b/examples/s32k144_server/main.c new file mode 100644 index 0000000..478f4c5 --- /dev/null +++ b/examples/s32k144_server/main.c @@ -0,0 +1,55 @@ +#include +#include "bsp.h" +#include "iso14229.h" +#include "tp/isotp_c.h" + +UDSServer_t server; +UDSTpISOTpC_t tp; + +static int isotp_user_send_can(const uint32_t arbitration_id, const uint8_t *data, + const uint8_t size, void *user_data) { + if (0 != BSPSendCAN(arbitration_id, data, size)) { + return ISOTP_RET_ERROR; + } else { + return ISOTP_RET_OK; + } +} + +static void isotp_debug(const char *msg, ...) { + // BSPLog("%s", msg); +} + +static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + return kPositiveResponse; +} + +int main() { + BSPInit(); + UDSServerInit(&server); + server.fn = fn; + server.tp = &tp.hdl; + + UDSTpISOTpCInit(&tp, &(UDSTpISOTpCConfig_t){ + .source_addr = 0x7E8, + .target_addr = 0x7E0, + .source_addr_func = 0x7DF, + .target_addr_func = 0, + .user_data = NULL, + .isotp_user_send_can = isotp_user_send_can, + .isotp_user_debug = isotp_debug, + }); + + uint8_t data[] = {0xf0, 0xf0}; + while (1) { + UDSServerPoll(&server); + BSPSetLED(0, true); + for (volatile int i = 0; i < 1000000; i++) { + __asm__("nop"); + } + BSPSetLED(0, false); + for (volatile int i = 0; i < 1000000; i++) { + __asm__("nop"); + } + BSPSendCAN(0x123, data, sizeof(data)); + } +} \ No newline at end of file diff --git a/examples/server.c b/examples/server.c deleted file mode 100644 index 8f16127..0000000 --- a/examples/server.c +++ /dev/null @@ -1,176 +0,0 @@ -#include "../iso14229.h" -#include "uds_params.h" -#include -#include -#include -#include -#include -#include -#include -#include -#if UDS_TP == UDS_TP_ISOTP_C -#include "../isotp-c/isotp.h" -#include "isotp-c_on_socketcan.h" -#endif - -static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg); - -static UDSServer_t srv; -static const UDSServerConfig_t cfg = { - .fn = fn, -#if UDS_TP == UDS_TP_ISOTP_SOCKET - .if_name = "vcan0", -#endif - .phys_send_id = SERVER_SEND_ID, - .phys_recv_id = SERVER_PHYS_RECV_ID, - .func_recv_id = SERVER_FUNC_RECV_ID, -}; -static bool serverWantsExit = false; -static struct RWDBIData { - uint8_t d1; - int8_t d2; - uint16_t d3; - int16_t d4; -} myData = {0}; - -// 用初始化服务器实例来简单模拟一个ECU复位 -// mock an ECU reset by resetting the server -static void mockECUReset(enum UDSECUResetType resetType) { - printf("Resetting ECU (type: %d)\n", resetType); - switch (resetType) { - case kHardReset: - case kSoftReset: - UDSServerDeInit(&srv); - UDSServerInit(&srv, &cfg); - break; - default: - printf("unknown reset type %d\n", resetType); - break; - } -} - -static uint8_t RDBI(UDSServer_t *srv, UDSRDBIArgs_t *r) { - static const uint8_t msg[] = "I'm a UDS server "; - switch (r->dataId) { - case 0x1: - return r->copy(srv, &myData.d1, sizeof(myData.d1)); - case 0x8: - return r->copy(srv, (void *)msg, sizeof(msg)); - default: - return kRequestOutOfRange; - } - return kPositiveResponse; -} - -static uint8_t WDBI(UDSServer_t *srv, UDSWDBIArgs_t *r) { - switch (r->dataId) { - case 0x1: - if (r->len != sizeof(myData.d1)) { - return kIncorrectMessageLengthOrInvalidFormat; - } - myData.d1 = r->data[0]; - break; - default: - return kRequestOutOfRange; - } - return kPositiveResponse; -} - -static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - switch (ev) { - case UDS_SRV_EVT_EcuReset: { // 0x10 - UDSECUResetArgs_t *r = (UDSECUResetArgs_t *)arg; - printf("got ECUReset request of type %x\n", r->type); - switch (r->type) { - case kHardReset: - case kSoftReset: - return kPositiveResponse; - break; - default: - return kSubFunctionNotSupported; - } - break; - } - case UDS_SRV_EVT_DiagSessCtrl: { // 0x11 - UDSDiagSessCtrlArgs_t *r = (UDSDiagSessCtrlArgs_t *)arg; - switch (r->type) { - case kDefaultSession: - return kPositiveResponse; - case kProgrammingSession: - case kExtendedDiagnostic: - if (srv->securityLevel > 0) { - return kPositiveResponse; - } else { - return kSecurityAccessDenied; - } - break; - default: - return kSubFunctionNotSupported; - } - } - case UDS_SRV_EVT_ReadDataByIdent: // 0x22 - return RDBI(srv, (UDSRDBIArgs_t *)arg); - case UDS_SRV_EVT_SecAccessRequestSeed: { // 0x27 - const uint8_t seed[] = {1, 2, 3, 4}; - UDSSecAccessRequestSeedArgs_t *r = (UDSSecAccessRequestSeedArgs_t *)arg; - return r->copySeed(srv, seed, sizeof(seed)); - } - case UDS_SRV_EVT_SecAccessValidateKey: { // 0x27 - return kPositiveResponse; - } - case UDS_SRV_EVT_WriteDataByIdent: // 0x2E - return WDBI(srv, (UDSWDBIArgs_t *)arg); - case UDS_SRV_EVT_RoutineCtrl: { // 0x31 - UDSRoutineCtrlArgs_t *r = (UDSRoutineCtrlArgs_t *)arg; - if (RID_TERMINATE_PROCESS == r->id) { - serverWantsExit = true; - return kPositiveResponse; - } else { - return kRequestOutOfRange; - } - break; - } - case UDS_SRV_EVT_SessionTimeout: - printf("server session timed out!\n"); - UDSServerDeInit(srv); - UDSServerInit(srv, &cfg); - break; - case UDS_SRV_EVT_DoScheduledReset: - printf("powering down!\n"); - mockECUReset(*((enum UDSECUResetType *)arg)); - break; - default: - printf("Unhandled event: %d\n", ev); - return kServiceNotSupported; - } - return kGeneralProgrammingFailure; -} - -static int SleepMillis(uint32_t tms) { - struct timespec ts; - int ret; - ts.tv_sec = tms / 1000; - ts.tv_nsec = (tms % 1000) * 1000000; - do { - ret = nanosleep(&ts, &ts); - } while (ret && errno == EINTR); - return ret; -} - -int main(int ac, char **av) { - if (UDSServerInit(&srv, &cfg)) { - exit(-1); - } - - printf("server up, polling . . .\n"); - while (!serverWantsExit) { - UDSServerPoll(&srv); -#if UDS_TP == UDS_TP_ISOTP_C - SocketCANRecv((UDSTpIsoTpC_t *)srv.tp, cfg.phys_recv_id); -#endif - SleepMillis(1); - } - printf("server exiting\n"); - UDSServerDeInit(&srv); - return 0; -} diff --git a/examples/uds_params.h b/examples/uds_params.h deleted file mode 100644 index ccc577b..0000000 --- a/examples/uds_params.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef SHARED_H -#define SHARED_H - -/** - * | Addr | Description | - * |-------|------------------------------------| - * | 0x7E0 | Server 1 Physical Receive Address | - * | 0x7E1 | Server 2 Physical Receive Address | - * | 0x7E8 | Server 1 Physical Transmit Address | - * | 0x7E9 | Server 2 Physical Transmit Address | - * | 0x7DF | Functional Receive Address | - * - * |--0x7DF->| - * -|--0x7E0->| Server 1 - * / |<-0x7E8--| - * Client - * \ |--0x7DF->| - * -|--0x7E1->| Server 2 - * |<-0x7E9--| - * - * Client has one link that strictly sends functional requests to 0x7DF. - * Functional responses are received on the other links - * - */ - -// 服务器响应地址 -#define SERVER_SEND_ID (0x7E8) /* server sends */ -// 服务器物理接收地址 (1:1) -#define SERVER_PHYS_RECV_ID (0x7E0) /* server listens for physically (1:1) addressed messages */ -// 服务器功能接收地址 (1:n) -#define SERVER_FUNC_RECV_ID (0x7DF) /* server listens for functionally (1:n) addressed messages */ - -#define CLIENT_DEFAULT_P2_MS 150 -#define CLIENT_DEFAULT_P2_STAR_MS 1500 -#define SERVER_DEFAULT_P2_MS 50 -#define SERVER_DEFAULT_P2_STAR_MS 2000 -#define SERVER_DEFAULT_S3_MS 5000 - -#define DID_0x0001_LEN 1U -#define DID_0x0008_LEN 21U - -#define RID_TERMINATE_PROCESS 0x1234 - -#endif diff --git a/fuzz_server.c b/fuzz_server.c deleted file mode 100644 index e363458..0000000 --- a/fuzz_server.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include -#include -#include -#include "iso14229.h" - -#ifdef __cplusplus -extern "C" int LLVMFuzzerTestOneInput(const uint8_t *, size_t); -#else -int LLVMFuzzerTestOneInput(const uint8_t *, size_t); -#endif - -uint8_t retval = kPositiveResponse; - -static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { return retval; } - -struct Impl { - UDSTpHandle_t hdl; - uint8_t buf[8192]; - size_t size; -}; - -static ssize_t tp_recv(UDSTpHandle_t *hdl, void *buf, size_t count, UDSTpAddr_t *ta_type) { - struct Impl *pL_Impl = (struct Impl *)hdl; - if (pL_Impl->size < count) { - count = pL_Impl->size; - } - memmove(buf, pL_Impl->buf, count); - pL_Impl->size = 0; - return count; -} - -ssize_t tp_send(struct UDSTpHandle *hdl, const void *buf, size_t count, UDSTpAddr_t ta_type) { - return count; -} - -UDSTpStatus_t tp_poll(struct UDSTpHandle *hdl) { return 0; } - -static struct Impl impl = { - .hdl = - { - .recv = tp_recv, - .send = tp_send, - .poll = tp_poll, - }, - .buf = {0}, - .size = 0, -}; - -static uint32_t g_ms = 0; -uint32_t UDSMillis() { return g_ms; } - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - UDSServer_t srv; - UDSServerConfig_t cfg = { - .fn = fn, - .tp = &impl.hdl, - }; - if (size < 1) { - return 0; - } - - retval = data[0]; - size = size - 1; - - if (size > sizeof(impl.buf)) { - size = sizeof(impl.buf); - } - memmove(impl.buf, data, size); - impl.size = size; - UDSServerInit(&srv, &cfg); - for (g_ms = 0; g_ms < 100; g_ms++) { - UDSServerPoll(&srv); - } - return 0; -} diff --git a/iso14229.c b/iso14229.c deleted file mode 100644 index 8210d34..0000000 --- a/iso14229.c +++ /dev/null @@ -1,2067 +0,0 @@ -#include "iso14229.h" -#include -#include -#include -#include -#include -#include -#include -#include - -// ISO-14229-1:2013 Table 2 -#define UDS_MAX_DIAGNOSTIC_SERVICES 0x7F - -#define UDS_RESPONSE_SID_OF(request_sid) (request_sid + 0x40) -#define UDS_REQUEST_SID_OF(response_sid) (response_sid - 0x40) - -#define UDS_NEG_RESP_LEN 3U -#define UDS_0X10_REQ_LEN 2U -#define UDS_0X10_RESP_LEN 6U -#define UDS_0X11_REQ_MIN_LEN 2U -#define UDS_0X11_RESP_BASE_LEN 2U -#define UDS_0X23_REQ_MIN_LEN 4U -#define UDS_0X23_RESP_BASE_LEN 1U -#define UDS_0X22_RESP_BASE_LEN 1U -#define UDS_0X27_REQ_BASE_LEN 2U -#define UDS_0X27_RESP_BASE_LEN 2U -#define UDS_0X28_REQ_BASE_LEN 3U -#define UDS_0X28_RESP_LEN 2U -#define UDS_0X2E_REQ_BASE_LEN 3U -#define UDS_0X2E_REQ_MIN_LEN 4U -#define UDS_0X2E_RESP_LEN 3U -#define UDS_0X31_REQ_MIN_LEN 4U -#define UDS_0X31_RESP_MIN_LEN 4U -#define UDS_0X34_REQ_BASE_LEN 3U -#define UDS_0X34_RESP_BASE_LEN 2U -#define UDS_0X35_REQ_BASE_LEN 3U -#define UDS_0X35_RESP_BASE_LEN 2U -#define UDS_0X36_REQ_BASE_LEN 2U -#define UDS_0X36_RESP_BASE_LEN 2U -#define UDS_0X37_REQ_BASE_LEN 1U -#define UDS_0X37_RESP_BASE_LEN 1U -#define UDS_0X3E_REQ_MIN_LEN 2U -#define UDS_0X3E_REQ_MAX_LEN 2U -#define UDS_0X3E_RESP_LEN 2U -#define UDS_0X85_REQ_BASE_LEN 2U -#define UDS_0X85_RESP_LEN 2U - -enum UDSDiagnosticServiceId { - kSID_DIAGNOSTIC_SESSION_CONTROL = 0x10, - kSID_ECU_RESET = 0x11, - kSID_CLEAR_DIAGNOSTIC_INFORMATION = 0x14, - kSID_READ_DTC_INFORMATION = 0x19, - kSID_READ_DATA_BY_IDENTIFIER = 0x22, - kSID_READ_MEMORY_BY_ADDRESS = 0x23, - kSID_READ_SCALING_DATA_BY_IDENTIFIER = 0x24, - kSID_SECURITY_ACCESS = 0x27, - kSID_COMMUNICATION_CONTROL = 0x28, - kSID_READ_PERIODIC_DATA_BY_IDENTIFIER = 0x2A, - kSID_DYNAMICALLY_DEFINE_DATA_IDENTIFIER = 0x2C, - kSID_WRITE_DATA_BY_IDENTIFIER = 0x2E, - kSID_INPUT_CONTROL_BY_IDENTIFIER = 0x2F, - kSID_ROUTINE_CONTROL = 0x31, - kSID_REQUEST_DOWNLOAD = 0x34, - kSID_REQUEST_UPLOAD = 0x35, - kSID_TRANSFER_DATA = 0x36, - kSID_REQUEST_TRANSFER_EXIT = 0x37, - kSID_REQUEST_FILE_TRANSFER = 0x38, - kSID_WRITE_MEMORY_BY_ADDRESS = 0x3D, - kSID_TESTER_PRESENT = 0x3E, - kSID_ACCESS_TIMING_PARAMETER = 0x83, - kSID_SECURED_DATA_TRANSMISSION = 0x84, - kSID_CONTROL_DTC_SETTING = 0x85, - kSID_RESPONSE_ON_EVENT = 0x86, -}; - -// ======================================================================== -// Transports -// ======================================================================== - -#if UDS_TP == UDS_TP_CUSTOM -#else -static UDSTpStatus_t tp_poll(UDSTpHandle_t *hdl) { - assert(hdl); - UDSTpStatus_t status = 0; -#if UDS_TP == UDS_TP_ISOTP_C - UDSTpIsoTpC_t *impl = (UDSTpIsoTpC_t *)hdl; - isotp_poll(&impl->phys_link); - isotp_poll(&impl->func_link); - if (impl->phys_link.send_status == ISOTP_SEND_STATUS_INPROGRESS) { - status |= UDS_TP_SEND_IN_PROGRESS; - } -#elif UDS_TP == UDS_TP_ISOTP_SOCKET -#endif - return status; -} -#endif - -#if UDS_TP == UDS_TP_CUSTOM -#else -static ssize_t tp_recv(UDSTpHandle_t *hdl, void *buf, size_t count, UDSTpAddr_t *ta_type) { - assert(hdl); - assert(ta_type); - assert(buf); - int ret = -1; -#if UDS_TP == UDS_TP_ISOTP_C - uint16_t size = 0; - UDSTpIsoTpC_t *impl = (UDSTpIsoTpC_t *)hdl; - struct { - IsoTpLink *link; - UDSTpAddr_t ta_type; - } arr[] = {{&impl->phys_link, kTpAddrTypePhysical}, {&impl->func_link, kTpAddrTypeFunctional}}; - for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { - ret = isotp_receive(arr[i].link, buf, count, &size); - switch (ret) { - case ISOTP_RET_OK: - *ta_type = arr[i].ta_type; - ret = size; - goto done; - case ISOTP_RET_NO_DATA: - ret = 0; - continue; - case ISOTP_RET_ERROR: - ret = -1; - goto done; - default: - ret = -2; - goto done; - } - } -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - UDSTpLinuxIsoTp_t *impl = (UDSTpLinuxIsoTp_t *)hdl; - struct { - int fd; - UDSTpAddr_t ta_type; - } arr[] = {{impl->phys_fd, kTpAddrTypePhysical}, {impl->func_fd, kTpAddrTypeFunctional}}; - for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) { - ret = read(arr[i].fd, buf, count); - if (ret < 0) { - if (EAGAIN == errno || EWOULDBLOCK == errno) { - ret = 0; - continue; - } else { - UDS_DBG_PRINT("read failed: %d with errno: %d\n", ret, errno); - if (EILSEQ == errno) { - UDS_DBG_PRINT("Perhaps I received multiple responses?\n"); - } - goto done; - } - } else { - *ta_type = arr[i].ta_type; - goto done; - } - } -#endif -done: - if (ret > 0) { - UDS_DBG_PRINT("<<< "); - UDS_DBG_PRINTHEX(buf, ret); - } - return ret; -} -#endif - -#if UDS_TP == UDS_TP_CUSTOM -#else -static ssize_t tp_send(UDSTpHandle_t *hdl, const void *buf, size_t count, UDSTpAddr_t ta_type) { - assert(hdl); - ssize_t ret = -1; -#if UDS_TP == UDS_TP_ISOTP_C - UDSTpIsoTpC_t *impl = (UDSTpIsoTpC_t *)hdl; - IsoTpLink *link = NULL; - switch (ta_type) { - case kTpAddrTypePhysical: - link = &impl->phys_link; - break; - case kTpAddrTypeFunctional: - link = &impl->func_link; - break; - default: - ret = -4; - goto done; - } - - int send_status = isotp_send(link, buf, count); - switch (send_status) { - case ISOTP_RET_OK: - ret = count; - goto done; - case ISOTP_RET_INPROGRESS: - case ISOTP_RET_OVERFLOW: - default: - ret = send_status; - goto done; - } -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - UDSTpLinuxIsoTp_t *impl = (UDSTpLinuxIsoTp_t *)hdl; - int fd; - switch (ta_type) { - case kTpAddrTypePhysical: - fd = impl->phys_fd; - break; - case kTpAddrTypeFunctional: - fd = impl->func_fd; - break; - default: - ret = -4; - goto done; - } - ret = write(fd, buf, count); - if (ret < 0) { - perror("write"); - } -#endif -done: - UDS_DBG_PRINT(">>> "); - UDS_DBG_PRINTHEX(buf, ret); - return ret; -} -#endif - -#if UDS_TP == UDS_TP_ISOTP_SOCKET -static int LinuxSockBind(const char *if_name, uint32_t rxid, uint32_t txid) { - int fd = 0; - struct ifreq ifr = {0}; - struct sockaddr_can addr = {0}; - struct can_isotp_fc_options fcopts = { - .bs = 0x10, - .stmin = 3, - .wftmax = 0, - }; - - if ((fd = socket(AF_CAN, SOCK_DGRAM | SOCK_NONBLOCK, CAN_ISOTP)) < 0) { - fprintf(stderr, "socket: %s", strerror(errno)); - return -1; - } - - if (setsockopt(fd, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fcopts, sizeof(fcopts)) < 0) { - perror("setsockopt"); - return -1; - } - - strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name) - 1); - if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { - fprintf(stderr, "ioctl: %s %s\n", strerror(errno), if_name); - return -1; - } - - addr.can_family = AF_CAN; - addr.can_addr.tp.rx_id = rxid; - addr.can_addr.tp.tx_id = txid; - addr.can_ifindex = ifr.ifr_ifindex; - - if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf(stderr, "bind: %s %s\n", strerror(errno), if_name); - return -1; - } - printf("opened ISO-TP link fd: %d, rxid: %03x, txid: %03x\n", fd, rxid, txid); - return fd; -} - -static int LinuxSockTpOpen(UDSTpHandle_t *hdl, const char *if_name, uint32_t phys_rxid, - uint32_t phys_txid, uint32_t func_rxid, uint32_t func_txid) { - assert(if_name); - UDSTpLinuxIsoTp_t *impl = (UDSTpLinuxIsoTp_t *)hdl; - hdl->recv = tp_recv; - hdl->send = tp_send; - hdl->poll = tp_poll; - impl->phys_fd = LinuxSockBind(if_name, phys_rxid, phys_txid); - impl->func_fd = LinuxSockBind(if_name, func_rxid, func_txid); - if (impl->phys_fd < 0 || impl->func_fd < 0) { - return -1; - } - return 0; -} - -void LinuxSockTpClose(UDSTpHandle_t *hdl) { - if (hdl) { - UDSTpLinuxIsoTp_t *impl = (UDSTpLinuxIsoTp_t *)hdl; - if (impl) { - if (close(impl->phys_fd) < 0) { - perror("failed to close socket"); - } - if (close(impl->func_fd) < 0) { - perror("failed to close socket"); - } - } - } -} -#endif // #if UDS_TP == UDS_TP_ISOTP_SOCKET -// ======================================================================== -// Common -// ======================================================================== - -#if UDS_CUSTOM_MILLIS -#else -uint32_t UDSMillis() { -#if UDS_ARCH == UDS_ARCH_UNIX - struct timeval te; - gettimeofday(&te, NULL); - long long milliseconds = te.tv_sec * 1000LL + te.tv_usec / 1000; - return milliseconds; -#elif UDS_ARCH == UDS_ARCH_WINDOWS - struct timespec ts; - timespec_get(&ts, TIME_UTC); - long long milliseconds = ts.tv_sec * 1000LL + ts.tv_nsec / 1000000; - return milliseconds; -#else -#error "UDSMillis() undefined!" -#endif -} -#endif - -static bool UDSSecurityAccessLevelIsReserved(uint8_t securityLevel) { - securityLevel &= 0x3f; - return (0 == securityLevel || (0x43 <= securityLevel && securityLevel >= 0x5E) || - 0x7F == securityLevel); -} - -// ======================================================================== -// Server -// ======================================================================== - -typedef uint8_t (*UDSService)(UDSServer_t *self); - -static inline uint8_t NegativeResponse(UDSServer_t *self, uint8_t response_code) { - self->send_buf[0] = 0x7F; - self->send_buf[1] = self->recv_buf[0]; - self->send_buf[2] = response_code; - self->send_size = UDS_NEG_RESP_LEN; - return response_code; -} - -static inline void NoResponse(UDSServer_t *self) { self->send_size = 0; } - -static uint8_t _0x10_DiagnosticSessionControl(UDSServer_t *self) { - if (self->recv_size < UDS_0X10_REQ_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - uint8_t sessType = self->recv_buf[1] & 0x4F; - - UDSDiagSessCtrlArgs_t args = { - .type = sessType, - .p2_ms = UDS_CLIENT_DEFAULT_P2_MS, - .p2_star_ms = UDS_CLIENT_DEFAULT_P2_STAR_MS, - }; - - uint8_t err = self->fn(self, UDS_SRV_EVT_DiagSessCtrl, &args); - - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - - self->sessionType = sessType; - - switch (sessType) { - case kDefaultSession: - break; - case kProgrammingSession: - case kExtendedDiagnostic: - default: - self->s3_session_timeout_timer = UDSMillis() + self->s3_ms; - break; - } - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_DIAGNOSTIC_SESSION_CONTROL); - self->send_buf[1] = sessType; - - // UDS-1-2013: Table 29 - // resolution: 1ms - self->send_buf[2] = args.p2_ms >> 8; - self->send_buf[3] = args.p2_ms; - - // resolution: 10ms - self->send_buf[4] = (args.p2_star_ms / 10) >> 8; - self->send_buf[5] = args.p2_star_ms / 10; - - self->send_size = UDS_0X10_RESP_LEN; - return kPositiveResponse; -} - -static uint8_t _0x11_ECUReset(UDSServer_t *self) { - uint8_t resetType = self->recv_buf[1] & 0x3F; - - if (self->recv_size < UDS_0X11_REQ_MIN_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - UDSECUResetArgs_t args = { - .type = resetType, - .powerDownTimeMillis = UDS_SERVER_DEFAULT_POWER_DOWN_TIME_MS, - }; - - uint8_t err = self->fn(self, UDS_SRV_EVT_EcuReset, &args); - - if (kPositiveResponse == err) { - self->notReadyToReceive = true; - self->ecuResetScheduled = resetType; - self->ecuResetTimer = UDSMillis() + args.powerDownTimeMillis; - } else { - return NegativeResponse(self, err); - } - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_ECU_RESET); - self->send_buf[1] = resetType; - - if (kEnableRapidPowerShutDown == resetType) { - uint32_t powerDownTime = args.powerDownTimeMillis / 1000; - if (powerDownTime > 255) { - powerDownTime = 255; - } - self->send_buf[2] = powerDownTime; - self->send_size = UDS_0X11_RESP_BASE_LEN + 1; - } else { - self->send_size = UDS_0X11_RESP_BASE_LEN; - } - return kPositiveResponse; -} - -static uint8_t safe_copy(UDSServer_t *srv, const void *src, uint16_t count) { - if (count <= srv->send_buf_size - srv->send_size) { - memmove(srv->send_buf + srv->send_size, src, count); - srv->send_size += count; - return kPositiveResponse; - } - return kResponseTooLong; -} - -static uint8_t _0x22_ReadDataByIdentifier(UDSServer_t *self) { - uint8_t numDIDs; - uint16_t dataId = 0; - uint8_t ret = kPositiveResponse; - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_READ_DATA_BY_IDENTIFIER); - self->send_size = 1; - - if (0 != (self->recv_size - 1) % sizeof(uint16_t)) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - numDIDs = self->recv_size / sizeof(uint16_t); - - if (0 == numDIDs) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - for (int did = 0; did < numDIDs; did++) { - uint16_t idx = 1 + did * 2; - dataId = (self->recv_buf[idx] << 8) + self->recv_buf[idx + 1]; - - if (self->send_size + 3 > self->send_buf_size) { - return NegativeResponse(self, kResponseTooLong); - } - uint8_t *copylocation = self->send_buf + self->send_size; - copylocation[0] = dataId >> 8; - copylocation[1] = dataId; - self->send_size += 2; - - UDSRDBIArgs_t args = { - .dataId = dataId, - .copy = safe_copy, - }; - - ret = self->fn(self, UDS_SRV_EVT_ReadDataByIdent, &args); - - if (kPositiveResponse != ret) { - return NegativeResponse(self, ret); - } - } - return kPositiveResponse; -} - -/** - * @brief decode the addressAndLengthFormatIdentifier that appears in ReadMemoryByAddress (0x23), - * DynamicallyDefineDataIdentifier (0x2C), RequestDownload (0X34) - * - * @param self - * @param buf pointer to addressAndDataLengthFormatIdentifier in recv_buf - * @param memoryAddress the decoded memory address - * @param memorySize the decoded memory size - * @return uint8_t - */ -static uint8_t decodeAddressAndLength(UDSServer_t *self, uint8_t *const buf, void **memoryAddress, - size_t *memorySize) { - assert(self); - assert(memoryAddress); - assert(memorySize); - long long unsigned int tmp = 0; - *memoryAddress = 0; - *memorySize = 0; - - assert(buf >= self->recv_buf && buf <= self->recv_buf + sizeof(self->recv_buf)); - - if (self->recv_size < 3) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - uint8_t memorySizeLength = (buf[0] & 0xF0) >> 4; - uint8_t memoryAddressLength = buf[0] & 0x0F; - - if (memorySizeLength == 0 || memorySizeLength > sizeof(size_t)) { - return NegativeResponse(self, kRequestOutOfRange); - } - - if (memoryAddressLength == 0 || memoryAddressLength > sizeof(size_t)) { - return NegativeResponse(self, kRequestOutOfRange); - } - - if (buf + memorySizeLength + memoryAddressLength > self->recv_buf + self->recv_size) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - for (int byteIdx = 0; byteIdx < memoryAddressLength; byteIdx++) { - long long unsigned int byte = buf[1 + byteIdx]; - uint8_t shiftBytes = memoryAddressLength - 1 - byteIdx; - tmp |= byte << (8 * shiftBytes); - } - *memoryAddress = (void *)tmp; - - for (int byteIdx = 0; byteIdx < memorySizeLength; byteIdx++) { - uint8_t byte = buf[1 + memoryAddressLength + byteIdx]; - uint8_t shiftBytes = memorySizeLength - 1 - byteIdx; - *memorySize |= (size_t)byte << (8 * shiftBytes); - } - return kPositiveResponse; -} - -static uint8_t _0x23_ReadMemoryByAddress(UDSServer_t *self) { - uint8_t ret = kPositiveResponse; - void *address = 0; - size_t length = 0; - - if (self->recv_size < UDS_0X23_REQ_MIN_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - ret = decodeAddressAndLength(self, &self->recv_buf[1], &address, &length); - if (kPositiveResponse != ret) { - return NegativeResponse(self, ret); - } - - UDSReadMemByAddrArgs_t args = { - .memAddr = address, - .memSize = length, - .copy = safe_copy, - }; - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_READ_MEMORY_BY_ADDRESS); - self->send_size = UDS_0X23_RESP_BASE_LEN; - ret = self->fn(self, UDS_SRV_EVT_ReadMemByAddr, &args); - if (kPositiveResponse != ret) { - return NegativeResponse(self, ret); - } - if (self->send_size != UDS_0X23_RESP_BASE_LEN + length) { - return kGeneralProgrammingFailure; - } - return kPositiveResponse; -} - -static uint8_t _0x27_SecurityAccess(UDSServer_t *self) { - uint8_t subFunction = self->recv_buf[1]; - uint8_t response = kPositiveResponse; - - if (UDSSecurityAccessLevelIsReserved(subFunction)) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_SECURITY_ACCESS); - self->send_buf[1] = subFunction; - self->send_size = UDS_0X27_RESP_BASE_LEN; - - // Even: sendKey - if (0 == subFunction % 2) { - uint8_t requestedLevel = subFunction - 1; - UDSSecAccessValidateKeyArgs_t args = { - .level = requestedLevel, - .key = &self->recv_buf[UDS_0X27_REQ_BASE_LEN], - .len = self->recv_size - UDS_0X27_REQ_BASE_LEN, - }; - - response = self->fn(self, UDS_SRV_EVT_SecAccessValidateKey, &args); - - if (kPositiveResponse != response) { - return NegativeResponse(self, response); - } - - // "requestSeed = 0x01" identifies a fixed relationship between - // "requestSeed = 0x01" and "sendKey = 0x02" - // "requestSeed = 0x03" identifies a fixed relationship between - // "requestSeed = 0x03" and "sendKey = 0x04" - self->securityLevel = requestedLevel; - self->send_size = UDS_0X27_RESP_BASE_LEN; - return kPositiveResponse; - } - - // Odd: requestSeed - else { - /* If a server supports security, but the requested security level is already unlocked when - a SecurityAccess ‘requestSeed’ message is received, that server shall respond with a - SecurityAccess ‘requestSeed’ positive response message service with a seed value equal to - zero (0). The server shall never send an all zero seed for a given security level that is - currently locked. The client shall use this method to determine if a server is locked for a - particular security level by checking for a non-zero seed. - */ - if (subFunction == self->securityLevel) { - // Table 52 sends a response of length 2. Use a preprocessor define if this needs - // customizing by the user. - const uint8_t already_unlocked[] = {0x00, 0x00}; - return safe_copy(self, already_unlocked, sizeof(already_unlocked)); - } else { - UDSSecAccessRequestSeedArgs_t args = { - .level = subFunction, - .dataRecord = &self->recv_buf[UDS_0X27_REQ_BASE_LEN], - .len = self->recv_size - UDS_0X27_REQ_BASE_LEN, - .copySeed = safe_copy, - }; - - response = self->fn(self, UDS_SRV_EVT_SecAccessRequestSeed, &args); - - if (kPositiveResponse != response) { - return NegativeResponse(self, response); - } - - if (self->send_size <= UDS_0X27_RESP_BASE_LEN) { // no data was copied - return NegativeResponse(self, kGeneralProgrammingFailure); - } - return kPositiveResponse; - } - } - return NegativeResponse(self, kGeneralProgrammingFailure); -} - -static uint8_t _0x28_CommunicationControl(UDSServer_t *self) { - uint8_t controlType = self->recv_buf[1] & 0x7F; - uint8_t communicationType = self->recv_buf[2]; - - if (self->recv_size < UDS_0X28_REQ_BASE_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - UDSCommCtrlArgs_t args = { - .ctrlType = controlType, - .commType = communicationType, - }; - - uint8_t err = self->fn(self, UDS_SRV_EVT_CommCtrl, &args); - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_COMMUNICATION_CONTROL); - self->send_buf[1] = controlType; - self->send_size = UDS_0X28_RESP_LEN; - return kPositiveResponse; -} - -static uint8_t _0x2E_WriteDataByIdentifier(UDSServer_t *self) { - uint16_t dataLen = 0; - uint16_t dataId = 0; - uint8_t err = kPositiveResponse; - - /* UDS-1 2013 Figure 21 Key 1 */ - if (self->recv_size < UDS_0X2E_REQ_MIN_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - dataId = (self->recv_buf[1] << 8) + self->recv_buf[2]; - dataLen = self->recv_size - UDS_0X2E_REQ_BASE_LEN; - - UDSWDBIArgs_t args = { - .dataId = dataId, - .data = &self->recv_buf[UDS_0X2E_REQ_BASE_LEN], - .len = dataLen, - }; - - err = self->fn(self, UDS_SRV_EVT_WriteDataByIdent, &args); - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_WRITE_DATA_BY_IDENTIFIER); - self->send_buf[1] = dataId >> 8; - self->send_buf[2] = dataId; - self->send_size = UDS_0X2E_RESP_LEN; - return kPositiveResponse; -} - -static uint8_t _0x31_RoutineControl(UDSServer_t *self) { - uint8_t err = kPositiveResponse; - if (self->recv_size < UDS_0X31_REQ_MIN_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - uint8_t routineControlType = self->recv_buf[1] & 0x7F; - uint16_t routineIdentifier = (self->recv_buf[2] << 8) + self->recv_buf[3]; - - UDSRoutineCtrlArgs_t args = { - .ctrlType = routineControlType, - .id = routineIdentifier, - .optionRecord = &self->recv_buf[UDS_0X31_REQ_MIN_LEN], - .len = self->recv_size - UDS_0X31_REQ_MIN_LEN, - .copyStatusRecord = safe_copy, - }; - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_ROUTINE_CONTROL); - self->send_buf[1] = routineControlType; - self->send_buf[2] = routineIdentifier >> 8; - self->send_buf[3] = routineIdentifier; - self->send_size = UDS_0X31_RESP_MIN_LEN; - - switch (routineControlType) { - case kStartRoutine: - case kStopRoutine: - case kRequestRoutineResults: - err = self->fn(self, UDS_SRV_EVT_RoutineCtrl, &args); - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - break; - default: - return NegativeResponse(self, kRequestOutOfRange); - } - return kPositiveResponse; -} - -static void ResetTransfer(UDSServer_t *srv) { - assert(srv); - srv->xferBlockSequenceCounter = 1; - srv->xferByteCounter = 0; - srv->xferTotalBytes = 0; - srv->xferIsActive = false; -} - -static uint8_t _0x34_RequestDownload(UDSServer_t *self) { - uint8_t err; - void *memoryAddress = 0; - size_t memorySize = 0; - - if (self->xferIsActive) { - return NegativeResponse(self, kConditionsNotCorrect); - } - - if (self->recv_size < UDS_0X34_REQ_BASE_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - err = decodeAddressAndLength(self, &self->recv_buf[2], &memoryAddress, &memorySize); - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - - UDSRequestDownloadArgs_t args = { - .addr = memoryAddress, - .size = memorySize, - .dataFormatIdentifier = self->recv_buf[1], - .maxNumberOfBlockLength = UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH, - }; - - err = self->fn(self, UDS_SRV_EVT_RequestDownload, &args); - - if (args.maxNumberOfBlockLength < 3) { - UDS_DBG_PRINT("ERROR: maxNumberOfBlockLength too short"); - return NegativeResponse(self, kGeneralProgrammingFailure); - } - - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - - ResetTransfer(self); - self->xferIsActive = true; - self->xferTotalBytes = memorySize; - self->xferBlockLength = args.maxNumberOfBlockLength; - - // ISO-14229-1:2013 Table 401: - uint8_t lengthFormatIdentifier = sizeof(args.maxNumberOfBlockLength) << 4; - - /* ISO-14229-1:2013 Table 396: maxNumberOfBlockLength - This parameter is used by the requestDownload positive response message to - inform the client how many data bytes (maxNumberOfBlockLength) to include in - each TransferData request message from the client. This length reflects the - complete message length, including the service identifier and the - data-parameters present in the TransferData request message. - */ - if (args.maxNumberOfBlockLength > UDS_TP_MTU) { - args.maxNumberOfBlockLength = UDS_TP_MTU; - } - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_REQUEST_DOWNLOAD); - self->send_buf[1] = lengthFormatIdentifier; - for (uint8_t idx = 0; idx < sizeof(args.maxNumberOfBlockLength); idx++) { - uint8_t shiftBytes = sizeof(args.maxNumberOfBlockLength) - 1 - idx; - uint8_t byte = args.maxNumberOfBlockLength >> (shiftBytes * 8); - self->send_buf[UDS_0X34_RESP_BASE_LEN + idx] = byte; - } - self->send_size = UDS_0X34_RESP_BASE_LEN + sizeof(args.maxNumberOfBlockLength); - return kPositiveResponse; -} - -static uint8_t _0x35_RequestUpload(UDSServer_t *self) { - uint8_t err; - void *memoryAddress = 0; - size_t memorySize = 0; - - if (self->xferIsActive) { - return NegativeResponse(self, kConditionsNotCorrect); - } - - if (self->recv_size < UDS_0X35_REQ_BASE_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - - err = decodeAddressAndLength(self, &self->recv_buf[2], &memoryAddress, &memorySize); - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - - UDSRequestUploadArgs_t args = { - .addr = memoryAddress, - .size = memorySize, - .dataFormatIdentifier = self->recv_buf[1], - .maxNumberOfBlockLength = UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH, - }; - - err = self->fn(self, UDS_SRV_EVT_RequestUpload, &args); - - if (args.maxNumberOfBlockLength < 3) { - UDS_DBG_PRINT("ERROR: maxNumberOfBlockLength too short"); - return NegativeResponse(self, kGeneralProgrammingFailure); - } - - if (kPositiveResponse != err) { - return NegativeResponse(self, err); - } - - ResetTransfer(self); - self->xferIsActive = true; - self->xferTotalBytes = memorySize; - self->xferBlockLength = args.maxNumberOfBlockLength; - - uint8_t lengthFormatIdentifier = sizeof(args.maxNumberOfBlockLength) << 4; - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_REQUEST_UPLOAD); - self->send_buf[1] = lengthFormatIdentifier; - for (uint8_t idx = 0; idx < sizeof(args.maxNumberOfBlockLength); idx++) { - uint8_t shiftBytes = sizeof(args.maxNumberOfBlockLength) - 1 - idx; - uint8_t byte = args.maxNumberOfBlockLength >> (shiftBytes * 8); - self->send_buf[UDS_0X35_RESP_BASE_LEN + idx] = byte; - } - self->send_size = UDS_0X35_RESP_BASE_LEN + sizeof(args.maxNumberOfBlockLength); - return kPositiveResponse; -} - -static uint8_t _0x36_TransferData(UDSServer_t *self) { - uint8_t err = kPositiveResponse; - uint16_t request_data_len = self->recv_size - UDS_0X36_REQ_BASE_LEN; - uint8_t blockSequenceCounter = 0; - - if (!self->xferIsActive) { - return NegativeResponse(self, kUploadDownloadNotAccepted); - } - - if (self->recv_size < UDS_0X36_REQ_BASE_LEN) { - err = kIncorrectMessageLengthOrInvalidFormat; - goto fail; - } - - blockSequenceCounter = self->recv_buf[1]; - - if (!self->RCRRP) { - if (blockSequenceCounter != self->xferBlockSequenceCounter) { - err = kRequestSequenceError; - goto fail; - } else { - self->xferBlockSequenceCounter++; - } - } - - if (self->xferByteCounter + request_data_len > self->xferTotalBytes) { - err = kTransferDataSuspended; - goto fail; - } - - { - UDSTransferDataArgs_t args = { - .data = &self->recv_buf[UDS_0X36_REQ_BASE_LEN], - .len = self->recv_size - UDS_0X36_REQ_BASE_LEN, - .maxRespLen = self->xferBlockLength - UDS_0X36_RESP_BASE_LEN, - .copyResponse = safe_copy, - }; - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_TRANSFER_DATA); - self->send_buf[1] = blockSequenceCounter; - self->send_size = UDS_0X36_RESP_BASE_LEN; - - err = self->fn(self, UDS_SRV_EVT_TransferData, &args); - - switch (err) { - case kPositiveResponse: - self->xferByteCounter += request_data_len; - return kPositiveResponse; - case kRequestCorrectlyReceived_ResponsePending: - return NegativeResponse(self, kRequestCorrectlyReceived_ResponsePending); - default: - goto fail; - } - } - -fail: - ResetTransfer(self); - return NegativeResponse(self, err); -} - -static uint8_t _0x37_RequestTransferExit(UDSServer_t *self) { - uint8_t err = kPositiveResponse; - - if (!self->xferIsActive) { - return NegativeResponse(self, kUploadDownloadNotAccepted); - } - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_REQUEST_TRANSFER_EXIT); - self->send_size = UDS_0X37_RESP_BASE_LEN; - - UDSRequestTransferExitArgs_t args = { - .data = &self->recv_buf[UDS_0X37_REQ_BASE_LEN], - .len = self->recv_size - UDS_0X37_REQ_BASE_LEN, - .copyResponse = safe_copy, - }; - - err = self->fn(self, UDS_SRV_EVT_RequestTransferExit, &args); - - switch (err) { - case kPositiveResponse: - ResetTransfer(self); - return kPositiveResponse; - case kRequestCorrectlyReceived_ResponsePending: - return NegativeResponse(self, kRequestCorrectlyReceived_ResponsePending); - default: - ResetTransfer(self); - return NegativeResponse(self, err); - } -} - -static uint8_t _0x3E_TesterPresent(UDSServer_t *self) { - if ((self->recv_size < UDS_0X3E_REQ_MIN_LEN) || - (self->recv_size > UDS_0X3E_REQ_MAX_LEN)) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - uint8_t zeroSubFunction = self->recv_buf[1]; - - switch (zeroSubFunction) { - case 0x00: - case 0x80: - self->s3_session_timeout_timer = UDSMillis() + self->s3_ms; - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_TESTER_PRESENT); - self->send_buf[1] = 0x00; - self->send_size = UDS_0X3E_RESP_LEN; - return kPositiveResponse; - default: - return NegativeResponse(self, kSubFunctionNotSupported); - } -} - -static uint8_t _0x85_ControlDTCSetting(UDSServer_t *self) { - (void)self; - if (self->recv_size < UDS_0X85_REQ_BASE_LEN) { - return NegativeResponse(self, kIncorrectMessageLengthOrInvalidFormat); - } - uint8_t dtcSettingType = self->recv_buf[1] & 0x3F; - - self->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_CONTROL_DTC_SETTING); - self->send_buf[1] = dtcSettingType; - self->send_size = UDS_0X85_RESP_LEN; - return kPositiveResponse; -} - -/** - * @brief Get the internal service handler matching the given SID. - * @param sid - * @return pointer to UDSService or NULL if no match - */ -static UDSService getServiceForSID(uint8_t sid) { - switch (sid) { - case kSID_DIAGNOSTIC_SESSION_CONTROL: - return _0x10_DiagnosticSessionControl; - case kSID_ECU_RESET: - return _0x11_ECUReset; - case kSID_CLEAR_DIAGNOSTIC_INFORMATION: - return NULL; - case kSID_READ_DTC_INFORMATION: - return NULL; - case kSID_READ_DATA_BY_IDENTIFIER: - return _0x22_ReadDataByIdentifier; - case kSID_READ_MEMORY_BY_ADDRESS: - return _0x23_ReadMemoryByAddress; - case kSID_READ_SCALING_DATA_BY_IDENTIFIER: - return NULL; - case kSID_SECURITY_ACCESS: - return _0x27_SecurityAccess; - case kSID_COMMUNICATION_CONTROL: - return _0x28_CommunicationControl; - case kSID_READ_PERIODIC_DATA_BY_IDENTIFIER: - return NULL; - case kSID_DYNAMICALLY_DEFINE_DATA_IDENTIFIER: - return NULL; - case kSID_WRITE_DATA_BY_IDENTIFIER: - return _0x2E_WriteDataByIdentifier; - case kSID_INPUT_CONTROL_BY_IDENTIFIER: - return NULL; - case kSID_ROUTINE_CONTROL: - return _0x31_RoutineControl; - case kSID_REQUEST_DOWNLOAD: - return _0x34_RequestDownload; - case kSID_REQUEST_UPLOAD: - return _0x35_RequestUpload; - case kSID_TRANSFER_DATA: - return _0x36_TransferData; - case kSID_REQUEST_TRANSFER_EXIT: - return _0x37_RequestTransferExit; - case kSID_REQUEST_FILE_TRANSFER: - return NULL; - case kSID_WRITE_MEMORY_BY_ADDRESS: - return NULL; - case kSID_TESTER_PRESENT: - return _0x3E_TesterPresent; - case kSID_ACCESS_TIMING_PARAMETER: - return NULL; - case kSID_SECURED_DATA_TRANSMISSION: - return NULL; - case kSID_CONTROL_DTC_SETTING: - return _0x85_ControlDTCSetting; - case kSID_RESPONSE_ON_EVENT: - return NULL; - default: - UDS_DBG_PRINT("no handler for request SID %x.\n", sid); - return NULL; - } -} - -/** - * @brief Call the service if it exists, modifying the response if the spec calls for it. - * @note see UDS-1 2013 7.5.5 Pseudo code example of server response behavior - * - * @param self - * @param addressingScheme - */ -static uint8_t evaluateServiceResponse(UDSServer_t *self, const uint8_t addressingScheme) { - uint8_t response = kPositiveResponse; - bool suppressResponse = false; - uint8_t sid = self->recv_buf[0]; - UDSService service = getServiceForSID(sid); - - if (NULL == service || NULL == self->fn) { - return NegativeResponse(self, kServiceNotSupported); - } - assert(service); - assert(self->fn); // service handler functions will call self->fn. it must be valid - - switch (sid) { - /* CASE Service_with_sub-function */ - /* test if service with sub-function is supported */ - case kSID_DIAGNOSTIC_SESSION_CONTROL: - case kSID_ECU_RESET: - case kSID_SECURITY_ACCESS: - case kSID_COMMUNICATION_CONTROL: - case kSID_ROUTINE_CONTROL: - case kSID_TESTER_PRESENT: - case kSID_CONTROL_DTC_SETTING: { - response = service(self); - - bool suppressPosRspMsgIndicationBit = self->recv_buf[1] & 0x80; - - /* test if positive response is required and if responseCode is positive 0x00 */ - if ((suppressPosRspMsgIndicationBit) && (response == kPositiveResponse) && - ( - // TODO: *not yet a NRC 0x78 response sent* - true)) { - suppressResponse = true; - } else { - suppressResponse = false; - } - break; - } - - /* CASE Service_without_sub-function */ - /* test if service without sub-function is supported */ - case kSID_READ_DATA_BY_IDENTIFIER: - case kSID_READ_MEMORY_BY_ADDRESS: - case kSID_WRITE_DATA_BY_IDENTIFIER: - case kSID_REQUEST_DOWNLOAD: - case kSID_REQUEST_UPLOAD: - case kSID_TRANSFER_DATA: - case kSID_REQUEST_TRANSFER_EXIT: { - response = service(self); - break; - } - - /* CASE Service_not_implemented */ - /* shouldn't get this far as getServiceForSID(sid) will return NULL*/ - case kSID_CLEAR_DIAGNOSTIC_INFORMATION: - case kSID_READ_DTC_INFORMATION: - case kSID_READ_SCALING_DATA_BY_IDENTIFIER: - case kSID_READ_PERIODIC_DATA_BY_IDENTIFIER: - case kSID_DYNAMICALLY_DEFINE_DATA_IDENTIFIER: - case kSID_INPUT_CONTROL_BY_IDENTIFIER: - case kSID_REQUEST_FILE_TRANSFER: - case kSID_WRITE_MEMORY_BY_ADDRESS: - case kSID_ACCESS_TIMING_PARAMETER: - case kSID_SECURED_DATA_TRANSMISSION: - case kSID_RESPONSE_ON_EVENT: - default: { - response = kServiceNotSupported; - break; - } - } - - if ((kTpAddrTypeFunctional == addressingScheme) && - ((kServiceNotSupported == response) || (kSubFunctionNotSupported == response) || - (kServiceNotSupportedInActiveSession == response) || - (kSubFunctionNotSupportedInActiveSession == response) || - (kRequestOutOfRange == response)) && - ( - // TODO: *not yet a NRC 0x78 response sent* - true)) { - suppressResponse = true; /* Suppress negative response message */ - NoResponse(self); - } else { - if (suppressResponse) { /* Suppress positive response message */ - NoResponse(self); - } else { /* send negative or positive response */ - ; - } - } - return response; -} - -/** - * @brief Process the data on this link - * - * @param self - * @param link transport handle - * @param addressingScheme - */ -static void ProcessLink(UDSServer_t *self, const UDSTpAddr_t ta_type) { - - uint8_t response = evaluateServiceResponse(self, ta_type); - - if (kRequestCorrectlyReceived_ResponsePending == response) { - self->RCRRP = true; - self->notReadyToReceive = true; - } else { - self->RCRRP = false; - } - - if (self->send_size) { - int result = self->tp->send(self->tp, self->send_buf, self->send_size, ta_type); - assert(result == self->send_size); // how should it be handled if send fails? - } -} - -// ======================================================================== -// Public Functions -// ======================================================================== - -/** - * @brief \~chinese 初始化服务器 \~english Initialize the server - * - * @param self - * @param cfg - * @return int - */ -UDSErr_t UDSServerInit(UDSServer_t *self, const UDSServerConfig_t *cfg) { - assert(self); - assert(cfg); - memset(self, 0, sizeof(UDSServer_t)); - self->recv_buf_size = sizeof(self->recv_buf); - self->send_buf_size = sizeof(self->send_buf); - self->p2_ms = UDS_SERVER_DEFAULT_P2_MS; - self->p2_star_ms = UDS_SERVER_DEFAULT_P2_STAR_MS; - self->s3_ms = UDS_SERVER_DEFAULT_S3_MS; - self->fn = cfg->fn; - self->sessionType = kDefaultSession; - - // Initialize p2_timer to an already past time, otherwise the server's - // response to incoming messages will be delayed. - self->p2_timer = UDSMillis() - self->p2_ms; - - // Set the session timeout for s3 milliseconds from now. - self->s3_session_timeout_timer = UDSMillis() + self->s3_ms; - -#if UDS_TP == UDS_TP_CUSTOM - assert(cfg->tp); - assert(cfg->tp->recv); - assert(cfg->tp->send); - assert(cfg->tp->poll); - self->tp = cfg->tp; -#elif UDS_TP == UDS_TP_ISOTP_C - assert(cfg->phys_send_id != cfg->func_recv_id && cfg->func_recv_id != cfg->phys_recv_id); - UDSTpIsoTpC_t *tp = &self->tp_impl; - isotp_init_link(&tp->phys_link, cfg->phys_send_id, self->send_buf, self->send_buf_size, - self->recv_buf, self->recv_buf_size); - isotp_init_link(&tp->func_link, cfg->phys_send_id, tp->func_send_buf, sizeof(tp->func_send_buf), - tp->func_recv_buf, sizeof(tp->func_recv_buf)); - self->tp = (UDSTpHandle_t *)tp; - self->tp->poll = tp_poll; - self->tp->send = tp_send; - self->tp->recv = tp_recv; -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - self->tp = (UDSTpHandle_t *)&self->tp_impl; - if (LinuxSockTpOpen(self->tp, cfg->if_name, cfg->phys_recv_id, cfg->phys_send_id, - cfg->func_recv_id, cfg->phys_send_id)) { - return UDS_ERR; - } -#endif - return UDS_OK; -} - -void UDSServerDeInit(UDSServer_t *self) { -#if UDS_TP == UDS_TP_ISOTP_SOCKET - LinuxSockTpClose(self->tp); -#endif -} - -void UDSServerPoll(UDSServer_t *self) { - // UDS-1-2013 Figure 38: Session Timeout (S3) - if (kDefaultSession != self->sessionType && - UDSTimeAfter(UDSMillis(), self->s3_session_timeout_timer)) { - if (self->fn) { - self->fn(self, UDS_SRV_EVT_SessionTimeout, NULL); - } - } - - if (self->ecuResetScheduled && UDSTimeAfter(UDSMillis(), self->ecuResetTimer)) { - if (self->fn) { - self->fn(self, UDS_SRV_EVT_DoScheduledReset, &self->ecuResetScheduled); - } - } - - UDSTpStatus_t tp_status = self->tp->poll(self->tp); - if (tp_status & UDS_TP_SEND_IN_PROGRESS) { - return; - } - - // If the user service handler responded RCRRP and the send link is now idle, - // the response has been sent and the long-running service can now be called. - if (self->RCRRP) { - ProcessLink(self, kTpAddrTypePhysical); - self->notReadyToReceive = self->RCRRP; - return; - } - - if (self->notReadyToReceive) { - return; - } - - // new data may be processed only after p2 has elapsed - int size = 0; - UDSTpAddr_t ta_type = kTpAddrTypePhysical; - if (UDSTimeAfter(UDSMillis(), self->p2_timer)) { - size = self->tp->recv(self->tp, self->recv_buf, self->recv_buf_size, &ta_type); - if (size > 0) { - self->recv_size = size; - ProcessLink(self, ta_type); - self->p2_timer = UDSMillis() + self->p2_ms; - } else if (size == 0) { - ; - } else { - UDS_DBG_PRINT("tp_recv failed with err %d on tp %d\n", size, ta_type); - } - } -} - -// ======================================================================== -// Client -// ======================================================================== - -static void clearRequestContext(UDSClient_t *client) { - assert(client); - assert(client->tp); - memset(client->recv_buf, 0, client->recv_buf_size); - memset(client->send_buf, 0, client->send_buf_size); - client->recv_size = 0; - client->send_size = 0; - client->state = kRequestStateIdle; - client->err = UDS_OK; -} - -UDSErr_t UDSClientInit(UDSClient_t *client, const UDSClientConfig_t *cfg) { - assert(client); - assert(cfg); - memset(client, 0, sizeof(*client)); - - client->p2_ms = UDS_CLIENT_DEFAULT_P2_MS; - client->p2_star_ms = UDS_CLIENT_DEFAULT_P2_STAR_MS; - client->recv_buf_size = sizeof(client->recv_buf); - client->send_buf_size = sizeof(client->send_buf); - - if (client->p2_star_ms < client->p2_ms) { - client->p2_star_ms = client->p2_ms; - } - -#if UDS_TP == UDS_TP_CUSTOM - assert(cfg->tp); - assert(cfg->tp->recv); - assert(cfg->tp->send); - assert(cfg->tp->poll); - client->tp = cfg->tp; -#elif UDS_TP == UDS_TP_ISOTP_C - assert(cfg->phys_recv_id != cfg->func_send_id && cfg->func_send_id != cfg->phys_send_id); - UDSTpIsoTpC_t *tp = (UDSTpIsoTpC_t *)&client->tp_impl; - isotp_init_link(&tp->phys_link, cfg->phys_send_id, client->send_buf, client->send_buf_size, - client->recv_buf, client->recv_buf_size); - isotp_init_link(&tp->func_link, cfg->func_send_id, tp->func_send_buf, sizeof(tp->func_send_buf), - tp->func_recv_buf, sizeof(tp->func_recv_buf)); - client->tp = (UDSTpHandle_t *)tp; - client->tp->poll = tp_poll; - client->tp->send = tp_send; - client->tp->recv = tp_recv; -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - client->tp = (UDSTpHandle_t *)&client->tp_impl; - if (LinuxSockTpOpen(client->tp, cfg->if_name, cfg->phys_recv_id, cfg->phys_send_id, - cfg->phys_recv_id, cfg->func_send_id)) { - return UDS_ERR; - } - assert(client->tp); -#endif - - clearRequestContext(client); - return UDS_OK; -} - -void UDSClientDeInit(UDSClient_t *client) { -#if UDS_TP == UDS_TP_ISOTP_SOCKET - LinuxSockTpClose(client->tp); -#endif -} - -static void changeState(UDSClient_t *client, enum UDSClientRequestState state) { - // printf("client state: %d -> %d\n", client->state, state); - client->state = state; -} - -/** - * @brief Check that the response is a valid UDS response - * - * @param ctx - * @return UDSErr_t - */ -static UDSErr_t _ClientValidateResponse(const UDSClient_t *client) { - - if (client->recv_size < 1) { - return UDS_ERR_RESP_TOO_SHORT; - } - - if (0x7F == client->recv_buf[0]) { // 否定响应 - if (client->recv_size < 2) { - return UDS_ERR_RESP_TOO_SHORT; - } else if (client->send_buf[0] != client->recv_buf[1]) { - return UDS_ERR_SID_MISMATCH; - } else if (kRequestCorrectlyReceived_ResponsePending == client->recv_buf[2]) { - return UDS_OK; - } else if (client->_options_copy & UDS_NEG_RESP_IS_ERR) { - return UDS_ERR_NEG_RESP; - } else { - ; - } - } else { // 肯定响应 - if (UDS_RESPONSE_SID_OF(client->send_buf[0]) != client->recv_buf[0]) { - return UDS_ERR_SID_MISMATCH; - } - switch (client->send_buf[0]) { - case kSID_ECU_RESET: - if (client->recv_size < 2) { - return UDS_ERR_RESP_TOO_SHORT; - } else if (client->send_buf[1] != client->recv_buf[1]) { - return UDS_ERR_SUBFUNCTION_MISMATCH; - } else { - ; - } - break; - } - } - - return UDS_OK; -} - -/** - * @brief Handle validated server response - * @param client - */ -static inline void _ClientHandleResponse(UDSClient_t *client) { - if (0x7F == client->recv_buf[0]) { - if (kRequestCorrectlyReceived_ResponsePending == client->recv_buf[2]) { - UDS_DBG_PRINT("got RCRRP, setting p2 timer\n"); - client->p2_timer = UDSMillis() + client->p2_star_ms; - memset(client->recv_buf, 0, client->recv_buf_size); - client->recv_size = 0; - changeState(client, kRequestStateAwaitResponse); - return; - } else { - ; - } - } else { - uint8_t respSid = client->recv_buf[0]; - switch (UDS_REQUEST_SID_OF(respSid)) { - case kSID_DIAGNOSTIC_SESSION_CONTROL: { - if (client->recv_size < UDS_0X10_RESP_LEN) { - UDS_DBG_PRINT("Error: SID %x response too short\n", - kSID_DIAGNOSTIC_SESSION_CONTROL); - client->err = UDS_ERR_RESP_TOO_SHORT; - changeState(client, kRequestStateIdle); - return; - } - - if (client->_options_copy & UDS_IGNORE_SRV_TIMINGS) { - changeState(client, kRequestStateIdle); - return; - } - - uint16_t p2 = (client->recv_buf[2] << 8) + client->recv_buf[3]; - uint32_t p2_star = ((client->recv_buf[4] << 8) + client->recv_buf[5]) * 10; - UDS_DBG_PRINT("received new timings: p2: %u, p2*: %u\n", p2, p2_star); - client->p2_ms = p2; - client->p2_star_ms = p2_star; - break; - } - default: - break; - } - } - changeState(client, kRequestStateIdle); -} - -/** - * @brief execute the client request state machine - * @param client - */ -static void PollLowLevel(UDSClient_t *client) { - assert(client); - UDSTpStatus_t tp_status = client->tp->poll(client->tp); - switch (client->state) { - case kRequestStateIdle: { - client->options = client->defaultOptions; - break; - } - case kRequestStateSending: { - UDSTpAddr_t ta_type = - client->_options_copy & UDS_FUNCTIONAL ? kTpAddrTypeFunctional : kTpAddrTypePhysical; - ssize_t ret = 0; - ret = client->tp->send(client->tp, client->send_buf, client->send_size, ta_type); - - if (ret < 0) { - client->err = UDS_ERR_TPORT; - UDS_DBG_PRINT("tport err: %ld\n", ret); - } else if (0 == ret) { - UDS_DBG_PRINT("send in progress...\n"); - ; // 等待发送成功 - } else if (client->send_size == ret) { - changeState(client, kRequestStateAwaitSendComplete); - } else { - client->err = UDS_ERR_BUFSIZ; - } - break; - } - case kRequestStateAwaitSendComplete: { - if (client->_options_copy & UDS_FUNCTIONAL) { - // "The Functional addressing is applied only to single frame transmission" - // Specification of Diagnostic Communication (Diagnostic on CAN - Network Layer) - changeState(client, kRequestStateIdle); - } - if (tp_status & UDS_TP_SEND_IN_PROGRESS) { - ; // await send complete - } else { - if (client->_options_copy & UDS_SUPPRESS_POS_RESP) { - changeState(client, kRequestStateIdle); - } else { - changeState(client, kRequestStateAwaitResponse); - client->p2_timer = UDSMillis() + client->p2_ms; - } - } - break; - } - case kRequestStateAwaitResponse: { - UDSTpAddr_t ta_type = kTpAddrTypePhysical; - ssize_t ret = - client->tp->recv(client->tp, client->recv_buf, client->recv_buf_size, &ta_type); - - if (kTpAddrTypeFunctional == ta_type) { - break; - } - if (ret < 0) { - client->err = UDS_ERR_TPORT; - changeState(client, kRequestStateIdle); - } else if (0 == ret) { - if (UDSTimeAfter(UDSMillis(), client->p2_timer)) { - client->err = UDS_ERR_TIMEOUT; - changeState(client, kRequestStateIdle); - } - } else { - client->recv_size = ret; - changeState(client, kRequestStateProcessResponse); - } - break; - } - case kRequestStateProcessResponse: { - client->err = _ClientValidateResponse(client); - if (UDS_OK == client->err) { - _ClientHandleResponse(client); - } else { - changeState(client, kRequestStateIdle); - } - break; - } - - default: - assert(0); - } -} - -static UDSErr_t _SendRequest(UDSClient_t *client) { - client->_options_copy = client->options; - - if (client->_options_copy & UDS_SUPPRESS_POS_RESP) { - // UDS-1:2013 8.2.2 Table 11 - client->send_buf[1] |= 0x80; - } - - changeState(client, kRequestStateSending); - PollLowLevel(client); // poll once to begin sending immediately - return UDS_OK; -} - -#define PRE_REQUEST_CHECK() \ - if (kRequestStateIdle != client->state) { \ - return UDS_ERR_BUSY; \ - } \ - clearRequestContext(client); - -UDSErr_t UDSSendECUReset(UDSClient_t *client, UDSECUReset_t type) { - PRE_REQUEST_CHECK(); - client->send_buf[0] = kSID_ECU_RESET; - client->send_buf[1] = type; - client->send_size = 2; - return _SendRequest(client); -} - -UDSErr_t UDSSendDiagSessCtrl(UDSClient_t *client, enum UDSDiagnosticSessionType mode) { - PRE_REQUEST_CHECK(); - client->send_buf[0] = kSID_DIAGNOSTIC_SESSION_CONTROL; - client->send_buf[1] = mode; - client->send_size = 2; - return _SendRequest(client); -} - -UDSErr_t UDSSendCommCtrl(UDSClient_t *client, enum UDSCommunicationControlType ctrl, - enum UDSCommunicationType comm) { - PRE_REQUEST_CHECK(); - client->send_buf[0] = kSID_COMMUNICATION_CONTROL; - client->send_buf[1] = ctrl; - client->send_buf[2] = comm; - client->send_size = 3; - return _SendRequest(client); -} - -UDSErr_t UDSSendTesterPresent(UDSClient_t *client) { - PRE_REQUEST_CHECK(); - client->send_buf[0] = kSID_TESTER_PRESENT; - client->send_buf[1] = 0; - client->send_size = 2; - return _SendRequest(client); -} - -UDSErr_t UDSSendRDBI(UDSClient_t *client, const uint16_t *didList, - const uint16_t numDataIdentifiers) { - PRE_REQUEST_CHECK(); - assert(didList); - assert(numDataIdentifiers); - client->send_buf[0] = kSID_READ_DATA_BY_IDENTIFIER; - for (int i = 0; i < numDataIdentifiers; i++) { - uint16_t offset = 1 + sizeof(uint16_t) * i; - if (offset + 2 > client->send_buf_size) { - return UDS_ERR_INVALID_ARG; - } - (client->send_buf + offset)[0] = (didList[i] & 0xFF00) >> 8; - (client->send_buf + offset)[1] = (didList[i] & 0xFF); - } - client->send_size = 1 + (numDataIdentifiers * sizeof(uint16_t)); - return _SendRequest(client); -} - -UDSErr_t UDSSendWDBI(UDSClient_t *client, uint16_t dataIdentifier, const uint8_t *data, - uint16_t size) { - PRE_REQUEST_CHECK(); - assert(data); - assert(size); - client->send_buf[0] = kSID_WRITE_DATA_BY_IDENTIFIER; - if (client->send_buf_size <= 3 || size > client->send_buf_size - 3) { - return UDS_ERR_BUFSIZ; - } - client->send_buf[1] = (dataIdentifier & 0xFF00) >> 8; - client->send_buf[2] = (dataIdentifier & 0xFF); - memmove(&client->send_buf[3], data, size); - client->send_size = 3 + size; - return _SendRequest(client); -} - -/** - * @brief RoutineControl - * - * @param client - * @param type - * @param routineIdentifier - * @param data - * @param size - * @return UDSErr_t - * @addtogroup routineControl_0x31 - */ -UDSErr_t UDSSendRoutineCtrl(UDSClient_t *client, enum RoutineControlType type, - uint16_t routineIdentifier, const uint8_t *data, uint16_t size) { - PRE_REQUEST_CHECK(); - client->send_buf[0] = kSID_ROUTINE_CONTROL; - client->send_buf[1] = type; - client->send_buf[2] = routineIdentifier >> 8; - client->send_buf[3] = routineIdentifier; - if (size) { - assert(data); - if (size > client->send_buf_size - UDS_0X31_REQ_MIN_LEN) { - return UDS_ERR_BUFSIZ; - } - memmove(&client->send_buf[UDS_0X31_REQ_MIN_LEN], data, size); - } else { - assert(NULL == data); - } - client->send_size = UDS_0X31_REQ_MIN_LEN + size; - return _SendRequest(client); -} - -/** - * @brief - * - * @param client - * @param dataFormatIdentifier - * @param addressAndLengthFormatIdentifier - * @param memoryAddress - * @param memorySize - * @return UDSErr_t - * @addtogroup requestDownload_0x34 - */ -UDSErr_t UDSSendRequestDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, - uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, - size_t memorySize) { - PRE_REQUEST_CHECK(); - uint8_t numMemorySizeBytes = (addressAndLengthFormatIdentifier & 0xF0) >> 4; - uint8_t numMemoryAddressBytes = addressAndLengthFormatIdentifier & 0x0F; - - client->send_buf[0] = kSID_REQUEST_DOWNLOAD; - client->send_buf[1] = dataFormatIdentifier; - client->send_buf[2] = addressAndLengthFormatIdentifier; - - uint8_t *ptr = &client->send_buf[UDS_0X34_REQ_BASE_LEN]; - - for (int i = numMemoryAddressBytes - 1; i >= 0; i--) { - *ptr = (memoryAddress & (0xFF << (8 * i))) >> (8 * i); - ptr++; - } - - for (int i = numMemorySizeBytes - 1; i >= 0; i--) { - *ptr = (memorySize & (0xFF << (8 * i))) >> (8 * i); - ptr++; - } - - client->send_size = UDS_0X34_REQ_BASE_LEN + numMemoryAddressBytes + numMemorySizeBytes; - return _SendRequest(client); -} - -/** - * @brief - * - * @param client - * @param dataFormatIdentifier - * @param addressAndLengthFormatIdentifier - * @param memoryAddress - * @param memorySize - * @return UDSErr_t - * @addtogroup requestDownload_0x35 - */ -UDSErr_t UDSSendRequestUpload(UDSClient_t *client, uint8_t dataFormatIdentifier, - uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, - size_t memorySize) { - PRE_REQUEST_CHECK(); - uint8_t numMemorySizeBytes = (addressAndLengthFormatIdentifier & 0xF0) >> 4; - uint8_t numMemoryAddressBytes = addressAndLengthFormatIdentifier & 0x0F; - - client->send_buf[0] = kSID_REQUEST_UPLOAD; - client->send_buf[1] = dataFormatIdentifier; - client->send_buf[2] = addressAndLengthFormatIdentifier; - - uint8_t *ptr = &client->send_buf[UDS_0X35_REQ_BASE_LEN]; - - for (int i = numMemoryAddressBytes - 1; i >= 0; i--) { - *ptr = (memoryAddress & (0xFF << (8 * i))) >> (8 * i); - ptr++; - } - - for (int i = numMemorySizeBytes - 1; i >= 0; i--) { - *ptr = (memorySize & (0xFF << (8 * i))) >> (8 * i); - ptr++; - } - - client->send_size = UDS_0X35_REQ_BASE_LEN + numMemoryAddressBytes + numMemorySizeBytes; - return _SendRequest(client); -} - -/** - * @brief - * - * @param client - * @param blockSequenceCounter - * @param blockLength - * @param fd - * @return UDSErr_t - * @addtogroup transferData_0x36 - */ -UDSErr_t UDSSendTransferData(UDSClient_t *client, uint8_t blockSequenceCounter, - const uint16_t blockLength, const uint8_t *data, uint16_t size) { - PRE_REQUEST_CHECK(); - assert(blockLength > 2); // blockLength must include SID and sequenceCounter - assert(size + 2 <= blockLength); // data must fit inside blockLength - 2 - client->send_buf[0] = kSID_TRANSFER_DATA; - client->send_buf[1] = blockSequenceCounter; - memmove(&client->send_buf[UDS_0X36_REQ_BASE_LEN], data, size); - UDS_DBG_PRINT("size: %d, blocklength: %d\n", size, blockLength); - client->send_size = UDS_0X36_REQ_BASE_LEN + size; - return _SendRequest(client); -} - -UDSErr_t UDSSendTransferDataStream(UDSClient_t *client, uint8_t blockSequenceCounter, - const uint16_t blockLength, FILE *fd) { - PRE_REQUEST_CHECK(); - assert(blockLength > 2); // blockLength must include SID and sequenceCounter - client->send_buf[0] = kSID_TRANSFER_DATA; - client->send_buf[1] = blockSequenceCounter; - - uint16_t size = fread(&client->send_buf[2], 1, blockLength - 2, fd); - UDS_DBG_PRINT("size: %d, blocklength: %d\n", size, blockLength); - client->send_size = UDS_0X36_REQ_BASE_LEN + size; - return _SendRequest(client); -} - -/** - * @brief - * - * @param client - * @return UDSErr_t - * @addtogroup requestTransferExit_0x37 - */ -UDSErr_t UDSSendRequestTransferExit(UDSClient_t *client) { - PRE_REQUEST_CHECK(); - client->send_buf[0] = kSID_REQUEST_TRANSFER_EXIT; - client->send_size = 1; - return _SendRequest(client); -} - -/** - * @brief - * - * @param client - * @param dtcSettingType - * @param data - * @param size - * @return UDSErr_t - * @addtogroup controlDTCSetting_0x85 - */ -UDSErr_t UDSCtrlDTCSetting(UDSClient_t *client, uint8_t dtcSettingType, uint8_t *data, - uint16_t size) { - PRE_REQUEST_CHECK(); - if (0x00 == dtcSettingType || 0x7F == dtcSettingType || - (0x03 <= dtcSettingType && dtcSettingType <= 0x3F)) { - assert(0); // reserved vals - } - client->send_buf[0] = kSID_CONTROL_DTC_SETTING; - client->send_buf[1] = dtcSettingType; - - if (NULL == data) { - assert(size == 0); - } else { - assert(size > 0); - if (size > client->send_buf_size - 2) { - return UDS_ERR_BUFSIZ; - } - memmove(&client->send_buf[2], data, size); - } - client->send_size = 2 + size; - return _SendRequest(client); -} - -/** - * @brief - * - * @param client - * @param level - * @param data - * @param size - * @return UDSErr_t - * @addtogroup securityAccess_0x27 - */ -UDSErr_t UDSSendSecurityAccess(UDSClient_t *client, uint8_t level, uint8_t *data, uint16_t size) { - PRE_REQUEST_CHECK(); - if (UDSSecurityAccessLevelIsReserved(level)) { - return UDS_ERR_INVALID_ARG; - } - client->send_buf[0] = kSID_SECURITY_ACCESS; - client->send_buf[1] = level; - if (size) { - assert(data); - if (size > client->send_buf_size - UDS_0X27_REQ_BASE_LEN) { - return UDS_ERR_BUFSIZ; - } - } else { - assert(NULL == data); - } - - memmove(&client->send_buf[UDS_0X27_REQ_BASE_LEN], data, size); - client->send_size = UDS_0X27_REQ_BASE_LEN + size; - return _SendRequest(client); -} - -typedef struct { - uint8_t dataFormatIdentifier; - uint8_t addressAndLengthFormatIdentifier; - size_t memoryAddress; - size_t memorySize; - FILE *fd; - uint8_t blockSequenceCounter; - uint16_t blockLength; -} UDSClientDownloadSequence_t; - -static UDSSeqState_t requestDownload(UDSClient_t *client) { - UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; - UDSSendRequestDownload(client, pL_Seq->dataFormatIdentifier, - pL_Seq->addressAndLengthFormatIdentifier, pL_Seq->memoryAddress, - pL_Seq->memorySize); - return UDSSeqStateGotoNext; -} - -static UDSSeqState_t checkRequestDownloadResponse(UDSClient_t *client) { - UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; - struct RequestDownloadResponse resp = {0}; - UDSErr_t err = UDSUnpackRequestDownloadResponse(client, &resp); - if (err) { - client->err = err; - return UDSSeqStateDone; - } - pL_Seq->blockLength = resp.maxNumberOfBlockLength; - if (0 == resp.maxNumberOfBlockLength) { - client->err = UDS_ERR; - return UDSSeqStateDone; - } - return UDSSeqStateGotoNext; -} - -static UDSSeqState_t prepareToTransfer(UDSClient_t *client) { - UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; - pL_Seq->blockSequenceCounter = 1; - return UDSSeqStateGotoNext; -} - -static UDSSeqState_t transferData(UDSClient_t *client) { - UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; - if (kRequestStateIdle == client->state) { - if (ferror(pL_Seq->fd)) { - fclose(pL_Seq->fd); - client->err = UDS_ERR_FILE_IO; // 读取文件故障 - return UDSSeqStateDone; - } else if (feof(pL_Seq->fd)) { // 传完了 - return UDSSeqStateGotoNext; - } else { - UDSSendTransferDataStream(client, pL_Seq->blockSequenceCounter++, pL_Seq->blockLength, - pL_Seq->fd); - } - } - return UDSSeqStateRunning; -} - -static UDSSeqState_t requestTransferExit(UDSClient_t *client) { - UDSSendRequestTransferExit(client); - return UDSSeqStateGotoNext; -} - -UDSErr_t UDSConfigDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, - uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, - size_t memorySize, FILE *fd) { - - static const UDSClientCallback callbacks[] = { - requestDownload, UDSClientAwaitIdle, checkRequestDownloadResponse, prepareToTransfer, - transferData, requestTransferExit, UDSClientAwaitIdle, NULL}; - static UDSClientDownloadSequence_t seq = {0}; - memset(&seq, 0, sizeof(seq)); - seq.blockSequenceCounter = 1; - seq.dataFormatIdentifier = dataFormatIdentifier; - seq.addressAndLengthFormatIdentifier = addressAndLengthFormatIdentifier; - seq.memoryAddress = memoryAddress; - seq.memorySize = memorySize; - seq.fd = fd; - client->cbList = callbacks; - client->cbIdx = 0; - client->cbData = &seq; - return UDS_OK; -} - -/** - * @brief - * - * @param client - * @param resp - * @return UDSErr_t - * @addtogroup securityAccess_0x27 - */ -UDSErr_t UDSUnpackSecurityAccessResponse(const UDSClient_t *client, - struct SecurityAccessResponse *resp) { - assert(client); - assert(resp); - if (UDS_RESPONSE_SID_OF(kSID_SECURITY_ACCESS) != client->recv_buf[0]) { - return UDS_ERR_SID_MISMATCH; - } - if (client->recv_size < UDS_0X27_RESP_BASE_LEN) { - return UDS_ERR_RESP_TOO_SHORT; - } - resp->securityAccessType = client->recv_buf[1]; - resp->securitySeedLength = client->recv_size - UDS_0X27_RESP_BASE_LEN; - resp->securitySeed = resp->securitySeedLength == 0 ? NULL : &client->recv_buf[2]; - return UDS_OK; -} - -/** - * @brief - * - * @param client - * @param resp - * @return UDSErr_t - * @addtogroup routineControl_0x31 - */ -UDSErr_t UDSUnpackRoutineControlResponse(const UDSClient_t *client, - struct RoutineControlResponse *resp) { - assert(client); - assert(resp); - if (UDS_RESPONSE_SID_OF(kSID_ROUTINE_CONTROL) != client->recv_buf[0]) { - return UDS_ERR_SID_MISMATCH; - } - if (client->recv_size < UDS_0X31_RESP_MIN_LEN) { - return UDS_ERR_RESP_TOO_SHORT; - } - resp->routineControlType = client->recv_buf[1]; - resp->routineIdentifier = (client->recv_buf[2] << 8) + client->recv_buf[3]; - resp->routineStatusRecordLength = client->recv_size - UDS_0X31_RESP_MIN_LEN; - resp->routineStatusRecord = - resp->routineStatusRecordLength == 0 ? NULL : &client->recv_buf[UDS_0X31_RESP_MIN_LEN]; - return UDS_OK; -} - -/** - * @brief - * - * @param client - * @param resp - * @return UDSErr_t - * @addtogroup requestDownload_0x34 - */ -UDSErr_t UDSUnpackRequestDownloadResponse(const UDSClient_t *client, - struct RequestDownloadResponse *resp) { - assert(client); - assert(resp); - if (UDS_RESPONSE_SID_OF(kSID_REQUEST_DOWNLOAD) != client->recv_buf[0]) { - return UDS_ERR_SID_MISMATCH; - } - if (client->recv_size < UDS_0X34_RESP_BASE_LEN) { - return UDS_ERR_RESP_TOO_SHORT; - } - uint8_t maxNumberOfBlockLengthSize = (client->recv_buf[1] & 0xF0) >> 4; - - if (sizeof(resp->maxNumberOfBlockLength) < maxNumberOfBlockLengthSize) { - UDS_DBG_PRINT("WARNING: sizeof(maxNumberOfBlockLength) > sizeof(size_t)"); - return UDS_ERR; - } - resp->maxNumberOfBlockLength = 0; - for (int byteIdx = 0; byteIdx < maxNumberOfBlockLengthSize; byteIdx++) { - uint8_t byte = client->recv_buf[UDS_0X34_RESP_BASE_LEN + byteIdx]; - uint8_t shiftBytes = maxNumberOfBlockLengthSize - 1 - byteIdx; - resp->maxNumberOfBlockLength |= byte << (8 * shiftBytes); - } - return UDS_OK; -} - -bool UDSClientPoll(UDSClient_t *client) { - PollLowLevel(client); - - if (client->err) { - return UDS_CLIENT_IDLE; - } - - if (kRequestStateIdle != client->state) { - return UDS_CLIENT_RUNNING; - } - - if (NULL == client->cbList) { - return UDS_CLIENT_IDLE; - } - - UDSClientCallback activeCallback = client->cbList[client->cbIdx]; - - if (NULL == activeCallback) { - return UDS_CLIENT_IDLE; - } - - UDSSeqState_t state = activeCallback(client); - - switch (state) { - case UDSSeqStateDone: - return UDS_CLIENT_IDLE; - case UDSSeqStateRunning: - return UDS_CLIENT_RUNNING; - case UDSSeqStateGotoNext: { - client->cbIdx += 1; - return UDS_CLIENT_RUNNING; - } - default: - assert(0); - return UDS_CLIENT_IDLE; - } -} - -UDSSeqState_t UDSClientAwaitIdle(UDSClient_t *client) { - if (client->err) { - return UDSSeqStateDone; - } else if (kRequestStateIdle == client->state) { - return UDSSeqStateGotoNext; - } else { - return UDSSeqStateRunning; - } -} - -UDSErr_t UDSUnpackRDBIResponse(const UDSClient_t *client, uint16_t did, uint8_t *data, - uint16_t size, uint16_t *offset) { - assert(client); - assert(data); - assert(offset); - if (0 == *offset) { - *offset = UDS_0X22_RESP_BASE_LEN; - } - - if (*offset + sizeof(did) > client->recv_size) { - return UDS_ERR_RESP_TOO_SHORT; - } - - uint16_t theirDID = (client->recv_buf[*offset] << 8) + client->recv_buf[*offset + 1]; - if (theirDID != did) { - return UDS_ERR_DID_MISMATCH; - } - - if (*offset + sizeof(uint16_t) + size > client->recv_size) { - return UDS_ERR_RESP_TOO_SHORT; - } - - memmove(data, &client->recv_buf[*offset + sizeof(uint16_t)], size); - - *offset += sizeof(uint16_t) + size; - return UDS_OK; -} diff --git a/iso14229.h b/iso14229.h deleted file mode 100644 index 1323c96..0000000 --- a/iso14229.h +++ /dev/null @@ -1,715 +0,0 @@ -/** - * @file iso14229.h - * @brief ISO-14229 (UDS) server and client - * @author driftregion - * @version 0.6.0 - * @date 2022-12-08 - */ - -#ifndef ISO14229_H -#define ISO14229_H - -#ifdef __cplusplus -extern "C" { -#define _Static_assert static_assert -#endif - -#define UDS_ARCH_CUSTOM 0 -#define UDS_ARCH_UNIX 1 -#define UDS_ARCH_WINDOWS 2 - -#define UDS_TP_CUSTOM 0 -#define UDS_TP_ISOTP_C 1 -#define UDS_TP_ISOTP_SOCKET 2 - -#if !defined(UDS_ARCH) -#if defined(__unix__) || defined(__APPLE__) -#define UDS_ARCH UDS_ARCH_UNIX -#elif defined(_WIN32) -#define UDS_ARCH UDS_ARCH_WINDOWS -#else -#define UDS_ARCH UDS_ARCH_CUSTOM -#endif -#endif - -#if !defined(UDS_TP) -#if (UDS_ARCH == UDS_ARCH_UNIX) -#define UDS_TP UDS_TP_ISOTP_SOCKET -#endif -#endif - -#include -#include -#include -#include -#include - -#if (UDS_TP == UDS_TP_ISOTP_C) -#include "isotp-c/isotp.h" -#include "isotp-c/isotp_config.h" -#include "isotp-c/isotp_defines.h" -#include "isotp-c/isotp_user.h" -#elif (UDS_TP == UDS_TP_ISOTP_SOCKET) -#include -#include -#include -#include -#include -#include -#include -#include -#include -#endif - -#if (UDS_ARCH == UDS_ARCH_UNIX) -#include -#include -#include -#elif (UDS_ARCH == UDS_ARCH_WINDOWS) -#include -typedef SSIZE_T ssize_t; -#endif - -/** ISO-TP Maximum Transmissiable Unit (ISO-15764-2-2004 section 5.3.3) */ -#define UDS_ISOTP_MTU (4095) - -#ifndef UDS_TP_MTU -#define UDS_TP_MTU UDS_ISOTP_MTU -#endif - -/** Default buffer size */ -#define UDS_BUFSIZE UDS_TP_MTU - -/* -provide a debug function with -DUDS_DBG_PRINT=printf when compiling this -library -*/ -#ifndef UDS_DBG_PRINT -#define UDS_DBG_PRINT(fmt, ...) ((void)fmt) -#endif - -#define UDS_DBG_PRINTHEX(addr, len) \ - for (int i = 0; i < len; i++) { \ - UDS_DBG_PRINT("%02x,", ((uint8_t *)addr)[i]); \ - } \ - UDS_DBG_PRINT("\n"); - -typedef enum { - UDS_ERR = -1, // 通用错误 - UDS_OK = 0, // 成功 - UDS_ERR_TIMEOUT, // 请求超时 - UDS_ERR_NEG_RESP, // 否定响应 - UDS_ERR_DID_MISMATCH, // 响应DID对不上期待的DID - UDS_ERR_SID_MISMATCH, // 请求和响应SID对不上 - UDS_ERR_SUBFUNCTION_MISMATCH, // 请求和响应SubFunction对不上 - UDS_ERR_TPORT, // 传输层错误 - UDS_ERR_FILE_IO, // 文件IO错误 - UDS_ERR_RESP_TOO_SHORT, // 响应太短 - UDS_ERR_BUFSIZ, // 缓冲器不够大 - UDS_ERR_INVALID_ARG, // 参数不对、没发 - UDS_ERR_BUSY, // 正在忙、没发 -} UDSErr_t; - -typedef enum { - UDSSeqStateDone = 0, - UDSSeqStateRunning = 1, - UDSSeqStateGotoNext = 2, -} UDSSeqState_t; - -enum UDSDiagnosticSessionType { - kDefaultSession = 0x01, - kProgrammingSession = 0x02, - kExtendedDiagnostic = 0x03, - kSafetySystemDiagnostic = 0x04, -}; - -enum { - kPositiveResponse = 0, - kGeneralReject = 0x10, - kServiceNotSupported = 0x11, - kSubFunctionNotSupported = 0x12, - kIncorrectMessageLengthOrInvalidFormat = 0x13, - kResponseTooLong = 0x14, - kBusyRepeatRequest = 0x21, - kConditionsNotCorrect = 0x22, - kRequestSequenceError = 0x24, - kNoResponseFromSubnetComponent = 0x25, - kFailurePreventsExecutionOfRequestedAction = 0x26, - kRequestOutOfRange = 0x31, - kSecurityAccessDenied = 0x33, - kInvalidKey = 0x35, - kExceedNumberOfAttempts = 0x36, - kRequiredTimeDelayNotExpired = 0x37, - kUploadDownloadNotAccepted = 0x70, - kTransferDataSuspended = 0x71, - kGeneralProgrammingFailure = 0x72, - kWrongBlockSequenceCounter = 0x73, - kRequestCorrectlyReceived_ResponsePending = 0x78, - kSubFunctionNotSupportedInActiveSession = 0x7E, - kServiceNotSupportedInActiveSession = 0x7F, - kRpmTooHigh = 0x81, - kRpmTooLow = 0x82, - kEngineIsRunning = 0x83, - kEngineIsNotRunning = 0x84, - kEngineRunTimeTooLow = 0x85, - kTemperatureTooHigh = 0x86, - kTemperatureTooLow = 0x87, - kVehicleSpeedTooHigh = 0x88, - kVehicleSpeedTooLow = 0x89, - kThrottlePedalTooHigh = 0x8A, - kThrottlePedalTooLow = 0x8B, - kTransmissionRangeNotInNeutral = 0x8C, - kTransmissionRangeNotInGear = 0x8D, - kISOSAEReserved = 0x8E, - kBrakeSwitchNotClosed = 0x8F, - kShifterLeverNotInPark = 0x90, - kTorqueConverterClutchLocked = 0x91, - kVoltageTooHigh = 0x92, - kVoltageTooLow = 0x93, -}; - -/** - * @brief LEV_RT_ - * @addtogroup ecuReset_0x11 - */ -enum UDSECUResetType { - kHardReset = 1, - kKeyOffOnReset = 2, - kSoftReset = 3, - kEnableRapidPowerShutDown = 4, - kDisableRapidPowerShutDown = 5, -}; - -typedef uint8_t UDSECUReset_t; - -/** - * @addtogroup securityAccess_0x27 - */ -enum UDSSecurityAccessType { - kRequestSeed = 0x01, - kSendKey = 0x02, -}; - -/** - * @addtogroup communicationControl_0x28 - */ -enum UDSCommunicationControlType { - kEnableRxAndTx = 0, - kEnableRxAndDisableTx = 1, - kDisableRxAndEnableTx = 2, - kDisableRxAndTx = 3, -}; - -/** - * @addtogroup communicationControl_0x28 - */ -enum UDSCommunicationType { - kNormalCommunicationMessages = 0x1, - kNetworkManagementCommunicationMessages = 0x2, - kNetworkManagementCommunicationMessagesAndNormalCommunicationMessages = 0x3, -}; - -/** - * @addtogroup routineControl_0x31 - */ -enum RoutineControlType { - kStartRoutine = 1, - kStopRoutine = 2, - kRequestRoutineResults = 3, -}; - -/** - * @addtogroup controlDTCSetting_0x85 - */ -enum DTCSettingType { - kDTCSettingON = 0x01, - kDTCSettingOFF = 0x02, -}; - -enum UDSTpAddr { - kTpAddrTypePhysical = 0, // 1:1 - kTpAddrTypeFunctional, // 1:many -}; - -typedef uint8_t UDSTpAddr_t; - -enum UDSTpStatusFlags { - UDS_TP_IDLE = 0x00000000, - UDS_TP_SEND_IN_PROGRESS = 0x00000001, -}; - -typedef uint32_t UDSTpStatus_t; - -/** - * @brief 传输层把柄 - */ -typedef struct UDSTpHandle { - /** - * @brief 接收 - * @param hdl: pointer to transport handle - * @param buf: if data is available, it will be copied here - * @param count: the implementation should not copy more than count bytes into buf - * @param ta_type: the addressing type of the data received or error encountered is written to - * this location. If no data is received, nothing is written - * @return 接收了多少个字节。 - * < 0 故障 - * == 0 没有数据 - * > 0 接收了、返回值是大小 - */ - ssize_t (*recv)(struct UDSTpHandle *hdl, void *buf, size_t count, UDSTpAddr_t *ta_type); - /** - * @brief 发送 - * @param hdl: pointer to transport handle - * @param buf: pointer to data to be sent - * @param count: number of bytes to be sent - * @param ta_type: the addressing type to use - * @return 发送了多少个字节。 - * < 0 故障 - * >= 0 发送成功了 - */ - ssize_t (*send)(struct UDSTpHandle *hdl, const void *buf, size_t count, UDSTpAddr_t ta_type); - /** - * @brief 轮询 - */ - UDSTpStatus_t (*poll)(struct UDSTpHandle *hdl); -} UDSTpHandle_t; - -#if UDS_TP == UDS_TP_ISOTP_C -typedef struct { - UDSTpHandle_t hdl; - IsoTpLink phys_link; - IsoTpLink func_link; - uint8_t func_recv_buf[8]; - uint8_t func_send_buf[8]; -} UDSTpIsoTpC_t; -#elif UDS_TP == UDS_TP_ISOTP_SOCKET -typedef struct { - UDSTpHandle_t hdl; - int phys_fd; - int func_fd; -} UDSTpLinuxIsoTp_t; -#endif - -// ======================================================================== -// Utility Functions -// ======================================================================== - -/* returns true if `a` is after `b` */ -static inline bool UDSTimeAfter(uint32_t a, uint32_t b) { - return ((int32_t)((int32_t)(b) - (int32_t)(a)) < 0); -} - -/** - * @brief \~chinese 用户定义获取时间(毫秒)回调函数 \~english user-provided function that - * returns the current time in milliseconds \~ - */ -uint32_t UDSMillis(); - -// ======================================================================== -// Client -// ======================================================================== - -#ifndef UDS_CLIENT_DEFAULT_P2_MS -#define UDS_CLIENT_DEFAULT_P2_MS (150U) -#endif - -#ifndef UDS_CLIENT_DEFAULT_P2_STAR_MS -#define UDS_CLIENT_DEFAULT_P2_STAR_MS (1500U) -#endif - -_Static_assert(UDS_CLIENT_DEFAULT_P2_STAR_MS > UDS_CLIENT_DEFAULT_P2_MS, ""); - -enum UDSClientRequestState { - kRequestStateIdle = 0, // 完成 - kRequestStateSending, // 传输层现在传输数据 - kRequestStateAwaitSendComplete, // 等待传输发送完成 - kRequestStateAwaitResponse, // 等待响应 - kRequestStateProcessResponse, // 处理响应 -}; - -typedef uint8_t UDSClientRequestState_t; - -typedef struct { -#if UDS_TP == UDS_TP_CUSTOM - UDSTpHandle_t *tp; -#elif UDS_TP == UDS_TP_ISOTP_C - uint32_t phys_recv_id; - uint32_t phys_send_id; - uint32_t func_send_id; -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - const char *if_name; - uint32_t phys_recv_id; - uint32_t phys_send_id; - uint32_t func_send_id; -#else -#error "transport undefined" -#endif -} UDSClientConfig_t; - -enum UDSClientOptions { - UDS_SUPPRESS_POS_RESP = 0x1, // 服务器不应该发送肯定响应 - UDS_FUNCTIONAL = 0x2, // 发功能请求 - UDS_NEG_RESP_IS_ERR = 0x4, // 否定响应是属于故障 - UDS_IGNORE_SRV_TIMINGS = 0x8, // 忽略服务器给的p2和p2_star -}; - -struct UDSClient; - -typedef UDSSeqState_t (*UDSClientCallback)(struct UDSClient *client); - -typedef struct UDSClient { - uint16_t p2_ms; // p2 超时时间 - uint32_t p2_star_ms; // 0x78 p2* 超时时间 - UDSTpHandle_t *tp; - - // 内状态 - uint32_t p2_timer; - uint8_t recv_buf[UDS_BUFSIZE]; - uint8_t send_buf[UDS_BUFSIZE]; - uint16_t recv_buf_size; - uint16_t send_buf_size; - uint16_t recv_size; - uint16_t send_size; - UDSErr_t err; - UDSClientRequestState_t state; - - uint8_t options; // enum udsclientoptions - uint8_t defaultOptions; // enum udsclientoptions - // a copy of the options at the time a request is made - uint8_t _options_copy; // enum udsclientoptions - - const UDSClientCallback *cbList; // null-terminated list of callback functions - size_t cbIdx; // index of currently active callback function - void *cbData; // a pointer to data available to callbacks - -#if UDS_TP == UDS_TP_CUSTOM -#elif UDS_TP == UDS_TP_ISOTP_C - UDSTpIsoTpC_t tp_impl; -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - UDSTpLinuxIsoTp_t tp_impl; -#endif - -} UDSClient_t; - -struct SecurityAccessResponse { - uint8_t securityAccessType; - const uint8_t *securitySeed; - uint16_t securitySeedLength; -}; - -struct RequestDownloadResponse { - size_t maxNumberOfBlockLength; -}; - -struct RoutineControlResponse { - uint8_t routineControlType; - uint16_t routineIdentifier; - const uint8_t *routineStatusRecord; - uint16_t routineStatusRecordLength; -}; - -UDSErr_t UDSClientInit(UDSClient_t *client, const UDSClientConfig_t *cfg); - -void UDSClientDeInit(UDSClient_t *client); - -#define UDS_CLIENT_IDLE (0) -#define UDS_CLIENT_RUNNING (1) - -/** - * @brief poll the client (call this in a loop) - * @param client - * @return UDS_CLIENT_IDLE if idle, otherwise UDS_CLIENT_RUNNING - */ -bool UDSClientPoll(UDSClient_t *client); - -UDSErr_t UDSSendECUReset(UDSClient_t *client, UDSECUReset_t type); -UDSErr_t UDSSendDiagSessCtrl(UDSClient_t *client, enum UDSDiagnosticSessionType mode); -UDSErr_t UDSSendSecurityAccess(UDSClient_t *client, uint8_t level, uint8_t *data, uint16_t size); -UDSErr_t UDSSendCommCtrl(UDSClient_t *client, enum UDSCommunicationControlType ctrl, - enum UDSCommunicationType comm); -UDSErr_t UDSSendRDBI(UDSClient_t *client, const uint16_t *didList, - const uint16_t numDataIdentifiers); -UDSErr_t UDSSendWDBI(UDSClient_t *client, uint16_t dataIdentifier, const uint8_t *data, - uint16_t size); -UDSErr_t UDSSendTesterPresent(UDSClient_t *client); -UDSErr_t UDSSendRoutineCtrl(UDSClient_t *client, enum RoutineControlType type, - uint16_t routineIdentifier, const uint8_t *data, uint16_t size); - -UDSErr_t UDSSendRequestDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, - uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, - size_t memorySize); - -UDSErr_t UDSSendRequestUpload(UDSClient_t *client, uint8_t dataFormatIdentifier, - uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, - size_t memorySize); -UDSErr_t UDSSendTransferData(UDSClient_t *client, uint8_t blockSequenceCounter, - const uint16_t blockLength, const uint8_t *data, uint16_t size); -UDSErr_t UDSSendTransferDataStream(UDSClient_t *client, uint8_t blockSequenceCounter, - const uint16_t blockLength, FILE *fd); -UDSErr_t UDSSendRequestTransferExit(UDSClient_t *client); - -UDSErr_t UDSCtrlDTCSetting(UDSClient_t *client, uint8_t dtcSettingType, - uint8_t *dtcSettingControlOptionRecord, uint16_t len); -UDSErr_t UDSUnpackRDBIResponse(const UDSClient_t *client, uint16_t did, uint8_t *data, - uint16_t size, uint16_t *offset); -UDSErr_t UDSUnpackSecurityAccessResponse(const UDSClient_t *client, - struct SecurityAccessResponse *resp); -UDSErr_t UDSUnpackRequestDownloadResponse(const UDSClient_t *client, - struct RequestDownloadResponse *resp); -UDSErr_t UDSUnpackRoutineControlResponse(const UDSClient_t *client, - struct RoutineControlResponse *resp); - -/** - * @brief Wait after request transmission for a response to be received - * @note if suppressPositiveResponse is set, this function will return - UDSSeqStateGotoNext as soon as the transport layer has completed transmission. - * - * @param client - * @param args - * @return UDSErr_t - - UDSSeqStateDone -- 流程完成 - - UDSSeqStateRunning -- 流程正在跑、还没完成 - */ -UDSSeqState_t UDSClientAwaitIdle(UDSClient_t *client); - -UDSErr_t UDSConfigDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, - uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, - size_t memorySize, FILE *fd); - -// ======================================================================== -// Server -// ======================================================================== - -#ifndef UDS_SERVER_DEFAULT_POWER_DOWN_TIME_MS -#define UDS_SERVER_DEFAULT_POWER_DOWN_TIME_MS (10) -#endif - -#ifndef UDS_SERVER_DEFAULT_P2_MS -#define UDS_SERVER_DEFAULT_P2_MS (50) -#endif - -#ifndef UDS_SERVER_DEFAULT_P2_STAR_MS -#define UDS_SERVER_DEFAULT_P2_STAR_MS (2000) -#endif - -#ifndef UDS_SERVER_DEFAULT_S3_MS -#define UDS_SERVER_DEFAULT_S3_MS (3000) -#endif - -_Static_assert(0 < UDS_SERVER_DEFAULT_P2_MS && - UDS_SERVER_DEFAULT_P2_MS < UDS_SERVER_DEFAULT_P2_STAR_MS && - UDS_SERVER_DEFAULT_P2_STAR_MS < UDS_SERVER_DEFAULT_S3_MS, - ""); - -#ifndef UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH -/*! ISO14229-1:2013 Table 396. This parameter is used by the requestDownload positive response -message to inform the client how many data bytes (maxNumberOfBlockLength) to include in each -TransferData request message from the client. */ -#define UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH (UDS_BUFSIZE) -#endif - -enum UDSServerEvent { - UDS_SRV_EVT_DiagSessCtrl, // UDSDiagSessCtrlArgs_t * - UDS_SRV_EVT_EcuReset, // UDSECUResetArgs_t * - UDS_SRV_EVT_ReadDataByIdent, // UDSRDBIArgs_t * - UDS_SRV_EVT_ReadMemByAddr, // UDSReadMemByAddrArgs_t * - UDS_SRV_EVT_CommCtrl, // UDSCommCtrlArgs_t * - UDS_SRV_EVT_SecAccessRequestSeed, // UDSSecAccessRequestSeedArgs_t * - UDS_SRV_EVT_SecAccessValidateKey, // UDSSecAccessValidateKeyArgs_t * - UDS_SRV_EVT_WriteDataByIdent, // UDSWDBIArgs_t * - UDS_SRV_EVT_RoutineCtrl, // UDSRoutineCtrlArgs_t* - UDS_SRV_EVT_RequestDownload, // UDSRequestDownloadArgs_t* - UDS_SRV_EVT_RequestUpload, // UDSRequestUploadArgs_t * - UDS_SRV_EVT_TransferData, // UDSTransferDataArgs_t * - UDS_SRV_EVT_RequestTransferExit, // UDSRequestTransferExitArgs_t * - UDS_SRV_EVT_SessionTimeout, // NULL - UDS_SRV_EVT_DoScheduledReset, // enum UDSEcuResetType * -}; - -typedef int UDSServerEvent_t; - -typedef struct UDSServer { - UDSTpHandle_t *tp; - uint8_t (*fn)(struct UDSServer *srv, UDSServerEvent_t event, const void *arg); - uint8_t recv_buf[UDS_BUFSIZE]; - uint8_t send_buf[UDS_BUFSIZE]; - uint16_t recv_size; - uint16_t send_size; - uint16_t recv_buf_size; - uint16_t send_buf_size; - - /** - * @brief \~chinese 服务器时间参数(毫秒) \~ Server time constants (milliseconds) \~ - */ - uint16_t p2_ms; // Default P2_server_max timing supported by the server for - // the activated diagnostic session. - uint32_t p2_star_ms; // Enhanced (NRC 0x78) P2_server_max supported by the - // server for the activated diagnostic session. - uint16_t s3_ms; // Session timeout - - uint8_t ecuResetScheduled; // nonzero indicates that an ECUReset has been scheduled - uint32_t ecuResetTimer; // for delaying resetting until a response - // has been sent to the client - uint32_t p2_timer; // for rate limiting server responses - uint32_t s3_session_timeout_timer; // for knowing when the diagnostic - // session has timed out - - /** - * @brief UDS-1-2013: Table 407 - 0x36 TransferData Supported negative - * response codes requires that the server keep track of whether the - * transfer is active - */ - bool xferIsActive; - // UDS-1-2013: 14.4.2.3, Table 404: The blockSequenceCounter parameter - // value starts at 0x01 - uint8_t xferBlockSequenceCounter; - size_t xferTotalBytes; // total transfer size in bytes requested by the client - size_t xferByteCounter; // total number of bytes transferred - size_t xferBlockLength; // block length (convenience for the TransferData API) - - /** - * @brief public subset of server state for user handlers - */ - uint8_t sessionType; - uint8_t securityLevel; // Current SecurityAccess (0x27) level - // this variable set to true when a user handler returns 0x78 - // requestCorrectlyReceivedResponsePending. After a response has been sent on the transport - // layer, this variable is set to false and the user handler will be called again. It is the - // responsibility of the user handler to track the call count. - bool RCRRP; - - // UDS-1 2013 defines the following conditions under which the server does not - // process incoming requests: - // - not ready to receive (Table A.1 0x78) - // - not accepting request messages and not sending responses (9.3.1) - // - // when this variable is set to true, incoming ISO-TP data will not be processed. - bool notReadyToReceive; - -#if UDS_TP == UDS_TP_CUSTOM -#elif UDS_TP == UDS_TP_ISOTP_C - UDSTpIsoTpC_t tp_impl; -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - UDSTpLinuxIsoTp_t tp_impl; -#endif -} UDSServer_t; - -typedef struct { - uint8_t (*fn)(UDSServer_t *srv, UDSServerEvent_t event, const void *arg); -#if UDS_TP == UDS_TP_CUSTOM - UDSTpHandle_t *tp; -#elif UDS_TP == UDS_TP_ISOTP_C - uint32_t phys_send_id; - uint32_t phys_recv_id; - uint32_t func_recv_id; -#elif UDS_TP == UDS_TP_ISOTP_SOCKET - const char *if_name; - uint32_t phys_send_id; - uint32_t phys_recv_id; - uint32_t func_recv_id; -#else -#error "transport undefined" -#endif -} UDSServerConfig_t; - -typedef struct { - const uint8_t type; /*! requested diagnostic session type (enum UDSDiagnosticSessionType) */ - uint16_t p2_ms; /*! optional: p2 timing override */ - uint32_t p2_star_ms; /*! optional: p2* timing override */ -} UDSDiagSessCtrlArgs_t; - -typedef struct { - const uint8_t type; /**< \~chinese 客户端请求的复位类型 \~english reset type requested by client - (enum UDSECUResetType) */ - uint32_t powerDownTimeMillis; /**< when this much time has elapsed after a kPositiveResponse, a - UDS_SRV_EVT_DoScheduledReset will be issued */ -} UDSECUResetArgs_t; - -typedef struct { - const uint16_t dataId; /*! RDBI Data Identifier */ - uint8_t (*copy)(UDSServer_t *srv, const void *src, - uint16_t count); /*! function for copying data */ -} UDSRDBIArgs_t; - -typedef struct { - const void *memAddr; - const size_t memSize; - uint8_t (*copy)(UDSServer_t *srv, const void *src, - uint16_t count); /*! function for copying data */ -} UDSReadMemByAddrArgs_t; - -typedef struct { - uint8_t ctrlType; /* enum UDSCommunicationControlType */ - uint8_t commType; /* enum UDSCommunicationType */ -} UDSCommCtrlArgs_t; - -typedef struct { - const uint8_t level; /*! requested security level */ - const uint8_t *const dataRecord; /*! pointer to request data */ - const uint16_t len; /*! size of request data */ - uint8_t (*copySeed)(UDSServer_t *srv, const void *src, - uint16_t len); /*! function for copying data */ -} UDSSecAccessRequestSeedArgs_t; - -typedef struct { - const uint8_t level; /*! security level to be validated */ - const uint8_t *const key; /*! key sent by client */ - const uint16_t len; /*! length of key */ -} UDSSecAccessValidateKeyArgs_t; - -typedef struct { - const uint16_t dataId; /*! WDBI Data Identifier */ - const uint8_t *const data; /*! pointer to data */ - const uint16_t len; /*! length of data */ -} UDSWDBIArgs_t; - -typedef struct { - const uint8_t ctrlType; /*! routineControlType */ - const uint16_t id; /*! routineIdentifier */ - const uint8_t *optionRecord; /*! optional data */ - const uint16_t len; /*! length of optional data */ - uint8_t (*copyStatusRecord)(UDSServer_t *srv, const void *src, - uint16_t len); /*! function for copying response data */ -} UDSRoutineCtrlArgs_t; - -typedef struct { - const void *addr; /*! requested address */ - const size_t size; /*! requested download size */ - const uint8_t dataFormatIdentifier; /*! optional specifier for format of data */ - uint16_t maxNumberOfBlockLength; /*! optional response: inform client how many data bytes to - send in each `TransferData` request */ -} UDSRequestDownloadArgs_t; - -typedef struct { - const void *addr; /*! requested address */ - const size_t size; /*! requested download size */ - const uint8_t dataFormatIdentifier; /*! optional specifier for format of data */ - uint16_t maxNumberOfBlockLength; /*! optional response: inform client how many data bytes to - send in each `TransferData` request */ -} UDSRequestUploadArgs_t; - -typedef struct { - const uint8_t *const data; /*! transfer data */ - const uint16_t len; /*! transfer data length */ - const uint16_t maxRespLen; /*! don't send more than this many bytes with copyResponse */ - uint8_t (*copyResponse)( - UDSServer_t *srv, const void *src, - uint16_t len); /*! function for copying transfer data response data (optional) */ -} UDSTransferDataArgs_t; - -typedef struct { - const uint8_t *const data; /*! request data */ - const uint16_t len; /*! request data length */ - uint8_t (*copyResponse)(UDSServer_t *srv, const void *src, - uint16_t len); /*! function for copying response data (optional) */ -} UDSRequestTransferExitArgs_t; - -UDSErr_t UDSServerInit(UDSServer_t *self, const UDSServerConfig_t *cfg); -void UDSServerDeInit(UDSServer_t *self); -void UDSServerPoll(UDSServer_t *self); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/iso14229serverbufferedwriter.h b/iso14229serverbufferedwriter.h deleted file mode 100644 index 7c3784f..0000000 --- a/iso14229serverbufferedwriter.h +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @file udsserverbufferedwriter.h - * - * [ UDS接收缓冲器 ] -BufferedWriterWrite()-> [FLASH扇区缓冲器] -writeFunc()-> [FLASH] - * - * - 写到FLASH时、要先发一个0x78响应然后去写 - */ - -#ifndef BUFFEREDWRITER_H -#define BUFFEREDWRITER_H - -#include -#include -#include -#include -#include - -typedef struct { - /** - * @brief writes to the program flash logical partition - * @param address: page-aligned - * @param len: <= pageBufferSize - * @param data: buffer - * @return - */ - int (*writeFunc)(void *address, const uint8_t *data, const uint32_t length); - const void *logicalPartitionStartAddr; - const uint32_t logicalPartitionSize; - const uint8_t *pageBuffer; // pointer to a buffer that is the size of a flash page - const uint32_t pageBufferSize; // size of a flash page in bytes -} BufferedWriterConfig; - -typedef struct { - const BufferedWriterConfig *cfg; - uint32_t pageBufIdx; - uint32_t flashOffset; - uint32_t iBufIdx; - bool writePending; -} BufferedWriter; - -static inline void bufferedWriterInit(BufferedWriter *self, const BufferedWriterConfig *cfg) { - assert(cfg->writeFunc); - assert(cfg->logicalPartitionStartAddr); - assert(cfg->pageBuffer); - assert(cfg->pageBufferSize > 0); - assert(cfg->logicalPartitionSize >= cfg->pageBufferSize); - assert(cfg->logicalPartitionSize % cfg->pageBufferSize == 0); - - self->writePending = false; - self->cfg = cfg; - self->iBufIdx = 0; - self->pageBufIdx = 0; - self->flashOffset = 0; - memset((void *)cfg->pageBuffer, 0, cfg->pageBufferSize); -} - -static inline void _BufferedWriterCopy(BufferedWriter *self, const uint8_t *ibuf, uint32_t size) { - if (0 == size) { - return; - } - const BufferedWriterConfig *cfg = self->cfg; - uint32_t bufferUnusedBytes = cfg->pageBufferSize - self->pageBufIdx; - uint32_t copyLen = - size - self->iBufIdx < bufferUnusedBytes ? size - self->iBufIdx : bufferUnusedBytes; - memmove((void *)(cfg->pageBuffer + self->pageBufIdx), ibuf + self->iBufIdx, copyLen); - self->pageBufIdx += copyLen; - self->iBufIdx += copyLen; -} - -static inline void _BufferedWriterWrite(BufferedWriter *self) { - const BufferedWriterConfig *cfg = self->cfg; - cfg->writeFunc((void *)((uint8_t *)cfg->logicalPartitionStartAddr + self->flashOffset), - cfg->pageBuffer, cfg->pageBufferSize); - self->flashOffset += cfg->pageBufferSize; - self->pageBufIdx = 0; - memset((void *)cfg->pageBuffer, 0, cfg->pageBufferSize); -} - -/** - * @brief 处理flash写入、包含0x78 RequestCorrectlyReceived_ResponsePending响应。 - * 为了了解更多关于0x78的作用、可以参考ISO-14229-1:2013。 - * - * @param self 指针到驱动实例 - * @param ibuf 输入缓冲器 (UDS接收缓冲器) - * @param size 输入缓冲器大小 - * @return true 等待写入。你应该返回0x78。不要换输入缓冲器。我还没读完。 - * @return false 不在等、可以返回0x01。 - */ -static inline bool BufferedWriterProcess(BufferedWriter *self, const uint8_t *ibuf, uint32_t size) { - - if (self->writePending) { // 要写入 - _BufferedWriterWrite(self); - if (self->iBufIdx == size) { - // 输入缓冲器用完了、我要新数据 - self->iBufIdx = 0; - return self->writePending = false; - } - } - - _BufferedWriterCopy(self, ibuf, size); - - if ( - // 扇区缓冲器充满了、要写入 - (self->pageBufIdx == self->cfg->pageBufferSize) || - // 扇区缓冲器有数据、用户要完成写入 - (0 != self->pageBufIdx && 0 == size)) { - return self->writePending = true; - } else { - self->iBufIdx = 0; - return self->writePending = false; - } -} - -#endif diff --git a/isotp-c/isotp_user.h b/isotp-c/isotp_user.h deleted file mode 100644 index ed40337..0000000 --- a/isotp-c/isotp_user.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __ISOTP_USER_H__ -#define __ISOTP_USER_H__ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/* user implemented, print debug message */ -void isotp_user_debug(const char* message, ...); - -/* user implemented, send can message. should return ISOTP_RET_OK when success. -*/ -int isotp_user_send_can(const uint32_t arbitration_id, - const uint8_t* data, const uint8_t size); - -/* user implemented, get microsecond */ -uint32_t isotp_user_get_us(void); - -#ifdef __cplusplus -} -#endif - -#endif // __ISOTP_H__ - diff --git a/platforms/BUILD b/platforms/BUILD new file mode 100644 index 0000000..7ca5b02 --- /dev/null +++ b/platforms/BUILD @@ -0,0 +1,72 @@ +package(default_visibility = ["//visibility:public"]) + +constraint_setting(name = "mcu_series") + +constraint_value( + name = "s32k", + constraint_setting = ":mcu_series", +) + +platform( + name = "x86_64", + constraint_values = [ + "@platforms//cpu:x86_64", + "@platforms//os:linux", + ], +) + +platform( + name = "x86_64_clang", + parents = [":x86_64"], + constraint_values = [ + "//platforms/compiler:clang", + ], +) + +platform( + name = "arm_linux", + constraint_values = [ + "@platforms//cpu:arm", + "@platforms//os:linux", + ], +) + +platform( + name = "arm_none", + constraint_values = [ + "@platforms//cpu:arm", + "@platforms//os:none", + ], +) + +platform( + name = "s32k_evb", + parents = [":arm_none"], + constraint_values = [ + ":s32k", + ], +) + +platform( + name = "ppc", + constraint_values = [ + "@platforms//cpu:ppc", + "@platforms//os:linux", + ], +) + +platform( + name = "ppc64", + constraint_values = [ + "//platforms/cpu:ppc64", + "@platforms//os:linux", + ], +) + +platform( + name = "ppc64le", + constraint_values = [ + "//platforms/cpu:ppc64le", + "@platforms//os:linux", + ], +) \ No newline at end of file diff --git a/platforms/cmsis.BUILD b/platforms/cmsis.BUILD new file mode 100644 index 0000000..e580aad --- /dev/null +++ b/platforms/cmsis.BUILD @@ -0,0 +1,13 @@ +package(default_visibility = ["//visibility:public"]) + + +filegroup( + name = "srcs", + srcs = glob(["**/**"]), +) + +cc_library( + name = "include", + srcs = glob(["CMSIS/Core/Include/**"]), + includes = ["CMSIS/Core/Include"], +) \ No newline at end of file diff --git a/platforms/compiler/BUILD b/platforms/compiler/BUILD new file mode 100644 index 0000000..79b892d --- /dev/null +++ b/platforms/compiler/BUILD @@ -0,0 +1,5 @@ +package(default_visibility = ["//visibility:public"]) + +constraint_setting(name = "compiler") +constraint_value(name = "gcc", constraint_setting = ":compiler") +constraint_value(name = "clang", constraint_setting = ":compiler") \ No newline at end of file diff --git a/platforms/cpu/BUILD b/platforms/cpu/BUILD new file mode 100644 index 0000000..9078e7a --- /dev/null +++ b/platforms/cpu/BUILD @@ -0,0 +1,3 @@ +package(default_visibility = ["//visibility:public"]) +constraint_value(name="ppc64", constraint_setting="@platforms//cpu:cpu") +constraint_value(name="ppc64le", constraint_setting="@platforms//cpu:cpu") \ No newline at end of file diff --git a/platforms/s32k_sdk.BUILD b/platforms/s32k_sdk.BUILD new file mode 100644 index 0000000..950faab --- /dev/null +++ b/platforms/s32k_sdk.BUILD @@ -0,0 +1,20 @@ +package(default_visibility = ["//visibility:public"]) + + +filegroup( + name = "srcs", + srcs = glob(["**/**"]), +) + +cc_library( + name = "include", + includes = [ + "platform/devices", + "platform/devices/common", + "platform/devices/S32K144/startup", + "platform/drivers/inc", + "platform/drivers/src/lpuart", + "rtos/osif", + "platform/pal/inc", + ], +) \ No newline at end of file diff --git a/run_clang_format.sh b/run_clang_format.sh index 8e2e8cf..8a1e8af 100755 --- a/run_clang_format.sh +++ b/run_clang_format.sh @@ -1,6 +1,6 @@ #! /bin/bash -files=`find . -type f \( -name '*.c' -o -name '*.h' \) -not -path "./isotp-c/*"` +files=`find src -type f \( -name '*.c' -o -name '*.h' \) -not -path "src/tp/isotp-c/*"` for file in $files ; do if [ -z "$CHECK_FORMAT" ] ; then diff --git a/run_gdb.py b/run_gdb.py new file mode 100755 index 0000000..5c8a25e --- /dev/null +++ b/run_gdb.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python3 + +__doc__ = """ +Automates bazel, gdb, and optionally qemu to accelerate debugging + +the `bazel test` command explicitly prohibits interactive debugging, +so this script exists to automate the debugging process. +""" + +import subprocess +import argparse +import os + +GDB = "gdb-multiarch" +QEMU = "qemu-arm" +QEMU_LDFLAGS = "/usr/arm-linux-gnueabihf/" +GDB_PORT = 1234 + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument("test", help="The test to debug") +parser.add_argument("--config", help="bazel config", default="") +args = parser.parse_args() +gdb_args = [GDB, "-q", "-ex", f"file bazel-bin/test/{args.test}"] + +print(f"building {args.test}...") +bazel_args = ["bazel", "build", "-c", "dbg", "--copt=-g", f"//test:{args.test}"] +if args.config: + bazel_args.append(f"--config={args.config}") + +subprocess.check_call(bazel_args) +procs = [] + +if args.config: + qemu = subprocess.Popen( + [QEMU, "-g", str(GDB_PORT), + "-L", "/usr/arm-linux-gnueabihf/", + f"bazel-bin/test/{args.test}"], + env=os.environ.copy(), + ) + + if qemu.returncode is not None: + exit(qemu.returncode) + + procs.append(qemu) + gdb_args += ["-ex", f"target remote localhost:{GDB_PORT}"] + + +try: + gdb = subprocess.Popen(gdb_args, env=os.environ.copy()) + procs.append(gdb) + gdb.wait() +except KeyboardInterrupt: + pass +finally: + [p.kill() for p in procs] + exit(gdb.returncode) diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..4c13488 --- /dev/null +++ b/src/client.c @@ -0,0 +1,856 @@ +#include "client.h" +#include "config.h" +#include "util.h" + +static void clearRequestContext(UDSClient_t *client) { + assert(client); + client->recv_size = 0; + client->send_size = 0; + client->state = kRequestStateIdle; + client->err = UDS_OK; +} + +UDSErr_t UDSClientInit(UDSClient_t *client) { + assert(client); + memset(client, 0, sizeof(*client)); + + client->p2_ms = UDS_CLIENT_DEFAULT_P2_MS; + client->p2_star_ms = UDS_CLIENT_DEFAULT_P2_STAR_MS; + + if (client->p2_star_ms < client->p2_ms) { + fprintf(stderr, "p2_star_ms must be >= p2_ms\n"); + client->p2_star_ms = client->p2_ms; + } + + clearRequestContext(client); + return UDS_OK; +} + +static const char *ClientStateName(enum UDSClientRequestState state) { + switch (state) { + case kRequestStateIdle: + return "Idle"; + case kRequestStateSending: + return "Sending"; + case kRequestStateAwaitSendComplete: + return "AwaitSendComplete"; + case kRequestStateAwaitResponse: + return "AwaitResponse"; + case kRequestStateProcessResponse: + return "ProcessResponse"; + default: + return "Unknown"; + } +} + +static void changeState(UDSClient_t *client, enum UDSClientRequestState state) { + printf("client state: %s (%d) -> %s (%d)\n", ClientStateName(client->state), client->state, + ClientStateName(state), state); + client->state = state; +} + +/** + * @brief Check that the response is a valid UDS response + * + * @param ctx + * @return UDSErr_t + */ +static UDSErr_t _ClientValidateResponse(const UDSClient_t *client) { + + if (client->recv_size < 1) { + return UDS_ERR_RESP_TOO_SHORT; + } + + if (0x7F == client->recv_buf[0]) { // 否定响应 + if (client->recv_size < 2) { + return UDS_ERR_RESP_TOO_SHORT; + } else if (client->send_buf[0] != client->recv_buf[1]) { + return UDS_ERR_SID_MISMATCH; + } else if (kRequestCorrectlyReceived_ResponsePending == client->recv_buf[2]) { + return UDS_OK; + } else if (client->_options_copy & UDS_NEG_RESP_IS_ERR) { + return UDS_ERR_NEG_RESP; + } else { + ; + } + } else { // 肯定响应 + if (UDS_RESPONSE_SID_OF(client->send_buf[0]) != client->recv_buf[0]) { + return UDS_ERR_SID_MISMATCH; + } + switch (client->send_buf[0]) { + case kSID_ECU_RESET: + if (client->recv_size < 2) { + return UDS_ERR_RESP_TOO_SHORT; + } else if (client->send_buf[1] != client->recv_buf[1]) { + return UDS_ERR_SUBFUNCTION_MISMATCH; + } else { + ; + } + break; + } + } + + return UDS_OK; +} + +/** + * @brief Handle validated server response + * @param client + */ +static inline void _ClientHandleResponse(UDSClient_t *client) { + if (0x7F == client->recv_buf[0]) { + if (kRequestCorrectlyReceived_ResponsePending == client->recv_buf[2]) { + UDS_DBG_PRINT("got RCRRP, setting p2 timer\n"); + client->p2_timer = UDSMillis() + client->p2_star_ms; + memset(client->recv_buf, 0, client->recv_buf_size); + client->recv_size = 0; + UDSTpAckRecv(client->tp); + changeState(client, kRequestStateAwaitResponse); + return; + } else { + ; + } + } else { + uint8_t respSid = client->recv_buf[0]; + switch (UDS_REQUEST_SID_OF(respSid)) { + case kSID_DIAGNOSTIC_SESSION_CONTROL: { + if (client->recv_size < UDS_0X10_RESP_LEN) { + UDS_DBG_PRINT("Error: SID %x response too short\n", + kSID_DIAGNOSTIC_SESSION_CONTROL); + client->err = UDS_ERR_RESP_TOO_SHORT; + UDSTpAckRecv(client->tp); + changeState(client, kRequestStateIdle); + return; + } + + if (client->_options_copy & UDS_IGNORE_SRV_TIMINGS) { + UDSTpAckRecv(client->tp); + changeState(client, kRequestStateIdle); + return; + } + + uint16_t p2 = (client->recv_buf[2] << 8) + client->recv_buf[3]; + uint32_t p2_star = ((client->recv_buf[4] << 8) + client->recv_buf[5]) * 10; + UDS_DBG_PRINT("received new timings: p2: %" PRIu16 ", p2*: %" PRIu32 "\n", p2, p2_star); + client->p2_ms = p2; + client->p2_star_ms = p2_star; + break; + } + default: + break; + } + } + UDSTpAckRecv(client->tp); + changeState(client, kRequestStateIdle); +} + +/** + * @brief execute the client request state machine + * @param client + */ +static void PollLowLevel(UDSClient_t *client) { + assert(client); + UDSTpStatus_t tp_status = client->tp->poll(client->tp); + switch (client->state) { + case kRequestStateIdle: { + client->options = client->defaultOptions; + break; + } + case kRequestStateSending: { + UDSTpAddr_t ta_type = client->_options_copy & UDS_FUNCTIONAL ? UDS_A_TA_TYPE_FUNCTIONAL + : UDS_A_TA_TYPE_PHYSICAL; + UDSSDU_t info = { + .A_Mtype = UDS_A_MTYPE_DIAG, + .A_TA_Type = ta_type, + }; + ssize_t ret = UDSTpSend(client->tp, client->send_buf, client->send_size, &info); + if (ret < 0) { + client->err = UDS_ERR_TPORT; + UDS_DBG_PRINT("tport err: %zd\n", ret); + } else if (0 == ret) { + UDS_DBG_PRINT("send in progress...\n"); + ; // 等待发送成功 + } else if (client->send_size == ret) { + changeState(client, kRequestStateAwaitSendComplete); + } else { + client->err = UDS_ERR_BUFSIZ; + } + break; + } + case kRequestStateAwaitSendComplete: { + if (client->_options_copy & UDS_FUNCTIONAL) { + // "The Functional addressing is applied only to single frame transmission" + // Specification of Diagnostic Communication (Diagnostic on CAN - Network Layer) + changeState(client, kRequestStateIdle); + } + if (tp_status & UDS_TP_SEND_IN_PROGRESS) { + ; // await send complete + } else { + if (client->_options_copy & UDS_SUPPRESS_POS_RESP) { + changeState(client, kRequestStateIdle); + } else { + changeState(client, kRequestStateAwaitResponse); + client->p2_timer = UDSMillis() + client->p2_ms; + } + } + break; + } + case kRequestStateAwaitResponse: { + UDSSDU_t info = {0}; + ssize_t len = UDSTpPeek(client->tp, &client->recv_buf, &info); + + if (UDS_A_TA_TYPE_FUNCTIONAL == info.A_TA_Type) { + UDSTpAckRecv(client->tp); + break; + } + if (len < 0) { + client->err = UDS_ERR_TPORT; + changeState(client, kRequestStateIdle); + } else if (0 == len) { + if (UDSTimeAfter(UDSMillis(), client->p2_timer)) { + client->err = UDS_ERR_TIMEOUT; + changeState(client, kRequestStateIdle); + } + } else { + printf("received %zd bytes\n", len); + client->recv_size = len; + changeState(client, kRequestStateProcessResponse); + } + break; + } + case kRequestStateProcessResponse: { + client->err = _ClientValidateResponse(client); + if (UDS_OK == client->err) { + _ClientHandleResponse(client); + } else { + UDSTpAckRecv(client->tp); + changeState(client, kRequestStateIdle); + } + break; + } + + default: + assert(0); + } +} + +static UDSErr_t _SendRequest(UDSClient_t *client) { + client->_options_copy = client->options; + + if (client->_options_copy & UDS_SUPPRESS_POS_RESP) { + // UDS-1:2013 8.2.2 Table 11 + client->send_buf[1] |= 0x80; + } + + changeState(client, kRequestStateSending); + PollLowLevel(client); // poll once to begin sending immediately + return UDS_OK; +} + +static UDSErr_t PreRequestCheck(UDSClient_t *client) { + if (kRequestStateIdle != client->state) { + return UDS_ERR_BUSY; + } + clearRequestContext(client); + if (client->tp == NULL) { + return UDS_ERR_TPORT; + } + ssize_t ret = UDSTpGetSendBuf(client->tp, &client->send_buf); + if (ret < 0) { + return UDS_ERR_TPORT; + } + client->send_buf_size = ret; + return UDS_OK; +} + +UDSErr_t UDSSendBytes(UDSClient_t *client, const uint8_t *data, uint16_t size) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + if (size > client->send_buf_size) { + return UDS_ERR_BUFSIZ; + } + memmove(client->send_buf, data, size); + client->send_size = size; + return _SendRequest(client); +} + +UDSErr_t UDSSendECUReset(UDSClient_t *client, UDSECUReset_t type) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + client->send_buf[0] = kSID_ECU_RESET; + client->send_buf[1] = type; + client->send_size = 2; + return _SendRequest(client); +} + +UDSErr_t UDSSendDiagSessCtrl(UDSClient_t *client, enum UDSDiagnosticSessionType mode) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + client->send_buf[0] = kSID_DIAGNOSTIC_SESSION_CONTROL; + client->send_buf[1] = mode; + client->send_size = 2; + return _SendRequest(client); +} + +UDSErr_t UDSSendCommCtrl(UDSClient_t *client, enum UDSCommunicationControlType ctrl, + enum UDSCommunicationType comm) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + client->send_buf[0] = kSID_COMMUNICATION_CONTROL; + client->send_buf[1] = ctrl; + client->send_buf[2] = comm; + client->send_size = 3; + return _SendRequest(client); +} + +UDSErr_t UDSSendTesterPresent(UDSClient_t *client) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + client->send_buf[0] = kSID_TESTER_PRESENT; + client->send_buf[1] = 0; + client->send_size = 2; + return _SendRequest(client); +} + +UDSErr_t UDSSendRDBI(UDSClient_t *client, const uint16_t *didList, + const uint16_t numDataIdentifiers) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + assert(didList); + assert(numDataIdentifiers); + client->send_buf[0] = kSID_READ_DATA_BY_IDENTIFIER; + for (int i = 0; i < numDataIdentifiers; i++) { + uint16_t offset = 1 + sizeof(uint16_t) * i; + if (offset + 2 > client->send_buf_size) { + return UDS_ERR_INVALID_ARG; + } + (client->send_buf + offset)[0] = (didList[i] & 0xFF00) >> 8; + (client->send_buf + offset)[1] = (didList[i] & 0xFF); + } + client->send_size = 1 + (numDataIdentifiers * sizeof(uint16_t)); + return _SendRequest(client); +} + +UDSErr_t UDSSendWDBI(UDSClient_t *client, uint16_t dataIdentifier, const uint8_t *data, + uint16_t size) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + assert(data); + assert(size); + client->send_buf[0] = kSID_WRITE_DATA_BY_IDENTIFIER; + if (client->send_buf_size <= 3 || size > client->send_buf_size - 3) { + return UDS_ERR_BUFSIZ; + } + client->send_buf[1] = (dataIdentifier & 0xFF00) >> 8; + client->send_buf[2] = (dataIdentifier & 0xFF); + memmove(&client->send_buf[3], data, size); + client->send_size = 3 + size; + return _SendRequest(client); +} + +/** + * @brief RoutineControl + * + * @param client + * @param type + * @param routineIdentifier + * @param data + * @param size + * @return UDSErr_t + * @addtogroup routineControl_0x31 + */ +UDSErr_t UDSSendRoutineCtrl(UDSClient_t *client, enum RoutineControlType type, + uint16_t routineIdentifier, const uint8_t *data, uint16_t size) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + client->send_buf[0] = kSID_ROUTINE_CONTROL; + client->send_buf[1] = type; + client->send_buf[2] = routineIdentifier >> 8; + client->send_buf[3] = routineIdentifier; + if (size) { + assert(data); + if (size > client->send_buf_size - UDS_0X31_REQ_MIN_LEN) { + return UDS_ERR_BUFSIZ; + } + memmove(&client->send_buf[UDS_0X31_REQ_MIN_LEN], data, size); + } else { + assert(NULL == data); + } + client->send_size = UDS_0X31_REQ_MIN_LEN + size; + return _SendRequest(client); +} + +/** + * @brief + * + * @param client + * @param dataFormatIdentifier + * @param addressAndLengthFormatIdentifier + * @param memoryAddress + * @param memorySize + * @return UDSErr_t + * @addtogroup requestDownload_0x34 + */ +UDSErr_t UDSSendRequestDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, + uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, + size_t memorySize) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + uint8_t numMemorySizeBytes = (addressAndLengthFormatIdentifier & 0xF0) >> 4; + uint8_t numMemoryAddressBytes = addressAndLengthFormatIdentifier & 0x0F; + + client->send_buf[0] = kSID_REQUEST_DOWNLOAD; + client->send_buf[1] = dataFormatIdentifier; + client->send_buf[2] = addressAndLengthFormatIdentifier; + + uint8_t *ptr = &client->send_buf[UDS_0X34_REQ_BASE_LEN]; + + for (int i = numMemoryAddressBytes - 1; i >= 0; i--) { + *ptr = (memoryAddress & (0xFF << (8 * i))) >> (8 * i); + ptr++; + } + + for (int i = numMemorySizeBytes - 1; i >= 0; i--) { + *ptr = (memorySize & (0xFF << (8 * i))) >> (8 * i); + ptr++; + } + + client->send_size = UDS_0X34_REQ_BASE_LEN + numMemoryAddressBytes + numMemorySizeBytes; + return _SendRequest(client); +} + +/** + * @brief + * + * @param client + * @param dataFormatIdentifier + * @param addressAndLengthFormatIdentifier + * @param memoryAddress + * @param memorySize + * @return UDSErr_t + * @addtogroup requestDownload_0x35 + */ +UDSErr_t UDSSendRequestUpload(UDSClient_t *client, uint8_t dataFormatIdentifier, + uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, + size_t memorySize) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + uint8_t numMemorySizeBytes = (addressAndLengthFormatIdentifier & 0xF0) >> 4; + uint8_t numMemoryAddressBytes = addressAndLengthFormatIdentifier & 0x0F; + + client->send_buf[0] = kSID_REQUEST_UPLOAD; + client->send_buf[1] = dataFormatIdentifier; + client->send_buf[2] = addressAndLengthFormatIdentifier; + + uint8_t *ptr = &client->send_buf[UDS_0X35_REQ_BASE_LEN]; + + for (int i = numMemoryAddressBytes - 1; i >= 0; i--) { + *ptr = (memoryAddress & (0xFF << (8 * i))) >> (8 * i); + ptr++; + } + + for (int i = numMemorySizeBytes - 1; i >= 0; i--) { + *ptr = (memorySize & (0xFF << (8 * i))) >> (8 * i); + ptr++; + } + + client->send_size = UDS_0X35_REQ_BASE_LEN + numMemoryAddressBytes + numMemorySizeBytes; + return _SendRequest(client); +} + +/** + * @brief + * + * @param client + * @param blockSequenceCounter + * @param blockLength + * @param fd + * @return UDSErr_t + * @addtogroup transferData_0x36 + */ +UDSErr_t UDSSendTransferData(UDSClient_t *client, uint8_t blockSequenceCounter, + const uint16_t blockLength, const uint8_t *data, uint16_t size) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + assert(blockLength > 2); // blockLength must include SID and sequenceCounter + assert(size + 2 <= blockLength); // data must fit inside blockLength - 2 + client->send_buf[0] = kSID_TRANSFER_DATA; + client->send_buf[1] = blockSequenceCounter; + memmove(&client->send_buf[UDS_0X36_REQ_BASE_LEN], data, size); + UDS_DBG_PRINT("size: %d, blocklength: %d\n", size, blockLength); + client->send_size = UDS_0X36_REQ_BASE_LEN + size; + return _SendRequest(client); +} + +UDSErr_t UDSSendTransferDataStream(UDSClient_t *client, uint8_t blockSequenceCounter, + const uint16_t blockLength, FILE *fd) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + assert(blockLength > 2); // blockLength must include SID and sequenceCounter + client->send_buf[0] = kSID_TRANSFER_DATA; + client->send_buf[1] = blockSequenceCounter; + + uint16_t size = fread(&client->send_buf[2], 1, blockLength - 2, fd); + UDS_DBG_PRINT("size: %d, blocklength: %d\n", size, blockLength); + client->send_size = UDS_0X36_REQ_BASE_LEN + size; + return _SendRequest(client); +} + +/** + * @brief + * + * @param client + * @return UDSErr_t + * @addtogroup requestTransferExit_0x37 + */ +UDSErr_t UDSSendRequestTransferExit(UDSClient_t *client) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + client->send_buf[0] = kSID_REQUEST_TRANSFER_EXIT; + client->send_size = 1; + return _SendRequest(client); +} + +/** + * @brief + * + * @param client + * @param dtcSettingType + * @param data + * @param size + * @return UDSErr_t + * @addtogroup controlDTCSetting_0x85 + */ +UDSErr_t UDSCtrlDTCSetting(UDSClient_t *client, uint8_t dtcSettingType, uint8_t *data, + uint16_t size) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + if (0x00 == dtcSettingType || 0x7F == dtcSettingType || + (0x03 <= dtcSettingType && dtcSettingType <= 0x3F)) { + assert(0); // reserved vals + } + client->send_buf[0] = kSID_CONTROL_DTC_SETTING; + client->send_buf[1] = dtcSettingType; + + if (NULL == data) { + assert(size == 0); + } else { + assert(size > 0); + if (size > client->send_buf_size - 2) { + return UDS_ERR_BUFSIZ; + } + memmove(&client->send_buf[2], data, size); + } + client->send_size = 2 + size; + return _SendRequest(client); +} + +/** + * @brief + * + * @param client + * @param level + * @param data + * @param size + * @return UDSErr_t + * @addtogroup securityAccess_0x27 + */ +UDSErr_t UDSSendSecurityAccess(UDSClient_t *client, uint8_t level, uint8_t *data, uint16_t size) { + UDSErr_t err = PreRequestCheck(client); + if (err) { + return err; + } + if (UDSSecurityAccessLevelIsReserved(level)) { + return UDS_ERR_INVALID_ARG; + } + client->send_buf[0] = kSID_SECURITY_ACCESS; + client->send_buf[1] = level; + if (size) { + assert(data); + if (size > client->send_buf_size - UDS_0X27_REQ_BASE_LEN) { + return UDS_ERR_BUFSIZ; + } + } else { + assert(NULL == data); + } + + memmove(&client->send_buf[UDS_0X27_REQ_BASE_LEN], data, size); + client->send_size = UDS_0X27_REQ_BASE_LEN + size; + return _SendRequest(client); +} + +typedef struct { + uint8_t dataFormatIdentifier; + uint8_t addressAndLengthFormatIdentifier; + size_t memoryAddress; + size_t memorySize; + FILE *fd; + uint8_t blockSequenceCounter; + uint16_t blockLength; +} UDSClientDownloadSequence_t; + +static UDSSeqState_t requestDownload(UDSClient_t *client) { + UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; + UDSSendRequestDownload(client, pL_Seq->dataFormatIdentifier, + pL_Seq->addressAndLengthFormatIdentifier, pL_Seq->memoryAddress, + pL_Seq->memorySize); + return UDSSeqStateGotoNext; +} + +static UDSSeqState_t checkRequestDownloadResponse(UDSClient_t *client) { + UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; + struct RequestDownloadResponse resp = {0}; + UDSErr_t err = UDSUnpackRequestDownloadResponse(client, &resp); + if (err) { + client->err = err; + return UDSSeqStateDone; + } + pL_Seq->blockLength = resp.maxNumberOfBlockLength; + if (0 == resp.maxNumberOfBlockLength) { + client->err = UDS_ERR; + return UDSSeqStateDone; + } + return UDSSeqStateGotoNext; +} + +static UDSSeqState_t prepareToTransfer(UDSClient_t *client) { + UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; + pL_Seq->blockSequenceCounter = 1; + return UDSSeqStateGotoNext; +} + +static UDSSeqState_t transferData(UDSClient_t *client) { + UDSClientDownloadSequence_t *pL_Seq = (UDSClientDownloadSequence_t *)client->cbData; + if (kRequestStateIdle == client->state) { + if (ferror(pL_Seq->fd)) { + fclose(pL_Seq->fd); + client->err = UDS_ERR_FILE_IO; // 读取文件故障 + return UDSSeqStateDone; + } else if (feof(pL_Seq->fd)) { // 传完了 + return UDSSeqStateGotoNext; + } else { + UDSSendTransferDataStream(client, pL_Seq->blockSequenceCounter++, pL_Seq->blockLength, + pL_Seq->fd); + } + } + return UDSSeqStateRunning; +} + +static UDSSeqState_t requestTransferExit(UDSClient_t *client) { + UDSSendRequestTransferExit(client); + return UDSSeqStateGotoNext; +} + +UDSErr_t UDSConfigDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, + uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, + size_t memorySize, FILE *fd) { + + static const UDSClientCallback callbacks[] = { + requestDownload, UDSClientAwaitIdle, checkRequestDownloadResponse, prepareToTransfer, + transferData, requestTransferExit, UDSClientAwaitIdle, NULL}; + static UDSClientDownloadSequence_t seq = {0}; + memset(&seq, 0, sizeof(seq)); + seq.blockSequenceCounter = 1; + seq.dataFormatIdentifier = dataFormatIdentifier; + seq.addressAndLengthFormatIdentifier = addressAndLengthFormatIdentifier; + seq.memoryAddress = memoryAddress; + seq.memorySize = memorySize; + seq.fd = fd; + client->cbList = callbacks; + client->cbIdx = 0; + client->cbData = &seq; + return UDS_OK; +} + +/** + * @brief + * + * @param client + * @param resp + * @return UDSErr_t + * @addtogroup securityAccess_0x27 + */ +UDSErr_t UDSUnpackSecurityAccessResponse(const UDSClient_t *client, + struct SecurityAccessResponse *resp) { + assert(client); + assert(resp); + if (UDS_RESPONSE_SID_OF(kSID_SECURITY_ACCESS) != client->recv_buf[0]) { + return UDS_ERR_SID_MISMATCH; + } + if (client->recv_size < UDS_0X27_RESP_BASE_LEN) { + return UDS_ERR_RESP_TOO_SHORT; + } + resp->securityAccessType = client->recv_buf[1]; + resp->securitySeedLength = client->recv_size - UDS_0X27_RESP_BASE_LEN; + resp->securitySeed = resp->securitySeedLength == 0 ? NULL : &client->recv_buf[2]; + return UDS_OK; +} + +/** + * @brief + * + * @param client + * @param resp + * @return UDSErr_t + * @addtogroup routineControl_0x31 + */ +UDSErr_t UDSUnpackRoutineControlResponse(const UDSClient_t *client, + struct RoutineControlResponse *resp) { + assert(client); + assert(resp); + if (UDS_RESPONSE_SID_OF(kSID_ROUTINE_CONTROL) != client->recv_buf[0]) { + return UDS_ERR_SID_MISMATCH; + } + if (client->recv_size < UDS_0X31_RESP_MIN_LEN) { + return UDS_ERR_RESP_TOO_SHORT; + } + resp->routineControlType = client->recv_buf[1]; + resp->routineIdentifier = (client->recv_buf[2] << 8) + client->recv_buf[3]; + resp->routineStatusRecordLength = client->recv_size - UDS_0X31_RESP_MIN_LEN; + resp->routineStatusRecord = + resp->routineStatusRecordLength == 0 ? NULL : &client->recv_buf[UDS_0X31_RESP_MIN_LEN]; + return UDS_OK; +} + +/** + * @brief + * + * @param client + * @param resp + * @return UDSErr_t + * @addtogroup requestDownload_0x34 + */ +UDSErr_t UDSUnpackRequestDownloadResponse(const UDSClient_t *client, + struct RequestDownloadResponse *resp) { + assert(client); + assert(resp); + if (UDS_RESPONSE_SID_OF(kSID_REQUEST_DOWNLOAD) != client->recv_buf[0]) { + return UDS_ERR_SID_MISMATCH; + } + if (client->recv_size < UDS_0X34_RESP_BASE_LEN) { + return UDS_ERR_RESP_TOO_SHORT; + } + uint8_t maxNumberOfBlockLengthSize = (client->recv_buf[1] & 0xF0) >> 4; + + if (sizeof(resp->maxNumberOfBlockLength) < maxNumberOfBlockLengthSize) { + UDS_DBG_PRINT("WARNING: sizeof(maxNumberOfBlockLength) > sizeof(size_t)"); + return UDS_ERR; + } + resp->maxNumberOfBlockLength = 0; + for (int byteIdx = 0; byteIdx < maxNumberOfBlockLengthSize; byteIdx++) { + uint8_t byte = client->recv_buf[UDS_0X34_RESP_BASE_LEN + byteIdx]; + uint8_t shiftBytes = maxNumberOfBlockLengthSize - 1 - byteIdx; + resp->maxNumberOfBlockLength |= byte << (8 * shiftBytes); + } + return UDS_OK; +} + +bool UDSClientPoll(UDSClient_t *client) { + PollLowLevel(client); + + if (client->err) { + return UDS_CLIENT_IDLE; + } + + if (kRequestStateIdle != client->state) { + return UDS_CLIENT_RUNNING; + } + + if (NULL == client->cbList) { + return UDS_CLIENT_IDLE; + } + + UDSClientCallback activeCallback = client->cbList[client->cbIdx]; + + if (NULL == activeCallback) { + return UDS_CLIENT_IDLE; + } + + UDSSeqState_t state = activeCallback(client); + + switch (state) { + case UDSSeqStateDone: + return UDS_CLIENT_IDLE; + case UDSSeqStateRunning: + return UDS_CLIENT_RUNNING; + case UDSSeqStateGotoNext: { + client->cbIdx += 1; + return UDS_CLIENT_RUNNING; + } + default: + assert(0); + return UDS_CLIENT_IDLE; + } +} + +void UDSClientPoll2(UDSClient_t *client, + int (*fn)(UDSClient_t *client, UDSEvent_t evt, void *ev_data, void *fn_data), + void *fn_data) { + UDSClientPoll(client); +} + +UDSSeqState_t UDSClientAwaitIdle(UDSClient_t *client) { + if (client->err) { + return UDSSeqStateDone; + } else if (kRequestStateIdle == client->state) { + return UDSSeqStateGotoNext; + } else { + return UDSSeqStateRunning; + } +} + +UDSErr_t UDSUnpackRDBIResponse(const uint8_t *buf, size_t buf_len, uint16_t did, uint8_t *data, + uint16_t data_size, uint16_t *offset) { + assert(buf); + assert(data); + assert(offset); + if (0 == *offset) { + *offset = UDS_0X22_RESP_BASE_LEN; + } + + if (*offset + sizeof(did) > buf_len) { + return UDS_ERR_RESP_TOO_SHORT; + } + + uint16_t theirDID = (buf[*offset] << 8) + buf[*offset + 1]; + if (theirDID != did) { + return UDS_ERR_DID_MISMATCH; + } + + if (*offset + sizeof(uint16_t) + data_size > buf_len) { + return UDS_ERR_RESP_TOO_SHORT; + } + + memmove(data, buf + *offset + sizeof(uint16_t), data_size); + + *offset += sizeof(uint16_t) + data_size; + return UDS_OK; +} diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..144948e --- /dev/null +++ b/src/client.h @@ -0,0 +1,141 @@ +#pragma once + +#include "sys.h" +#include "tp.h" +#include "uds.h" + +enum UDSClientRequestState { + kRequestStateIdle = 0, // 完成 + kRequestStateSending, // 传输层现在传输数据 + kRequestStateAwaitSendComplete, // 等待传输发送完成 + kRequestStateAwaitResponse, // 等待响应 + kRequestStateProcessResponse, // 处理响应 +}; + +typedef uint8_t UDSClientRequestState_t; + +enum UDSClientOptions { + UDS_SUPPRESS_POS_RESP = 0x1, // 服务器不应该发送肯定响应 + UDS_FUNCTIONAL = 0x2, // 发功能请求 + UDS_NEG_RESP_IS_ERR = 0x4, // 否定响应是属于故障 + UDS_IGNORE_SRV_TIMINGS = 0x8, // 忽略服务器给的p2和p2_star +}; + +struct UDSClient; + +typedef UDSSeqState_t (*UDSClientCallback)(struct UDSClient *client); + +typedef struct UDSClient { + uint16_t p2_ms; // p2 超时时间 + uint32_t p2_star_ms; // 0x78 p2* 超时时间 + UDSTpHandle_t *tp; + + // 内状态 + uint32_t p2_timer; + uint8_t *recv_buf; + uint8_t *send_buf; + uint16_t recv_buf_size; + uint16_t send_buf_size; + uint16_t recv_size; + uint16_t send_size; + UDSErr_t err; + UDSClientRequestState_t state; + + uint8_t options; // enum udsclientoptions + uint8_t defaultOptions; // enum udsclientoptions + // a copy of the options at the time a request is made + uint8_t _options_copy; // enum udsclientoptions + int (*fn)(struct UDSClient *, int, void *, void *); + + const UDSClientCallback *cbList; // null-terminated list of callback functions + size_t cbIdx; // index of currently active callback function + void *cbData; // a pointer to data available to callbacks + +} UDSClient_t; + +struct SecurityAccessResponse { + uint8_t securityAccessType; + const uint8_t *securitySeed; + uint16_t securitySeedLength; +}; + +struct RequestDownloadResponse { + size_t maxNumberOfBlockLength; +}; + +struct RoutineControlResponse { + uint8_t routineControlType; + uint16_t routineIdentifier; + const uint8_t *routineStatusRecord; + uint16_t routineStatusRecordLength; +}; + +UDSErr_t UDSClientInit(UDSClient_t *client); + +#define UDS_CLIENT_IDLE (0) +#define UDS_CLIENT_RUNNING (1) + +/** + * @brief poll the client (call this in a loop) + * @param client + * @return UDS_CLIENT_IDLE if idle, otherwise UDS_CLIENT_RUNNING + */ +bool UDSClientPoll(UDSClient_t *client); +void UDSClientPoll2(UDSClient_t *client, + int (*fn)(UDSClient_t *client, int evt, void *ev_data, void *fn_data), + void *fn_data); + +UDSErr_t UDSSendBytes(UDSClient_t *client, const uint8_t *data, uint16_t size); +UDSErr_t UDSSendECUReset(UDSClient_t *client, UDSECUReset_t type); +UDSErr_t UDSSendDiagSessCtrl(UDSClient_t *client, enum UDSDiagnosticSessionType mode); +UDSErr_t UDSSendSecurityAccess(UDSClient_t *client, uint8_t level, uint8_t *data, uint16_t size); +UDSErr_t UDSSendCommCtrl(UDSClient_t *client, enum UDSCommunicationControlType ctrl, + enum UDSCommunicationType comm); +UDSErr_t UDSSendRDBI(UDSClient_t *client, const uint16_t *didList, + const uint16_t numDataIdentifiers); +UDSErr_t UDSSendWDBI(UDSClient_t *client, uint16_t dataIdentifier, const uint8_t *data, + uint16_t size); +UDSErr_t UDSSendTesterPresent(UDSClient_t *client); +UDSErr_t UDSSendRoutineCtrl(UDSClient_t *client, enum RoutineControlType type, + uint16_t routineIdentifier, const uint8_t *data, uint16_t size); + +UDSErr_t UDSSendRequestDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, + uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, + size_t memorySize); + +UDSErr_t UDSSendRequestUpload(UDSClient_t *client, uint8_t dataFormatIdentifier, + uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, + size_t memorySize); +UDSErr_t UDSSendTransferData(UDSClient_t *client, uint8_t blockSequenceCounter, + const uint16_t blockLength, const uint8_t *data, uint16_t size); +UDSErr_t UDSSendTransferDataStream(UDSClient_t *client, uint8_t blockSequenceCounter, + const uint16_t blockLength, FILE *fd); +UDSErr_t UDSSendRequestTransferExit(UDSClient_t *client); + +UDSErr_t UDSCtrlDTCSetting(UDSClient_t *client, uint8_t dtcSettingType, + uint8_t *dtcSettingControlOptionRecord, uint16_t len); +UDSErr_t UDSUnpackRDBIResponse(const uint8_t *buf, size_t buf_len, uint16_t did, uint8_t *data, + uint16_t size, uint16_t *offset); +UDSErr_t UDSUnpackSecurityAccessResponse(const UDSClient_t *client, + struct SecurityAccessResponse *resp); +UDSErr_t UDSUnpackRequestDownloadResponse(const UDSClient_t *client, + struct RequestDownloadResponse *resp); +UDSErr_t UDSUnpackRoutineControlResponse(const UDSClient_t *client, + struct RoutineControlResponse *resp); + +/** + * @brief Wait after request transmission for a response to be received + * @note if suppressPositiveResponse is set, this function will return + UDSSeqStateGotoNext as soon as the transport layer has completed transmission. + * + * @param client + * @param args + * @return UDSErr_t + - UDSSeqStateDone -- 流程完成 + - UDSSeqStateRunning -- 流程正在跑、还没完成 + */ +UDSSeqState_t UDSClientAwaitIdle(UDSClient_t *client); + +UDSErr_t UDSConfigDownload(UDSClient_t *client, uint8_t dataFormatIdentifier, + uint8_t addressAndLengthFormatIdentifier, size_t memoryAddress, + size_t memorySize, FILE *fd); diff --git a/src/config.h b/src/config.h new file mode 100644 index 0000000..5382f53 --- /dev/null +++ b/src/config.h @@ -0,0 +1,64 @@ +#pragma once + +#ifndef UDS_ENABLE_DBG_PRINT +#define UDS_ENABLE_DBG_PRINT 0 +#endif + +#ifndef UDS_ENABLE_ASSERT +#define UDS_ENABLE_ASSERT 0 +#endif + +/** ISO-TP Maximum Transmissiable Unit (ISO-15764-2-2004 section 5.3.3) */ +#define UDS_ISOTP_MTU (4095) + +#ifndef UDS_TP_MTU +#define UDS_TP_MTU UDS_ISOTP_MTU +#endif + +#ifndef UDS_CLIENT_DEFAULT_P2_MS +#define UDS_CLIENT_DEFAULT_P2_MS (150U) +#endif + +#ifndef UDS_CLIENT_DEFAULT_P2_STAR_MS +#define UDS_CLIENT_DEFAULT_P2_STAR_MS (1500U) +#endif + +_Static_assert(UDS_CLIENT_DEFAULT_P2_STAR_MS > UDS_CLIENT_DEFAULT_P2_MS, ""); + +#ifndef UDS_SERVER_DEFAULT_POWER_DOWN_TIME_MS +#define UDS_SERVER_DEFAULT_POWER_DOWN_TIME_MS (10) +#endif + +#ifndef UDS_SERVER_DEFAULT_P2_MS +#define UDS_SERVER_DEFAULT_P2_MS (50) +#endif + +#ifndef UDS_SERVER_DEFAULT_P2_STAR_MS +#define UDS_SERVER_DEFAULT_P2_STAR_MS (2000) +#endif + +#ifndef UDS_SERVER_DEFAULT_S3_MS +#define UDS_SERVER_DEFAULT_S3_MS (3000) +#endif + +_Static_assert(0 < UDS_SERVER_DEFAULT_P2_MS && + UDS_SERVER_DEFAULT_P2_MS < UDS_SERVER_DEFAULT_P2_STAR_MS && + UDS_SERVER_DEFAULT_P2_STAR_MS < UDS_SERVER_DEFAULT_S3_MS, + ""); + +// Amount of time to wait after boot before accepting 0x27 requests. +#ifndef UDS_SERVER_0x27_BRUTE_FORCE_MITIGATION_BOOT_DELAY_MS +#define UDS_SERVER_0x27_BRUTE_FORCE_MITIGATION_BOOT_DELAY_MS (1000) +#endif + +// Amount of time to wait after an authentication failure before accepting another 0x27 request. +#ifndef UDS_SERVER_0x27_BRUTE_FORCE_MITIGATION_AUTH_FAIL_DELAY_MS +#define UDS_SERVER_0x27_BRUTE_FORCE_MITIGATION_AUTH_FAIL_DELAY_MS (1000) +#endif + +#ifndef UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH +/*! ISO14229-1:2013 Table 396. This parameter is used by the requestDownload positive response +message to inform the client how many data bytes (maxNumberOfBlockLength) to include in each +TransferData request message from the client. */ +#define UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH (UDS_TP_MTU) +#endif diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..6a959cd --- /dev/null +++ b/src/server.c @@ -0,0 +1,948 @@ +#include "server.h" +#include "config.h" +#include "uds.h" +#include "util.h" + +static inline uint8_t NegativeResponse(UDSReq_t *r, uint8_t response_code) { + r->send_buf[0] = 0x7F; + r->send_buf[1] = r->recv_buf[0]; + r->send_buf[2] = response_code; + r->send_len = UDS_NEG_RESP_LEN; + return response_code; +} + +static inline void NoResponse(UDSReq_t *r) { r->send_len = 0; } + +static uint8_t EmitEvent(UDSServer_t *srv, UDSServerEvent_t evt, void *data) { + if (srv->fn) { + return srv->fn(srv, evt, data); + } else { + UDS_DBG_PRINT("Unhandled UDSServerEvent %d, srv.fn not installed!\n", evt); + return kGeneralReject; + } +} + +static uint8_t _0x10_DiagnosticSessionControl(UDSServer_t *srv, UDSReq_t *r) { + if (r->recv_len < UDS_0X10_REQ_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + uint8_t sessType = r->recv_buf[1] & 0x4F; + + UDSDiagSessCtrlArgs_t args = { + .type = sessType, + .p2_ms = UDS_CLIENT_DEFAULT_P2_MS, + .p2_star_ms = UDS_CLIENT_DEFAULT_P2_STAR_MS, + }; + + uint8_t err = EmitEvent(srv, UDS_SRV_EVT_DiagSessCtrl, &args); + + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + + srv->sessionType = sessType; + + switch (sessType) { + case kDefaultSession: + break; + case kProgrammingSession: + case kExtendedDiagnostic: + default: + srv->s3_session_timeout_timer = UDSMillis() + srv->s3_ms; + break; + } + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_DIAGNOSTIC_SESSION_CONTROL); + r->send_buf[1] = sessType; + + // UDS-1-2013: Table 29 + // resolution: 1ms + r->send_buf[2] = args.p2_ms >> 8; + r->send_buf[3] = args.p2_ms; + + // resolution: 10ms + r->send_buf[4] = (args.p2_star_ms / 10) >> 8; + r->send_buf[5] = args.p2_star_ms / 10; + + r->send_len = UDS_0X10_RESP_LEN; + return kPositiveResponse; +} + +static uint8_t _0x11_ECUReset(UDSServer_t *srv, UDSReq_t *r) { + uint8_t resetType = r->recv_buf[1] & 0x3F; + + if (r->recv_len < UDS_0X11_REQ_MIN_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + UDSECUResetArgs_t args = { + .type = resetType, + .powerDownTimeMillis = UDS_SERVER_DEFAULT_POWER_DOWN_TIME_MS, + }; + + uint8_t err = EmitEvent(srv, UDS_SRV_EVT_EcuReset, &args); + + if (kPositiveResponse == err) { + srv->notReadyToReceive = true; + srv->ecuResetScheduled = resetType; + srv->ecuResetTimer = UDSMillis() + args.powerDownTimeMillis; + } else { + return NegativeResponse(r, err); + } + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_ECU_RESET); + r->send_buf[1] = resetType; + + if (kEnableRapidPowerShutDown == resetType) { + uint32_t powerDownTime = args.powerDownTimeMillis / 1000; + if (powerDownTime > 255) { + powerDownTime = 255; + } + r->send_buf[2] = powerDownTime; + r->send_len = UDS_0X11_RESP_BASE_LEN + 1; + } else { + r->send_len = UDS_0X11_RESP_BASE_LEN; + } + return kPositiveResponse; +} + +static uint8_t safe_copy(UDSServer_t *srv, const void *src, uint16_t count) { + if (srv == NULL) { + return kGeneralReject; + } + UDSReq_t *r = (UDSReq_t *)&srv->r; + if (count <= r->send_buf_size - r->send_len) { + memmove(r->send_buf + r->send_len, src, count); + r->send_len += count; + return kPositiveResponse; + } + return kResponseTooLong; +} + +static uint8_t _0x22_ReadDataByIdentifier(UDSServer_t *srv, UDSReq_t *r) { + uint8_t numDIDs; + uint16_t dataId = 0; + uint8_t ret = kPositiveResponse; + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_READ_DATA_BY_IDENTIFIER); + r->send_len = 1; + + if (0 != (r->recv_len - 1) % sizeof(uint16_t)) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + numDIDs = r->recv_len / sizeof(uint16_t); + + if (0 == numDIDs) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + for (int did = 0; did < numDIDs; did++) { + uint16_t idx = 1 + did * 2; + dataId = (r->recv_buf[idx] << 8) + r->recv_buf[idx + 1]; + + if (r->send_len + 3 > r->send_buf_size) { + return NegativeResponse(r, kResponseTooLong); + } + uint8_t *copylocation = r->send_buf + r->send_len; + copylocation[0] = dataId >> 8; + copylocation[1] = dataId; + r->send_len += 2; + + UDSRDBIArgs_t args = { + .dataId = dataId, + .copy = safe_copy, + }; + + ret = EmitEvent(srv, UDS_SRV_EVT_ReadDataByIdent, &args); + + if (kPositiveResponse != ret) { + return NegativeResponse(r, ret); + } + } + return kPositiveResponse; +} + +/** + * @brief decode the addressAndLengthFormatIdentifier that appears in ReadMemoryByAddress (0x23), + * DynamicallyDefineDataIdentifier (0x2C), RequestDownload (0X34) + * + * @param srv + * @param buf pointer to addressAndDataLengthFormatIdentifier in recv_buf + * @param memoryAddress the decoded memory address + * @param memorySize the decoded memory size + * @return uint8_t + */ +static uint8_t decodeAddressAndLength(UDSReq_t *r, uint8_t *const buf, void **memoryAddress, + size_t *memorySize) { + assert(r); + assert(memoryAddress); + assert(memorySize); + uintptr_t tmp = 0; + *memoryAddress = 0; + *memorySize = 0; + + assert(buf >= r->recv_buf && buf <= r->recv_buf + sizeof(r->recv_buf)); + + if (r->recv_len < 3) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + uint8_t memorySizeLength = (buf[0] & 0xF0) >> 4; + uint8_t memoryAddressLength = buf[0] & 0x0F; + + if (memorySizeLength == 0 || memorySizeLength > sizeof(size_t)) { + return NegativeResponse(r, kRequestOutOfRange); + } + + if (memoryAddressLength == 0 || memoryAddressLength > sizeof(size_t)) { + return NegativeResponse(r, kRequestOutOfRange); + } + + if (buf + memorySizeLength + memoryAddressLength > r->recv_buf + r->recv_len) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + for (int byteIdx = 0; byteIdx < memoryAddressLength; byteIdx++) { + long long unsigned int byte = buf[1 + byteIdx]; + uint8_t shiftBytes = memoryAddressLength - 1 - byteIdx; + tmp |= byte << (8 * shiftBytes); + } + *memoryAddress = (void *)tmp; + + for (int byteIdx = 0; byteIdx < memorySizeLength; byteIdx++) { + uint8_t byte = buf[1 + memoryAddressLength + byteIdx]; + uint8_t shiftBytes = memorySizeLength - 1 - byteIdx; + *memorySize |= (size_t)byte << (8 * shiftBytes); + } + return kPositiveResponse; +} + +static uint8_t _0x23_ReadMemoryByAddress(UDSServer_t *srv, UDSReq_t *r) { + uint8_t ret = kPositiveResponse; + void *address = 0; + size_t length = 0; + + if (r->recv_len < UDS_0X23_REQ_MIN_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + ret = decodeAddressAndLength(r, &r->recv_buf[1], &address, &length); + if (kPositiveResponse != ret) { + return NegativeResponse(r, ret); + } + + UDSReadMemByAddrArgs_t args = { + .memAddr = address, + .memSize = length, + .copy = safe_copy, + }; + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_READ_MEMORY_BY_ADDRESS); + r->send_len = UDS_0X23_RESP_BASE_LEN; + ret = EmitEvent(srv, UDS_SRV_EVT_ReadMemByAddr, &args); + if (kPositiveResponse != ret) { + return NegativeResponse(r, ret); + } + if (r->send_len != UDS_0X23_RESP_BASE_LEN + length) { + return kGeneralProgrammingFailure; + } + return kPositiveResponse; +} + +static uint8_t _0x27_SecurityAccess(UDSServer_t *srv, UDSReq_t *r) { + uint8_t subFunction = r->recv_buf[1]; + uint8_t response = kPositiveResponse; + + if (UDSSecurityAccessLevelIsReserved(subFunction)) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + if (!UDSTimeAfter(UDSMillis(), srv->sec_access_boot_delay_timer)) { + return NegativeResponse(r, kRequiredTimeDelayNotExpired); + } + + if (!(UDSTimeAfter(UDSMillis(), srv->sec_access_auth_fail_timer))) { + return NegativeResponse(r, kExceedNumberOfAttempts); + } + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_SECURITY_ACCESS); + r->send_buf[1] = subFunction; + r->send_len = UDS_0X27_RESP_BASE_LEN; + + // Even: sendKey + if (0 == subFunction % 2) { + uint8_t requestedLevel = subFunction - 1; + UDSSecAccessValidateKeyArgs_t args = { + .level = requestedLevel, + .key = &r->recv_buf[UDS_0X27_REQ_BASE_LEN], + .len = r->recv_len - UDS_0X27_REQ_BASE_LEN, + }; + + response = EmitEvent(srv, UDS_SRV_EVT_SecAccessValidateKey, &args); + + if (kPositiveResponse != response) { + srv->sec_access_auth_fail_timer = + UDSMillis() + UDS_SERVER_0x27_BRUTE_FORCE_MITIGATION_AUTH_FAIL_DELAY_MS; + return NegativeResponse(r, response); + } + + // "requestSeed = 0x01" identifies a fixed relationship between + // "requestSeed = 0x01" and "sendKey = 0x02" + // "requestSeed = 0x03" identifies a fixed relationship between + // "requestSeed = 0x03" and "sendKey = 0x04" + srv->securityLevel = requestedLevel; + r->send_len = UDS_0X27_RESP_BASE_LEN; + return kPositiveResponse; + } + + // Odd: requestSeed + else { + /* If a server supports security, but the requested security level is already unlocked when + a SecurityAccess ‘requestSeed’ message is received, that server shall respond with a + SecurityAccess ‘requestSeed’ positive response message service with a seed value equal to + zero (0). The server shall never send an all zero seed for a given security level that is + currently locked. The client shall use this method to determine if a server is locked for a + particular security level by checking for a non-zero seed. + */ + if (subFunction == srv->securityLevel) { + // Table 52 sends a response of length 2. Use a preprocessor define if this needs + // customizing by the user. + const uint8_t already_unlocked[] = {0x00, 0x00}; + return safe_copy(srv, already_unlocked, sizeof(already_unlocked)); + } else { + UDSSecAccessRequestSeedArgs_t args = { + .level = subFunction, + .dataRecord = &r->recv_buf[UDS_0X27_REQ_BASE_LEN], + .len = r->recv_len - UDS_0X27_REQ_BASE_LEN, + .copySeed = safe_copy, + }; + + response = EmitEvent(srv, UDS_SRV_EVT_SecAccessRequestSeed, &args); + + if (kPositiveResponse != response) { + return NegativeResponse(r, response); + } + + if (r->send_len <= UDS_0X27_RESP_BASE_LEN) { // no data was copied + return NegativeResponse(r, kGeneralProgrammingFailure); + } + return kPositiveResponse; + } + } + return NegativeResponse(r, kGeneralProgrammingFailure); +} + +static uint8_t _0x28_CommunicationControl(UDSServer_t *srv, UDSReq_t *r) { + uint8_t controlType = r->recv_buf[1] & 0x7F; + uint8_t communicationType = r->recv_buf[2]; + + if (r->recv_len < UDS_0X28_REQ_BASE_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + UDSCommCtrlArgs_t args = { + .ctrlType = controlType, + .commType = communicationType, + }; + + uint8_t err = EmitEvent(srv, UDS_SRV_EVT_CommCtrl, &args); + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_COMMUNICATION_CONTROL); + r->send_buf[1] = controlType; + r->send_len = UDS_0X28_RESP_LEN; + return kPositiveResponse; +} + +static uint8_t _0x2E_WriteDataByIdentifier(UDSServer_t *srv, UDSReq_t *r) { + uint16_t dataLen = 0; + uint16_t dataId = 0; + uint8_t err = kPositiveResponse; + + /* UDS-1 2013 Figure 21 Key 1 */ + if (r->recv_len < UDS_0X2E_REQ_MIN_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + dataId = (r->recv_buf[1] << 8) + r->recv_buf[2]; + dataLen = r->recv_len - UDS_0X2E_REQ_BASE_LEN; + + UDSWDBIArgs_t args = { + .dataId = dataId, + .data = &r->recv_buf[UDS_0X2E_REQ_BASE_LEN], + .len = dataLen, + }; + + err = EmitEvent(srv, UDS_SRV_EVT_WriteDataByIdent, &args); + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_WRITE_DATA_BY_IDENTIFIER); + r->send_buf[1] = dataId >> 8; + r->send_buf[2] = dataId; + r->send_len = UDS_0X2E_RESP_LEN; + return kPositiveResponse; +} + +static uint8_t _0x31_RoutineControl(UDSServer_t *srv, UDSReq_t *r) { + uint8_t err = kPositiveResponse; + if (r->recv_len < UDS_0X31_REQ_MIN_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + uint8_t routineControlType = r->recv_buf[1] & 0x7F; + uint16_t routineIdentifier = (r->recv_buf[2] << 8) + r->recv_buf[3]; + + UDSRoutineCtrlArgs_t args = { + .ctrlType = routineControlType, + .id = routineIdentifier, + .optionRecord = &r->recv_buf[UDS_0X31_REQ_MIN_LEN], + .len = r->recv_len - UDS_0X31_REQ_MIN_LEN, + .copyStatusRecord = safe_copy, + }; + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_ROUTINE_CONTROL); + r->send_buf[1] = routineControlType; + r->send_buf[2] = routineIdentifier >> 8; + r->send_buf[3] = routineIdentifier; + r->send_len = UDS_0X31_RESP_MIN_LEN; + + switch (routineControlType) { + case kStartRoutine: + case kStopRoutine: + case kRequestRoutineResults: + err = EmitEvent(srv, UDS_SRV_EVT_RoutineCtrl, &args); + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + break; + default: + return NegativeResponse(r, kRequestOutOfRange); + } + return kPositiveResponse; +} + +static void ResetTransfer(UDSServer_t *srv) { + assert(srv); + srv->xferBlockSequenceCounter = 1; + srv->xferByteCounter = 0; + srv->xferTotalBytes = 0; + srv->xferIsActive = false; +} + +static uint8_t _0x34_RequestDownload(UDSServer_t *srv, UDSReq_t *r) { + uint8_t err; + void *memoryAddress = 0; + size_t memorySize = 0; + + if (srv->xferIsActive) { + return NegativeResponse(r, kConditionsNotCorrect); + } + + if (r->recv_len < UDS_0X34_REQ_BASE_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + err = decodeAddressAndLength(r, &r->recv_buf[2], &memoryAddress, &memorySize); + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + + UDSRequestDownloadArgs_t args = { + .addr = memoryAddress, + .size = memorySize, + .dataFormatIdentifier = r->recv_buf[1], + .maxNumberOfBlockLength = UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH, + }; + + err = EmitEvent(srv, UDS_SRV_EVT_RequestDownload, &args); + + if (args.maxNumberOfBlockLength < 3) { + UDS_DBG_PRINT("ERROR: maxNumberOfBlockLength too short"); + return NegativeResponse(r, kGeneralProgrammingFailure); + } + + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + + ResetTransfer(srv); + srv->xferIsActive = true; + srv->xferTotalBytes = memorySize; + srv->xferBlockLength = args.maxNumberOfBlockLength; + + // ISO-14229-1:2013 Table 401: + uint8_t lengthFormatIdentifier = sizeof(args.maxNumberOfBlockLength) << 4; + + /* ISO-14229-1:2013 Table 396: maxNumberOfBlockLength + This parameter is used by the requestDownload positive response message to + inform the client how many data bytes (maxNumberOfBlockLength) to include in + each TransferData request message from the client. This length reflects the + complete message length, including the service identifier and the + data-parameters present in the TransferData request message. + */ + if (args.maxNumberOfBlockLength > UDS_TP_MTU) { + args.maxNumberOfBlockLength = UDS_TP_MTU; + } + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_REQUEST_DOWNLOAD); + r->send_buf[1] = lengthFormatIdentifier; + for (uint8_t idx = 0; idx < sizeof(args.maxNumberOfBlockLength); idx++) { + uint8_t shiftBytes = sizeof(args.maxNumberOfBlockLength) - 1 - idx; + uint8_t byte = args.maxNumberOfBlockLength >> (shiftBytes * 8); + r->send_buf[UDS_0X34_RESP_BASE_LEN + idx] = byte; + } + r->send_len = UDS_0X34_RESP_BASE_LEN + sizeof(args.maxNumberOfBlockLength); + return kPositiveResponse; +} + +static uint8_t _0x35_RequestUpload(UDSServer_t *srv, UDSReq_t *r) { + uint8_t err; + void *memoryAddress = 0; + size_t memorySize = 0; + + if (srv->xferIsActive) { + return NegativeResponse(r, kConditionsNotCorrect); + } + + if (r->recv_len < UDS_0X35_REQ_BASE_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + + err = decodeAddressAndLength(r, &r->recv_buf[2], &memoryAddress, &memorySize); + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + + UDSRequestUploadArgs_t args = { + .addr = memoryAddress, + .size = memorySize, + .dataFormatIdentifier = r->recv_buf[1], + .maxNumberOfBlockLength = UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH, + }; + + err = EmitEvent(srv, UDS_SRV_EVT_RequestUpload, &args); + + if (args.maxNumberOfBlockLength < 3) { + UDS_DBG_PRINT("ERROR: maxNumberOfBlockLength too short"); + return NegativeResponse(r, kGeneralProgrammingFailure); + } + + if (kPositiveResponse != err) { + return NegativeResponse(r, err); + } + + ResetTransfer(srv); + srv->xferIsActive = true; + srv->xferTotalBytes = memorySize; + srv->xferBlockLength = args.maxNumberOfBlockLength; + + uint8_t lengthFormatIdentifier = sizeof(args.maxNumberOfBlockLength) << 4; + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_REQUEST_UPLOAD); + r->send_buf[1] = lengthFormatIdentifier; + for (uint8_t idx = 0; idx < sizeof(args.maxNumberOfBlockLength); idx++) { + uint8_t shiftBytes = sizeof(args.maxNumberOfBlockLength) - 1 - idx; + uint8_t byte = args.maxNumberOfBlockLength >> (shiftBytes * 8); + r->send_buf[UDS_0X35_RESP_BASE_LEN + idx] = byte; + } + r->send_len = UDS_0X35_RESP_BASE_LEN + sizeof(args.maxNumberOfBlockLength); + return kPositiveResponse; +} + +static uint8_t _0x36_TransferData(UDSServer_t *srv, UDSReq_t *r) { + uint8_t err = kPositiveResponse; + uint16_t request_data_len = r->recv_len - UDS_0X36_REQ_BASE_LEN; + uint8_t blockSequenceCounter = 0; + + if (!srv->xferIsActive) { + return NegativeResponse(r, kUploadDownloadNotAccepted); + } + + if (r->recv_len < UDS_0X36_REQ_BASE_LEN) { + err = kIncorrectMessageLengthOrInvalidFormat; + goto fail; + } + + blockSequenceCounter = r->recv_buf[1]; + + if (!srv->RCRRP) { + if (blockSequenceCounter != srv->xferBlockSequenceCounter) { + err = kRequestSequenceError; + goto fail; + } else { + srv->xferBlockSequenceCounter++; + } + } + + if (srv->xferByteCounter + request_data_len > srv->xferTotalBytes) { + err = kTransferDataSuspended; + goto fail; + } + + { + UDSTransferDataArgs_t args = { + .data = &r->recv_buf[UDS_0X36_REQ_BASE_LEN], + .len = r->recv_len - UDS_0X36_REQ_BASE_LEN, + .maxRespLen = srv->xferBlockLength - UDS_0X36_RESP_BASE_LEN, + .copyResponse = safe_copy, + }; + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_TRANSFER_DATA); + r->send_buf[1] = blockSequenceCounter; + r->send_len = UDS_0X36_RESP_BASE_LEN; + + err = EmitEvent(srv, UDS_SRV_EVT_TransferData, &args); + + switch (err) { + case kPositiveResponse: + srv->xferByteCounter += request_data_len; + return kPositiveResponse; + case kRequestCorrectlyReceived_ResponsePending: + return NegativeResponse(r, kRequestCorrectlyReceived_ResponsePending); + default: + goto fail; + } + } + +fail: + ResetTransfer(srv); + return NegativeResponse(r, err); +} + +static uint8_t _0x37_RequestTransferExit(UDSServer_t *srv, UDSReq_t *r) { + uint8_t err = kPositiveResponse; + + if (!srv->xferIsActive) { + return NegativeResponse(r, kUploadDownloadNotAccepted); + } + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_REQUEST_TRANSFER_EXIT); + r->send_len = UDS_0X37_RESP_BASE_LEN; + + UDSRequestTransferExitArgs_t args = { + .data = &r->recv_buf[UDS_0X37_REQ_BASE_LEN], + .len = r->recv_len - UDS_0X37_REQ_BASE_LEN, + .copyResponse = safe_copy, + }; + + err = EmitEvent(srv, UDS_SRV_EVT_RequestTransferExit, &args); + + switch (err) { + case kPositiveResponse: + ResetTransfer(srv); + return kPositiveResponse; + case kRequestCorrectlyReceived_ResponsePending: + return NegativeResponse(r, kRequestCorrectlyReceived_ResponsePending); + default: + ResetTransfer(srv); + return NegativeResponse(r, err); + } +} + +static uint8_t _0x3E_TesterPresent(UDSServer_t *srv, UDSReq_t *r) { + if ((r->recv_len < UDS_0X3E_REQ_MIN_LEN) || (r->recv_len > UDS_0X3E_REQ_MAX_LEN)) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + uint8_t zeroSubFunction = r->recv_buf[1]; + + switch (zeroSubFunction) { + case 0x00: + case 0x80: + srv->s3_session_timeout_timer = UDSMillis() + srv->s3_ms; + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_TESTER_PRESENT); + r->send_buf[1] = 0x00; + r->send_len = UDS_0X3E_RESP_LEN; + return kPositiveResponse; + default: + return NegativeResponse(r, kSubFunctionNotSupported); + } +} + +static uint8_t _0x85_ControlDTCSetting(UDSServer_t *srv, UDSReq_t *r) { + if (r->recv_len < UDS_0X85_REQ_BASE_LEN) { + return NegativeResponse(r, kIncorrectMessageLengthOrInvalidFormat); + } + uint8_t dtcSettingType = r->recv_buf[1] & 0x3F; + + r->send_buf[0] = UDS_RESPONSE_SID_OF(kSID_CONTROL_DTC_SETTING); + r->send_buf[1] = dtcSettingType; + r->send_len = UDS_0X85_RESP_LEN; + return kPositiveResponse; +} + +typedef uint8_t (*UDSService)(UDSServer_t *srv, UDSReq_t *r); + +/** + * @brief Get the internal service handler matching the given SID. + * @param sid + * @return pointer to UDSService or NULL if no match + */ +static UDSService getServiceForSID(uint8_t sid) { + switch (sid) { + case kSID_DIAGNOSTIC_SESSION_CONTROL: + return _0x10_DiagnosticSessionControl; + case kSID_ECU_RESET: + return _0x11_ECUReset; + case kSID_CLEAR_DIAGNOSTIC_INFORMATION: + return NULL; + case kSID_READ_DTC_INFORMATION: + return NULL; + case kSID_READ_DATA_BY_IDENTIFIER: + return _0x22_ReadDataByIdentifier; + case kSID_READ_MEMORY_BY_ADDRESS: + return _0x23_ReadMemoryByAddress; + case kSID_READ_SCALING_DATA_BY_IDENTIFIER: + return NULL; + case kSID_SECURITY_ACCESS: + return _0x27_SecurityAccess; + case kSID_COMMUNICATION_CONTROL: + return _0x28_CommunicationControl; + case kSID_READ_PERIODIC_DATA_BY_IDENTIFIER: + return NULL; + case kSID_DYNAMICALLY_DEFINE_DATA_IDENTIFIER: + return NULL; + case kSID_WRITE_DATA_BY_IDENTIFIER: + return _0x2E_WriteDataByIdentifier; + case kSID_INPUT_CONTROL_BY_IDENTIFIER: + return NULL; + case kSID_ROUTINE_CONTROL: + return _0x31_RoutineControl; + case kSID_REQUEST_DOWNLOAD: + return _0x34_RequestDownload; + case kSID_REQUEST_UPLOAD: + return _0x35_RequestUpload; + case kSID_TRANSFER_DATA: + return _0x36_TransferData; + case kSID_REQUEST_TRANSFER_EXIT: + return _0x37_RequestTransferExit; + case kSID_REQUEST_FILE_TRANSFER: + return NULL; + case kSID_WRITE_MEMORY_BY_ADDRESS: + return NULL; + case kSID_TESTER_PRESENT: + return _0x3E_TesterPresent; + case kSID_ACCESS_TIMING_PARAMETER: + return NULL; + case kSID_SECURED_DATA_TRANSMISSION: + return NULL; + case kSID_CONTROL_DTC_SETTING: + return _0x85_ControlDTCSetting; + case kSID_RESPONSE_ON_EVENT: + return NULL; + default: + UDS_DBG_PRINT("no handler for request SID %x.\n", sid); + return NULL; + } +} + +/** + * @brief Call the service if it exists, modifying the response if the spec calls for it. + * @note see UDS-1 2013 7.5.5 Pseudo code example of server response behavior + * + * @param srv + * @param addressingScheme + */ +static uint8_t evaluateServiceResponse(UDSServer_t *srv, UDSReq_t *r) { + uint8_t response = kPositiveResponse; + bool suppressResponse = false; + uint8_t sid = r->recv_buf[0]; + UDSService service = getServiceForSID(sid); + + if (NULL == service || NULL == srv->fn) { + return NegativeResponse(r, kServiceNotSupported); + } + assert(service); + assert(srv->fn); // service handler functions will call srv->fn. it must be valid + + switch (sid) { + /* CASE Service_with_sub-function */ + /* test if service with sub-function is supported */ + case kSID_DIAGNOSTIC_SESSION_CONTROL: + case kSID_ECU_RESET: + case kSID_SECURITY_ACCESS: + case kSID_COMMUNICATION_CONTROL: + case kSID_ROUTINE_CONTROL: + case kSID_TESTER_PRESENT: + case kSID_CONTROL_DTC_SETTING: { + response = service(srv, r); + + bool suppressPosRspMsgIndicationBit = r->recv_buf[1] & 0x80; + + /* test if positive response is required and if responseCode is positive 0x00 */ + if ((suppressPosRspMsgIndicationBit) && (response == kPositiveResponse) && + ( + // TODO: *not yet a NRC 0x78 response sent* + true)) { + suppressResponse = true; + } else { + suppressResponse = false; + } + break; + } + + /* CASE Service_without_sub-function */ + /* test if service without sub-function is supported */ + case kSID_READ_DATA_BY_IDENTIFIER: + case kSID_READ_MEMORY_BY_ADDRESS: + case kSID_WRITE_DATA_BY_IDENTIFIER: + case kSID_REQUEST_DOWNLOAD: + case kSID_REQUEST_UPLOAD: + case kSID_TRANSFER_DATA: + case kSID_REQUEST_TRANSFER_EXIT: { + response = service(srv, r); + break; + } + + /* CASE Service_not_implemented */ + /* shouldn't get this far as getServiceForSID(sid) will return NULL*/ + case kSID_CLEAR_DIAGNOSTIC_INFORMATION: + case kSID_READ_DTC_INFORMATION: + case kSID_READ_SCALING_DATA_BY_IDENTIFIER: + case kSID_READ_PERIODIC_DATA_BY_IDENTIFIER: + case kSID_DYNAMICALLY_DEFINE_DATA_IDENTIFIER: + case kSID_INPUT_CONTROL_BY_IDENTIFIER: + case kSID_REQUEST_FILE_TRANSFER: + case kSID_WRITE_MEMORY_BY_ADDRESS: + case kSID_ACCESS_TIMING_PARAMETER: + case kSID_SECURED_DATA_TRANSMISSION: + case kSID_RESPONSE_ON_EVENT: + default: { + response = kServiceNotSupported; + break; + } + } + + if ((UDS_A_TA_TYPE_FUNCTIONAL == r->info.A_TA_Type) && + ((kServiceNotSupported == response) || (kSubFunctionNotSupported == response) || + (kServiceNotSupportedInActiveSession == response) || + (kSubFunctionNotSupportedInActiveSession == response) || + (kRequestOutOfRange == response)) && + ( + // TODO: *not yet a NRC 0x78 response sent* + true)) { + suppressResponse = true; /* Suppress negative response message */ + NoResponse(r); + } else { + if (suppressResponse) { /* Suppress positive response message */ + NoResponse(r); + } else { /* send negative or positive response */ + ; + } + } + return response; +} + +// ======================================================================== +// Public Functions +// ======================================================================== + +/** + * @brief \~chinese 初始化服务器 \~english Initialize the server + * + * @param srv + * @param cfg + * @return int + */ +UDSErr_t UDSServerInit(UDSServer_t *srv) { + assert(srv); + memset(srv, 0, sizeof(UDSServer_t)); + srv->p2_ms = UDS_SERVER_DEFAULT_P2_MS; + srv->p2_star_ms = UDS_SERVER_DEFAULT_P2_STAR_MS; + srv->s3_ms = UDS_SERVER_DEFAULT_S3_MS; + srv->sessionType = kDefaultSession; + srv->p2_timer = UDSMillis() + srv->p2_ms; + srv->s3_session_timeout_timer = UDSMillis() + srv->s3_ms; + srv->sec_access_boot_delay_timer = + UDSMillis() + UDS_SERVER_0x27_BRUTE_FORCE_MITIGATION_BOOT_DELAY_MS; + srv->sec_access_auth_fail_timer = UDSMillis(); + return UDS_OK; +} + +void UDSServerPoll(UDSServer_t *srv) { + // UDS-1-2013 Figure 38: Session Timeout (S3) + if (kDefaultSession != srv->sessionType && + UDSTimeAfter(UDSMillis(), srv->s3_session_timeout_timer)) { + EmitEvent(srv, UDS_SRV_EVT_SessionTimeout, NULL); + } + + if (srv->ecuResetScheduled && UDSTimeAfter(UDSMillis(), srv->ecuResetTimer)) { + EmitEvent(srv, UDS_SRV_EVT_DoScheduledReset, &srv->ecuResetScheduled); + } + + UDSTpPoll(srv->tp); + + UDSReq_t *r = &srv->r; + + if (srv->requestInProgress) { + if (srv->RCRRP) { + // responds only if + // 1. changed (no longer RCRRP), or + // 2. p2_timer has elapsed + uint8_t response = evaluateServiceResponse(srv, r); + if (kRequestCorrectlyReceived_ResponsePending == response) { + // it's the second time the service has responded with RCRRP + srv->notReadyToReceive = true; + } else { + // No longer RCRRP'ing + srv->RCRRP = false; + srv->notReadyToReceive = false; + + // Not a consecutive 0x78 response, use p2 instead of p2_star * 0.3 + srv->p2_timer = UDSMillis() + srv->p2_ms; + } + } + + if (UDSTimeAfter(UDSMillis(), srv->p2_timer)) { + printf("len: %zu\n", r->send_len); + ssize_t ret = UDSTpSend(srv->tp, r->send_buf, r->send_len, NULL); + // TODO test injection of transport errors: + if (ret < 0) { + UDSErr_t err = UDS_ERR_TPORT; + EmitEvent(srv, UDS_SRV_EVT_Err, &err); + UDS_DBG_PRINT("UDSTpSend failed with %zd\n", ret); + } + + if (srv->RCRRP) { + // ISO14229-2:2013 Table 4 footnote b + // min time between consecutive 0x78 responses is 0.3 * p2* + uint32_t wait_time = srv->p2_star_ms * 3 / 10; + srv->p2_timer = UDSMillis() + wait_time; + } else { + srv->p2_timer = UDSMillis() + srv->p2_ms; + UDSTpAckRecv(srv->tp); + srv->requestInProgress = false; + } + } + + } else { + if (srv->notReadyToReceive) { + return; // cannot respond to request right now + } + r->recv_len = UDSTpPeek(srv->tp, &r->recv_buf, &r->info); + r->send_buf_size = UDSTpGetSendBuf(srv->tp, &r->send_buf); + if (r->recv_len > 0) { + if (r->send_buf == NULL) { + UDS_DBG_PRINT("Send buf null\n"); + } + if (r->recv_buf == NULL) { + UDS_DBG_PRINT("Recv buf null\n"); + } + if (r->send_buf == NULL || r->recv_buf == NULL) { + UDSErr_t err = UDS_ERR_TPORT; + EmitEvent(srv, UDS_SRV_EVT_Err, &err); + UDS_DBG_PRINT("bad tport\n"); + return; + } + uint8_t response = evaluateServiceResponse(srv, r); + srv->requestInProgress = true; + if (kRequestCorrectlyReceived_ResponsePending == response) { + srv->RCRRP = true; + } + } + } +} \ No newline at end of file diff --git a/src/server.h b/src/server.h new file mode 100644 index 0000000..e1e57da --- /dev/null +++ b/src/server.h @@ -0,0 +1,166 @@ +#pragma once + +#include "sys.h" +#include "tp.h" +#include "uds.h" + +/** + * @brief Server request context + */ +typedef struct { + uint8_t *recv_buf; + uint8_t *send_buf; + size_t recv_len; + size_t send_len; + size_t send_buf_size; + UDSSDU_t info; +} UDSReq_t; + +typedef struct UDSServer { + UDSTpHandle_t *tp; + uint8_t (*fn)(struct UDSServer *srv, UDSServerEvent_t event, const void *arg); + + /** + * @brief \~chinese 服务器时间参数(毫秒) \~ Server time constants (milliseconds) \~ + */ + uint16_t p2_ms; // Default P2_server_max timing supported by the server for + // the activated diagnostic session. + uint32_t p2_star_ms; // Enhanced (NRC 0x78) P2_server_max supported by the + // server for the activated diagnostic session. + uint16_t s3_ms; // Session timeout + + uint8_t ecuResetScheduled; // nonzero indicates that an ECUReset has been scheduled + uint32_t ecuResetTimer; // for delaying resetting until a response + // has been sent to the client + uint32_t p2_timer; // for rate limiting server responses + uint32_t s3_session_timeout_timer; // indicates that diagnostic session has timed out + uint32_t sec_access_auth_fail_timer; // brute-force hardening: rate limit security access + // requests + uint32_t sec_access_boot_delay_timer; // brute-force hardening: restrict security access until + // timer expires + + /** + * @brief UDS-1-2013: Table 407 - 0x36 TransferData Supported negative + * response codes requires that the server keep track of whether the + * transfer is active + */ + bool xferIsActive; + // UDS-1-2013: 14.4.2.3, Table 404: The blockSequenceCounter parameter + // value starts at 0x01 + uint8_t xferBlockSequenceCounter; + size_t xferTotalBytes; // total transfer size in bytes requested by the client + size_t xferByteCounter; // total number of bytes transferred + size_t xferBlockLength; // block length (convenience for the TransferData API) + + uint8_t sessionType; // diagnostic session type (0x10) + uint8_t securityLevel; // SecurityAccess (0x27) level + + bool RCRRP; // set to true when user fn returns 0x78 and false otherwise + bool requestInProgress; // set to true when a request has been processed but the response has + // not yet been sent + + // UDS-1 2013 defines the following conditions under which the server does not + // process incoming requests: + // - not ready to receive (Table A.1 0x78) + // - not accepting request messages and not sending responses (9.3.1) + // + // when this variable is set to true, incoming ISO-TP data will not be processed. + bool notReadyToReceive; + + UDSReq_t r; +} UDSServer_t; + +typedef struct { + const uint8_t type; /*! requested diagnostic session type (enum UDSDiagnosticSessionType) */ + uint16_t p2_ms; /*! optional: p2 timing override */ + uint32_t p2_star_ms; /*! optional: p2* timing override */ +} UDSDiagSessCtrlArgs_t; + +typedef struct { + const uint8_t type; /**< \~chinese 客户端请求的复位类型 \~english reset type requested by client + (enum UDSECUResetType) */ + uint32_t powerDownTimeMillis; /**< when this much time has elapsed after a kPositiveResponse, a + UDS_SRV_EVT_DoScheduledReset will be issued */ +} UDSECUResetArgs_t; + +typedef struct { + const uint16_t dataId; /*! RDBI Data Identifier */ + uint8_t (*copy)(UDSServer_t *srv, const void *src, + uint16_t count); /*! function for copying data */ +} UDSRDBIArgs_t; + +typedef struct { + const void *memAddr; + const size_t memSize; + uint8_t (*copy)(UDSServer_t *srv, const void *src, + uint16_t count); /*! function for copying data */ +} UDSReadMemByAddrArgs_t; + +typedef struct { + uint8_t ctrlType; /* enum UDSCommunicationControlType */ + uint8_t commType; /* enum UDSCommunicationType */ +} UDSCommCtrlArgs_t; + +typedef struct { + const uint8_t level; /*! requested security level */ + const uint8_t *const dataRecord; /*! pointer to request data */ + const uint16_t len; /*! size of request data */ + uint8_t (*copySeed)(UDSServer_t *srv, const void *src, + uint16_t len); /*! function for copying data */ +} UDSSecAccessRequestSeedArgs_t; + +typedef struct { + const uint8_t level; /*! security level to be validated */ + const uint8_t *const key; /*! key sent by client */ + const uint16_t len; /*! length of key */ +} UDSSecAccessValidateKeyArgs_t; + +typedef struct { + const uint16_t dataId; /*! WDBI Data Identifier */ + const uint8_t *const data; /*! pointer to data */ + const uint16_t len; /*! length of data */ +} UDSWDBIArgs_t; + +typedef struct { + const uint8_t ctrlType; /*! routineControlType */ + const uint16_t id; /*! routineIdentifier */ + const uint8_t *optionRecord; /*! optional data */ + const uint16_t len; /*! length of optional data */ + uint8_t (*copyStatusRecord)(UDSServer_t *srv, const void *src, + uint16_t len); /*! function for copying response data */ +} UDSRoutineCtrlArgs_t; + +typedef struct { + const void *addr; /*! requested address */ + const size_t size; /*! requested download size */ + const uint8_t dataFormatIdentifier; /*! optional specifier for format of data */ + uint16_t maxNumberOfBlockLength; /*! optional response: inform client how many data bytes to + send in each `TransferData` request */ +} UDSRequestDownloadArgs_t; + +typedef struct { + const void *addr; /*! requested address */ + const size_t size; /*! requested download size */ + const uint8_t dataFormatIdentifier; /*! optional specifier for format of data */ + uint16_t maxNumberOfBlockLength; /*! optional response: inform client how many data bytes to + send in each `TransferData` request */ +} UDSRequestUploadArgs_t; + +typedef struct { + const uint8_t *const data; /*! transfer data */ + const uint16_t len; /*! transfer data length */ + const uint16_t maxRespLen; /*! don't send more than this many bytes with copyResponse */ + uint8_t (*copyResponse)( + UDSServer_t *srv, const void *src, + uint16_t len); /*! function for copying transfer data response data (optional) */ +} UDSTransferDataArgs_t; + +typedef struct { + const uint8_t *const data; /*! request data */ + const uint16_t len; /*! request data length */ + uint8_t (*copyResponse)(UDSServer_t *srv, const void *src, + uint16_t len); /*! function for copying response data (optional) */ +} UDSRequestTransferExitArgs_t; + +UDSErr_t UDSServerInit(UDSServer_t *srv); +void UDSServerPoll(UDSServer_t *srv); diff --git a/src/sys.h b/src/sys.h new file mode 100644 index 0000000..8afea10 --- /dev/null +++ b/src/sys.h @@ -0,0 +1,28 @@ +#pragma once + +#define UDS_SYS_CUSTOM 0 +#define UDS_SYS_UNIX 1 +#define UDS_SYS_WINDOWS 2 +#define UDS_SYS_ARDUINO 3 +#define UDS_SYS_ESP32 4 + +#if !defined(UDS_SYS) + +#if defined(__unix__) || defined(__APPLE__) +#define UDS_SYS UDS_SYS_UNIX +#elif defined(_WIN32) +#define UDS_SYS UDS_SYS_WINDOWS +#elif defined(ARDUINO) +#define UDS_SYS UDS_SYS_ARDUINO +#elif defined(ESP_PLATFORM) +#define UDS_SYS UDS_SYS_ESP32 +#else +#define UDS_SYS UDS_SYS_CUSTOM +#endif + +#endif + +#include "sys_unix.h" +#include "sys_win32.h" +#include "sys_arduino.h" +#include "sys_esp32.h" diff --git a/src/sys_arduino.h b/src/sys_arduino.h new file mode 100644 index 0000000..a926d1b --- /dev/null +++ b/src/sys_arduino.h @@ -0,0 +1,16 @@ +#pragma once + +#if UDS_SYS == UDS_SYS_ARDUINO + +#include +#include +#include +#include + +#define UDS_TP UDS_TP_ISOTP_C +#define UDS_ENABLE_DBG_PRINT 1 +#define UDS_ENABLE_ASSERT 1 +int print_impl(const char *fmt, ...); +#define UDS_DBG_PRINT_IMPL print_impl + +#endif diff --git a/src/sys_esp32.h b/src/sys_esp32.h new file mode 100644 index 0000000..ccb41e1 --- /dev/null +++ b/src/sys_esp32.h @@ -0,0 +1,13 @@ +#pragma once + +#if UDS_SYS == UDS_SYS_ESP32 + +#include +#include +#include + +#define UDS_TP_ISOTP_C 1 +#define UDS_ENABLE_DBG_PRINT 1 +#define UDS_ENABLE_ASSERT 1 + +#endif diff --git a/src/sys_unix.h b/src/sys_unix.h new file mode 100644 index 0000000..6222363 --- /dev/null +++ b/src/sys_unix.h @@ -0,0 +1,15 @@ +#pragma once + +#if UDS_SYS == UDS_SYS_UNIX + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#endif diff --git a/src/sys_win32.h b/src/sys_win32.h new file mode 100644 index 0000000..0bf3d94 --- /dev/null +++ b/src/sys_win32.h @@ -0,0 +1,8 @@ +#pragma once + +#if UDS_SYS == UDS_SYS_WIN32 + +#include +typedef SSIZE_T ssize_t; + +#endif diff --git a/src/tp.c b/src/tp.c new file mode 100644 index 0000000..63106d7 --- /dev/null +++ b/src/tp.c @@ -0,0 +1,64 @@ +#include "tp.h" + +/** + * @brief + * + * @param hdl + * @param info, if NULL, the default values are used: + * A_Mtype: message type (diagnostic (DEFAULT), remote diagnostic, secure diagnostic, secure + * remote diagnostic) + * A_TA_Type: application target address type (physical (DEFAULT) or functional) + * A_SA: unused + * A_TA: unused + * A_AE: unused + * @return ssize_t + */ +ssize_t UDSTpGetSendBuf(struct UDSTpHandle *hdl, uint8_t **buf) { + assert(hdl); + assert(hdl->get_send_buf); + return hdl->get_send_buf(hdl, buf); +} +ssize_t UDSTpSend(struct UDSTpHandle *hdl, const uint8_t *buf, ssize_t len, UDSSDU_t *info) { + assert(hdl); + assert(hdl->send); + return hdl->send(hdl, (uint8_t *)buf, len, info); +} + +UDSTpStatus_t UDSTpPoll(struct UDSTpHandle *hdl) { + assert(hdl); + assert(hdl->poll); + return hdl->poll(hdl); +} + +ssize_t UDSTpPeek(struct UDSTpHandle *hdl, uint8_t **buf, UDSSDU_t *info) { + assert(hdl); + assert(hdl->peek); + return hdl->peek(hdl, buf, info); +} + +const uint8_t *UDSTpGetRecvBuf(struct UDSTpHandle *hdl, size_t *p_len) { + assert(hdl); + ssize_t len = 0; + uint8_t *buf = NULL; + len = UDSTpPeek(hdl, &buf, NULL); + if (len > 0) { + if (p_len) { + *p_len = len; + } + return buf; + } else { + return NULL; + } +} + +size_t UDSTpGetRecvLen(UDSTpHandle_t *hdl) { + assert(hdl); + size_t len = 0; + UDSTpGetRecvBuf(hdl, &len); + return len; +} + +void UDSTpAckRecv(UDSTpHandle_t *hdl) { + assert(hdl); + hdl->ack_recv(hdl); +} diff --git a/src/tp.h b/src/tp.h new file mode 100644 index 0000000..a32fcf3 --- /dev/null +++ b/src/tp.h @@ -0,0 +1,103 @@ +#pragma once + +#include "sys.h" + +#if defined(UDS_TP_ISOTP_C) || defined(UDS_TP_ISOTP_C_SOCKETCAN) +#define UDS_ISOTP_C +#endif + +enum UDSTpStatusFlags { + UDS_TP_IDLE = 0x00000000, + UDS_TP_SEND_IN_PROGRESS = 0x00000001, + UDS_TP_RECV_COMPLETE = 0x00000002, +}; + +typedef uint32_t UDSTpStatus_t; + +typedef enum { + UDS_A_MTYPE_DIAG = 0, + UDS_A_MTYPE_REMOTE_DIAG, + UDS_A_MTYPE_SECURE_DIAG, + UDS_A_MTYPE_SECURE_REMOTE_DIAG, +} UDS_A_Mtype_t; + +typedef enum { + UDS_A_TA_TYPE_PHYSICAL = 0, // unicast (1:1) + UDS_A_TA_TYPE_FUNCTIONAL, // multicast +} UDS_A_TA_Type_t; + +typedef uint8_t UDSTpAddr_t; + +/** + * @brief Service data unit (SDU) + * @details data interface between the application layer and the transport layer + */ +typedef struct { + UDS_A_Mtype_t A_Mtype; // message type (diagnostic, remote diagnostic, secure diagnostic, secure + // remote diagnostic) + uint16_t A_SA; // application source address + uint16_t A_TA; // application target address + UDS_A_TA_Type_t A_TA_Type; // application target address type (physical or functional) + uint16_t A_AE; // application layer remote address +} UDSSDU_t; + +#define UDS_TP_NOOP_ADDR (0xFFFFFFFF) + +/** + * @brief Interface to OSI layer 4 (transport layer) + * @note implementers should embed this struct at offset zero in their own transport layer handle + */ +typedef struct UDSTpHandle { + /** + * @brief Get the transport layer's send buffer + * @param hdl: pointer to transport handle + * @param buf: double pointer which will be pointed to the send buffer + * @return size of transport layer's send buffer on success, -1 on error + */ + ssize_t (*get_send_buf)(struct UDSTpHandle *hdl, uint8_t **p_buf); + + /** + * @brief Send the data in the buffer buf + * @param hdl: pointer to transport handle + * @param buf: a pointer to the data to send (this may be the buffer returned by @ref + * get_send_buf) + * @param info: pointer to SDU info (may be NULL). If NULL, implementation should send with + * physical addressing + */ + ssize_t (*send)(struct UDSTpHandle *hdl, uint8_t *buf, size_t len, UDSSDU_t *info); + + /** + * @brief Poll the transport layer. + * @param hdl: pointer to transport handle + * @note the transport layer user is responsible for calling this function periodically + * @note threaded implementations like linux isotp sockets don't need to do anything here. + * @return UDS_TP_IDLE if idle, otherwise UDS_TP_SEND_IN_PROGRESS or UDS_TP_RECV_COMPLETE + */ + UDSTpStatus_t (*poll)(struct UDSTpHandle *hdl); + + /** + * @brief Peek at the received data + * @param hdl: pointer to transport handle + * @param buf: set to the received data + * @param info: filled with SDU info by the callee if not NULL + * @return size of received data on success, -1 on error + * @note The transport will be unable to receive further data until @ref ack_recv is called + * @note The information returned by peek will not change until @ref ack_recv is called + */ + ssize_t (*peek)(struct UDSTpHandle *hdl, uint8_t **buf, UDSSDU_t *info); + + /** + * @brief Acknowledge that the received data has been processed and may be discarded + * @param hdl: pointer to transport handle + * @note: after ack_recv() is called and before new messages are received, peek must return 0. + */ + void (*ack_recv)(struct UDSTpHandle *hdl); +} UDSTpHandle_t; + +ssize_t UDSTpGetSendBuf(UDSTpHandle_t *hdl, uint8_t **buf); +ssize_t UDSTpSend(UDSTpHandle_t *hdl, const uint8_t *buf, ssize_t len, UDSSDU_t *info); +UDSTpStatus_t UDSTpPoll(UDSTpHandle_t *hdl); +ssize_t UDSTpPeek(struct UDSTpHandle *hdl, uint8_t **buf, UDSSDU_t *info); +const uint8_t *UDSTpGetRecvBuf(UDSTpHandle_t *hdl, size_t *len); +size_t UDSTpGetRecvLen(UDSTpHandle_t *hdl); +void UDSTpAckRecv(UDSTpHandle_t *hdl); diff --git a/src/tp/README.md b/src/tp/README.md new file mode 100644 index 0000000..ccba17e --- /dev/null +++ b/src/tp/README.md @@ -0,0 +1,66 @@ + +The transport layer / session layer interface is a little complex. + +- see iso14229.h `UDSTpHandle_t` + - implement all callback functions +- do sanity checks on init + +```c +#include "assert.h" +assert(cfg->source_addr != cfg->target_addr); +assert(cfg->target_addr != cfg->source_addr_func); +assert(cfg->source_addr_func != cfg->source_addr); + +assert(cfg->tp->recv); +assert(cfg->tp->send); +assert(cfg->tp->poll); + +#if foo +#elif UDS_TP == UDS_TP_ISOTP_C + assert(cfg->target_addr != cfg->source_addr_func && cfg->source_addr_func != cfg->source_addr); + UDSTpIsoTpC_t *tp = &self->tp_impl; + isotp_init_link(&tp->phys_link, cfg->target_addr, self->send_buf, self->send_buf_size, + self->recv_buf, self->recv_buf_size); + isotp_init_link(&tp->func_link, cfg->target_addr, tp->func_send_buf, sizeof(tp->func_send_buf), + tp->func_recv_buf, sizeof(tp->func_recv_buf)); + self->tp = (UDSTpHandle_t *)tp; + self->tp->poll = tp_poll; + self->tp->send = tp_send; + self->tp->recv = tp_recv; +#elif UDS_TP == UDS_TP_ISOTP_SOCKET + self->tp = (UDSTpHandle_t *)&self->tp_impl; + if (LinuxSockTpOpen(self->tp, cfg->if_name, cfg->source_addr, cfg->target_addr, + cfg->source_addr_func, cfg->target_addr)) { + return UDS_ERR; + } + +// client + +#if UDS_TP == UDS_TP_CUSTOM + assert(cfg->tp); + assert(cfg->tp->recv); + assert(cfg->tp->send); + assert(cfg->tp->poll); + client->tp = cfg->tp; +#elif UDS_TP == UDS_TP_ISOTP_C + assert(cfg->source_addr != cfg->target_addr_func && cfg->target_addr_func != cfg->target_addr); + UDSTpIsoTpC_t *tp = (UDSTpIsoTpC_t *)&client->tp_impl; + isotp_init_link(&tp->phys_link, cfg->target_addr, client->send_buf, client->send_buf_size, + client->recv_buf, client->recv_buf_size); + isotp_init_link(&tp->func_link, cfg->target_addr_func, tp->func_send_buf, + sizeof(tp->func_send_buf), tp->func_recv_buf, sizeof(tp->func_recv_buf)); + client->tp = (UDSTpHandle_t *)tp; + client->tp->poll = tp_poll; + client->tp->send = tp_send; + client->tp->recv = tp_recv; +#elif UDS_TP == UDS_TP_ISOTP_SOCKET + client->tp = (UDSTpHandle_t *)&client->tp_impl; + if (LinuxSockTpOpen(client->tp, cfg->if_name, cfg->source_addr, cfg->target_addr, + cfg->source_addr, cfg->target_addr_func)) { + return UDS_ERR; + } + assert(client->tp); +#endif + + +``` \ No newline at end of file diff --git a/isotp-c/.gitignore b/src/tp/isotp-c/.gitignore similarity index 96% rename from isotp-c/.gitignore rename to src/tp/isotp-c/.gitignore index 0d36244..81bf9b5 100644 --- a/isotp-c/.gitignore +++ b/src/tp/isotp-c/.gitignore @@ -9,7 +9,6 @@ ### *.o bin/ -build/ ### # Ignore Backups diff --git a/isotp-c/.travis.yml b/src/tp/isotp-c/.travis.yml similarity index 100% rename from isotp-c/.travis.yml rename to src/tp/isotp-c/.travis.yml diff --git a/isotp-c/CMakeLists.txt b/src/tp/isotp-c/CMakeLists.txt similarity index 100% rename from isotp-c/CMakeLists.txt rename to src/tp/isotp-c/CMakeLists.txt diff --git a/isotp-c/LICENSE b/src/tp/isotp-c/LICENSE similarity index 100% rename from isotp-c/LICENSE rename to src/tp/isotp-c/LICENSE diff --git a/isotp-c/Makefile b/src/tp/isotp-c/Makefile similarity index 100% rename from isotp-c/Makefile rename to src/tp/isotp-c/Makefile diff --git a/isotp-c/README.md b/src/tp/isotp-c/README.mkd similarity index 89% rename from isotp-c/README.md rename to src/tp/isotp-c/README.mkd index 1a2fc3b..4cc5093 100644 --- a/isotp-c/README.md +++ b/src/tp/isotp-c/README.mkd @@ -61,7 +61,10 @@ You can use isotp-c in the following way: /* Initialize link, 0x7TT is the CAN ID you send with */ isotp_init_link(&g_link, 0x7TT, g_isotpSendBuf, sizeof(g_isotpSendBuf), - g_isotpRecvBuf, sizeof(g_isotpRecvBuf)); + g_isotpRecvBuf, sizeof(g_isotpRecvBuf), + isotp_user_get_ms, + isotp_user_send_can, + isotp_user_debug); while(1) { @@ -128,10 +131,16 @@ If you need handle functional addressing, you must use two separate links, one f /* Initialize link, 0x7TT is the CAN ID you send with */ isotp_init_link(&g_phylink, 0x7TT, g_isotpPhySendBuf, sizeof(g_isotpPhySendBuf), - g_isotpPhyRecvBuf, sizeof(g_isotpPhyRecvBuf)); + g_isotpPhyRecvBuf, sizeof(g_isotpPhyRecvBuf), + isotp_user_get_ms, + isotp_user_send_can, + isotp_user_debug); isotp_init_link(&g_funclink, 0x7TT, g_isotpFuncSendBuf, sizeof(g_isotpFuncSendBuf), - g_isotpFuncRecvBuf, sizeof(g_isotpFuncRecvBuf)); + g_isotpFuncRecvBuf, sizeof(g_isotpFuncRecvBuf), + isotp_user_get_ms, + isotp_user_send_can, + isotp_user_debug); while(1) { @@ -182,7 +191,7 @@ If you need handle functional addressing, you must use two separate links, one f ## Authors * **shen.li lishen5@gmail.com** (Original author!) -* **Simon Cahill** **s.cahill@grimme.de** (or **contact@simonc.eu**) +* **Simon Cahill** **s.cahill@grimme.de** (or **simon@h3lix.de**) ## License diff --git a/isotp-c/isotp.c b/src/tp/isotp-c/isotp.c similarity index 78% rename from isotp-c/isotp.c rename to src/tp/isotp-c/isotp.c index b1f4807..737911c 100644 --- a/isotp-c/isotp.c +++ b/src/tp/isotp-c/isotp.c @@ -7,26 +7,33 @@ /////////////////////////////////////////////////////// /* st_min to microsecond */ -static uint8_t isotp_us_to_st_min(uint32_t us) { - if (us <= 127000) { - return us / 1000; - } else if (us >= 100 && us <= 900) { - return 0xF0 + (us / 100); +static uint8_t isotp_ms_to_st_min(uint8_t ms) { + uint8_t st_min; + + st_min = ms; + if (st_min > 0x7F) { + st_min = 0x7F; } - return 0; + + return st_min; } -/* st_min to usec */ -static uint32_t isotp_st_min_to_us(uint8_t st_min) { - if (st_min <= 0x7F) { - return st_min * 1000; - } else if (st_min >= 0xF1 && st_min <= 0xF9) { - return (st_min - 0xF0) * 100; +/* st_min to msec */ +static uint8_t isotp_st_min_to_ms(uint8_t st_min) { + uint8_t ms; + + if (st_min >= 0xF1 && st_min <= 0xF9) { + ms = 1; + } else if (st_min <= 0x7F) { + ms = st_min; + } else { + ms = 0; } - return 0; + + return ms; } -static int isotp_send_flow_control(IsoTpLink* link, uint8_t flow_status, uint8_t block_size, uint32_t st_min_us) { +static int isotp_send_flow_control(IsoTpLink* link, uint8_t flow_status, uint8_t block_size, uint8_t st_min_ms) { IsoTpCanMessage message; int ret; @@ -35,14 +42,14 @@ static int isotp_send_flow_control(IsoTpLink* link, uint8_t flow_status, uint8_t message.as.flow_control.type = ISOTP_PCI_TYPE_FLOW_CONTROL_FRAME; message.as.flow_control.FS = flow_status; message.as.flow_control.BS = block_size; - message.as.flow_control.STmin = isotp_us_to_st_min(st_min_us); + message.as.flow_control.STmin = isotp_ms_to_st_min(st_min_ms); /* send message */ #ifdef ISO_TP_FRAME_PADDING (void) memset(message.as.flow_control.reserve, 0, sizeof(message.as.flow_control.reserve)); - ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, sizeof(message)); + ret = link->isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, sizeof(message), link->user_data); #else - ret = isotp_user_send_can(link->send_arbitration_id, + ret = link->isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, 3); #endif @@ -66,9 +73,9 @@ static int isotp_send_single_frame(IsoTpLink* link, uint32_t id) { /* send message */ #ifdef ISO_TP_FRAME_PADDING (void) memset(message.as.single_frame.data + link->send_size, 0, sizeof(message.as.single_frame.data) - link->send_size); - ret = isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message)); + ret = link->isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message), link->user_data); #else - ret = isotp_user_send_can(id, + ret = link->isotp_user_send_can(id, message.as.data_array.ptr, link->send_size + 1); #endif @@ -91,7 +98,7 @@ static int isotp_send_first_frame(IsoTpLink* link, uint32_t id) { (void) memcpy(message.as.first_frame.data, link->send_buffer, sizeof(message.as.first_frame.data)); /* send message */ - ret = isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message)); + ret = link->isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message), link->user_data); if (ISOTP_RET_OK == ret) { link->send_offset += sizeof(message.as.first_frame.data); link->send_sn = 1; @@ -121,9 +128,9 @@ static int isotp_send_consecutive_frame(IsoTpLink* link) { /* send message */ #ifdef ISO_TP_FRAME_PADDING (void) memset(message.as.consecutive_frame.data + data_length, 0, sizeof(message.as.consecutive_frame.data) - data_length); - ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, sizeof(message)); + ret = link->isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, sizeof(message), link->user_data); #else - ret = isotp_user_send_can(link->send_arbitration_id, + ret = link->isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, data_length + 1); #endif @@ -140,7 +147,7 @@ static int isotp_send_consecutive_frame(IsoTpLink* link) { static int isotp_receive_single_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) { /* check data length */ if ((0 == message->as.single_frame.SF_DL) || (message->as.single_frame.SF_DL > (len - 1))) { - isotp_user_debug("Single-frame length too small."); + link->isotp_user_debug("Single-frame length too small."); return ISOTP_RET_LENGTH; } @@ -155,7 +162,7 @@ static int isotp_receive_first_frame(IsoTpLink *link, IsoTpCanMessage *message, uint16_t payload_length; if (8 != len) { - isotp_user_debug("First frame should be 8 bytes in length."); + link->isotp_user_debug("First frame should be 8 bytes in length."); return ISOTP_RET_LENGTH; } @@ -165,12 +172,12 @@ static int isotp_receive_first_frame(IsoTpLink *link, IsoTpCanMessage *message, /* should not use multiple frame transmition */ if (payload_length <= 7) { - isotp_user_debug("Should not use multiple frame transmission."); + link->isotp_user_debug("Should not use multiple frame transmission."); return ISOTP_RET_LENGTH; } if (payload_length > link->receive_buf_size) { - isotp_user_debug("Multi-frame response too large for receiving buffer."); + link->isotp_user_debug("Multi-frame response too large for receiving buffer."); return ISOTP_RET_OVERFLOW; } @@ -197,7 +204,7 @@ static int isotp_receive_consecutive_frame(IsoTpLink *link, IsoTpCanMessage *mes remaining_bytes = sizeof(message->as.consecutive_frame.data); } if (remaining_bytes > len - 1) { - isotp_user_debug("Consecutive frame too short."); + link->isotp_user_debug("Consecutive frame too short."); return ISOTP_RET_LENGTH; } @@ -215,7 +222,7 @@ static int isotp_receive_consecutive_frame(IsoTpLink *link, IsoTpCanMessage *mes static int isotp_receive_flow_control_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) { /* check message length */ if (len < 3) { - isotp_user_debug("Flow control frame too short."); + link->isotp_user_debug("Flow control frame too short."); return ISOTP_RET_LENGTH; } @@ -234,20 +241,19 @@ int isotp_send_with_id(IsoTpLink *link, uint32_t id, const uint8_t payload[], ui int ret; if (link == 0x0) { - isotp_user_debug("Link is null!"); + link->isotp_user_debug("Link is null!"); return ISOTP_RET_ERROR; } if (size > link->send_buf_size) { - isotp_user_debug("Message size too large. Increase ISO_TP_MAX_MESSAGE_SIZE to set a larger buffer\n"); + link->isotp_user_debug("Message size too large. Increase ISO_TP_MAX_MESSAGE_SIZE to set a larger buffer\n"); char message[128]; sprintf(&message[0], "Attempted to send %d bytes; max size is %d!\n", size, link->send_buf_size); - isotp_user_debug(message); return ISOTP_RET_OVERFLOW; } if (ISOTP_SEND_STATUS_INPROGRESS == link->send_status) { - isotp_user_debug("Abort previous message, transmission in progress.\n"); + link->isotp_user_debug("Abort previous message, transmission in progress.\n"); return ISOTP_RET_INPROGRESS; } @@ -266,10 +272,10 @@ int isotp_send_with_id(IsoTpLink *link, uint32_t id, const uint8_t payload[], ui /* init multi-frame control flags */ if (ISOTP_RET_OK == ret) { link->send_bs_remain = 0; - link->send_st_min_us = 0; + link->send_st_min = 0; link->send_wtf_count = 0; - link->send_timer_st = isotp_user_get_us(); - link->send_timer_bs = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; + link->send_timer_st = link->isotp_user_get_ms(); + link->send_timer_bs = link->isotp_user_get_ms() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT; link->send_protocol_result = ISOTP_PROTOCOL_RESULT_OK; link->send_status = ISOTP_SEND_STATUS_INPROGRESS; } @@ -335,9 +341,9 @@ void isotp_on_can_message(IsoTpLink *link, uint8_t *data, uint8_t len) { link->receive_status = ISOTP_RECEIVE_STATUS_INPROGRESS; /* send fc frame */ link->receive_bs_count = ISO_TP_DEFAULT_BLOCK_SIZE; - isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE, link->receive_bs_count, ISO_TP_DEFAULT_ST_MIN_US); + isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE, link->receive_bs_count, ISO_TP_DEFAULT_ST_MIN); /* refresh timer cs */ - link->receive_timer_cr = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; + link->receive_timer_cr = link->isotp_user_get_ms() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT; } break; @@ -362,7 +368,7 @@ void isotp_on_can_message(IsoTpLink *link, uint8_t *data, uint8_t len) { /* if success */ if (ISOTP_RET_OK == ret) { /* refresh timer cs */ - link->receive_timer_cr = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; + link->receive_timer_cr = link->isotp_user_get_ms() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT; /* receive finished */ if (link->receive_offset >= link->receive_size) { @@ -371,7 +377,7 @@ void isotp_on_can_message(IsoTpLink *link, uint8_t *data, uint8_t len) { /* send fc when bs reaches limit */ if (0 == --link->receive_bs_count) { link->receive_bs_count = ISO_TP_DEFAULT_BLOCK_SIZE; - isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE, link->receive_bs_count, ISO_TP_DEFAULT_ST_MIN_US); + isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE, link->receive_bs_count, ISO_TP_DEFAULT_ST_MIN); } } } @@ -389,7 +395,7 @@ void isotp_on_can_message(IsoTpLink *link, uint8_t *data, uint8_t len) { if (ISOTP_RET_OK == ret) { /* refresh bs timer */ - link->send_timer_bs = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; + link->send_timer_bs = link->isotp_user_get_ms() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT; /* overflow */ if (PCI_FLOW_STATUS_OVERFLOW == message.as.flow_control.FS) { @@ -414,8 +420,7 @@ void isotp_on_can_message(IsoTpLink *link, uint8_t *data, uint8_t len) { } else { link->send_bs_remain = message.as.flow_control.BS; } - uint32_t message_st_min_us = isotp_st_min_to_us(message.as.flow_control.STmin); - link->send_st_min_us = message_st_min_us > ISO_TP_DEFAULT_ST_MIN_US ? message_st_min_us : ISO_TP_DEFAULT_ST_MIN_US; // prefer as much st_min as possible for stability? + link->send_st_min = isotp_st_min_to_ms(message.as.flow_control.STmin); link->send_wtf_count = 0; } } @@ -447,7 +452,18 @@ int isotp_receive(IsoTpLink *link, uint8_t *payload, const uint16_t payload_size return ISOTP_RET_OK; } -void isotp_init_link(IsoTpLink *link, uint32_t sendid, uint8_t *sendbuf, uint16_t sendbufsize, uint8_t *recvbuf, uint16_t recvbufsize) { +void isotp_init_link( + IsoTpLink *link, + uint32_t sendid, + uint8_t *sendbuf, + uint16_t sendbufsize, + uint8_t *recvbuf, + uint16_t recvbufsize, + uint32_t (*isotp_user_get_ms)(void), + int (*isotp_user_send_can)(const uint32_t arbitration_id, const uint8_t* data, const uint8_t size, void *user_data), + void (*isotp_user_debug)(const char* message, ...), + void *user_data + ) { memset(link, 0, sizeof(*link)); link->receive_status = ISOTP_RECEIVE_STATUS_IDLE; link->send_status = ISOTP_SEND_STATUS_IDLE; @@ -456,7 +472,11 @@ void isotp_init_link(IsoTpLink *link, uint32_t sendid, uint8_t *sendbuf, uint16_ link->send_buf_size = sendbufsize; link->receive_buffer = recvbuf; link->receive_buf_size = recvbufsize; - + link->isotp_user_get_ms = isotp_user_get_ms; + link->isotp_user_send_can = isotp_user_send_can; + link->isotp_user_debug = isotp_user_debug; + link->user_data = user_data; + return; } @@ -470,15 +490,15 @@ void isotp_poll(IsoTpLink *link) { if (/* send data if bs_remain is invalid or bs_remain large than zero */ (ISOTP_INVALID_BS == link->send_bs_remain || link->send_bs_remain > 0) && /* and if st_min is zero or go beyond interval time */ - (0 == link->send_st_min_us || IsoTpTimeAfter(isotp_user_get_us(), link->send_timer_st))) { + (0 == link->send_st_min || (0 != link->send_st_min && IsoTpTimeAfter(link->isotp_user_get_ms(), link->send_timer_st)))) { ret = isotp_send_consecutive_frame(link); if (ISOTP_RET_OK == ret) { if (ISOTP_INVALID_BS != link->send_bs_remain) { link->send_bs_remain -= 1; } - link->send_timer_bs = isotp_user_get_us() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US; - link->send_timer_st = isotp_user_get_us() + link->send_st_min_us; + link->send_timer_bs = link->isotp_user_get_ms() + ISO_TP_DEFAULT_RESPONSE_TIMEOUT; + link->send_timer_st = link->isotp_user_get_ms() + link->send_st_min; /* check if send finish */ if (link->send_offset >= link->send_size) { @@ -490,7 +510,7 @@ void isotp_poll(IsoTpLink *link) { } /* check timeout */ - if (IsoTpTimeAfter(isotp_user_get_us(), link->send_timer_bs)) { + if (IsoTpTimeAfter(link->isotp_user_get_ms(), link->send_timer_bs)) { link->send_protocol_result = ISOTP_PROTOCOL_RESULT_TIMEOUT_BS; link->send_status = ISOTP_SEND_STATUS_ERROR; } @@ -500,7 +520,7 @@ void isotp_poll(IsoTpLink *link) { if (ISOTP_RECEIVE_STATUS_INPROGRESS == link->receive_status) { /* check timeout */ - if (IsoTpTimeAfter(isotp_user_get_us(), link->receive_timer_cr)) { + if (IsoTpTimeAfter(link->isotp_user_get_ms(), link->receive_timer_cr)) { link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_TIMEOUT_CR; link->receive_status = ISOTP_RECEIVE_STATUS_IDLE; } @@ -508,3 +528,4 @@ void isotp_poll(IsoTpLink *link) { return; } + diff --git a/isotp-c/isotp.h b/src/tp/isotp-c/isotp.h similarity index 78% rename from isotp-c/isotp.h rename to src/tp/isotp-c/isotp.h index 682bcea..65171f3 100644 --- a/isotp-c/isotp.h +++ b/src/tp/isotp-c/isotp.h @@ -12,7 +12,6 @@ extern "C" { #include "isotp_defines.h" #include "isotp_config.h" -#include "isotp_user.h" /** * @brief Struct containing the data for linking an application to a CAN instance. @@ -30,7 +29,7 @@ typedef struct IsoTpLink { /* multi-frame flags */ uint8_t send_sn; uint16_t send_bs_remain; /* Remaining block size */ - uint32_t send_st_min_us; /* Separation Time between consecutive frames */ + uint8_t send_st_min; /* Separation Time between consecutive frames, unit millis */ uint8_t send_wtf_count; /* Maximum number of FC.Wait frame transmissions */ uint32_t send_timer_st; /* Last time send consecutive frame */ uint32_t send_timer_bs; /* Time until reception of the next FlowControl N_PDU @@ -38,6 +37,7 @@ typedef struct IsoTpLink { end at receive FC */ int send_protocol_result; uint8_t send_status; + /* receiver paramters */ uint32_t receive_arbitration_id; /* message buffer */ @@ -53,6 +53,13 @@ typedef struct IsoTpLink { end at receive FC */ int receive_protocol_result; uint8_t receive_status; + + /* user implemented callback functions */ + uint32_t (*isotp_user_get_ms)(void); /* get millisecond */ + int (*isotp_user_send_can)(const uint32_t arbitration_id, + const uint8_t* data, const uint8_t size, void *user_data); /* send can message. should return ISOTP_RET_OK when success. */ + void (*isotp_user_debug)(const char* message, ...); /* print debug message */ + void* user_data; /* user data */ } IsoTpLink; /** @@ -64,10 +71,23 @@ typedef struct IsoTpLink { * @param sendbufsize The size of the buffer area. * @param recvbuf A pointer to an area in memory which can be used as a buffer for data to be received. * @param recvbufsize The size of the buffer area. + * @param isotp_user_get_ms A pointer to a function which returns the current time as milliseconds. + * @param isotp_user_send_can A pointer to a function which sends a can message. should return ISOTP_RET_OK when success. + * @param isotp_user_debug A pointer to a function which prints a debug message. + * @param isotp_user_debug A pointer to user data passed to the user implemented callback functions. */ -void isotp_init_link(IsoTpLink *link, uint32_t sendid, - uint8_t *sendbuf, uint16_t sendbufsize, - uint8_t *recvbuf, uint16_t recvbufsize); +void isotp_init_link( + IsoTpLink *link, + uint32_t sendid, + uint8_t *sendbuf, + uint16_t sendbufsize, + uint8_t *recvbuf, + uint16_t recvbufsize, + uint32_t (*isotp_user_get_ms)(void), + int (*isotp_user_send_can)(const uint32_t arbitration_id, const uint8_t* data, const uint8_t size, void *user_data), + void (*isotp_user_debug)(const char* message, ...), + void *user_data + ); /** * @brief Polling function; call this function periodically to handle timeouts, send consecutive frames, etc. diff --git a/isotp-c/isotp_config.h b/src/tp/isotp-c/isotp_config.h similarity index 89% rename from isotp-c/isotp_config.h rename to src/tp/isotp-c/isotp_config.h index 637682b..7f4223d 100644 --- a/isotp-c/isotp_config.h +++ b/src/tp/isotp-c/isotp_config.h @@ -9,7 +9,7 @@ /* The STmin parameter value specifies the minimum time gap allowed between * the transmission of consecutive frame network protocol data units */ -#define ISO_TP_DEFAULT_ST_MIN_US 0 +#define ISO_TP_DEFAULT_ST_MIN 0 /* This parameter indicate how many FC N_PDU WTs can be transmitted by the * receiver in a row. @@ -19,7 +19,7 @@ /* Private: The default timeout to use when waiting for a response during a * multi-frame send or receive. */ -#define ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US 100000 +#define ISO_TP_DEFAULT_RESPONSE_TIMEOUT 100 /* Private: Determines if by default, padding is added to ISO-TP message frames. */ diff --git a/isotp-c/isotp_defines.h b/src/tp/isotp-c/isotp_defines.h similarity index 100% rename from isotp-c/isotp_defines.h rename to src/tp/isotp-c/isotp_defines.h diff --git a/isotp-c/vars.mk b/src/tp/isotp-c/vars.mk similarity index 100% rename from isotp-c/vars.mk rename to src/tp/isotp-c/vars.mk diff --git a/src/tp/isotp_c.c b/src/tp/isotp_c.c new file mode 100644 index 0000000..b69bd6e --- /dev/null +++ b/src/tp/isotp_c.c @@ -0,0 +1,167 @@ +#if defined(UDS_TP_ISOTP_C) + +#include "util.h" +#include "tp/isotp_c.h" +#include "tp/isotp-c/isotp.h" + +static UDSTpStatus_t tp_poll(UDSTpHandle_t *hdl) { + assert(hdl); + UDSTpStatus_t status = 0; + UDSISOTpC_t *impl = (UDSISOTpC_t *)hdl; + isotp_poll(&impl->phys_link); + if (impl->phys_link.send_status == ISOTP_SEND_STATUS_INPROGRESS) { + status |= UDS_TP_SEND_IN_PROGRESS; + } + return status; +} + +int peek_link(IsoTpLink *link, uint8_t *buf, size_t bufsize, bool functional) { + assert(link); + assert(buf); + int ret = -1; + switch (link->receive_status) { + case ISOTP_RECEIVE_STATUS_IDLE: + ret = 0; + goto done; + case ISOTP_RECEIVE_STATUS_INPROGRESS: + ret = 0; + goto done; + case ISOTP_RECEIVE_STATUS_FULL: + ret = link->receive_size; + printf("The link is full. Copying %d bytes\n", ret); + memmove(buf, link->receive_buffer, link->receive_size); + break; + default: + UDS_DBG_PRINT("receive_status %d not implemented\n", link->receive_status); + ret = -1; + goto done; + } +done: + return ret; +} + +static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { + assert(hdl); + assert(p_buf); + UDSISOTpC_t *tp = (UDSISOTpC_t *)hdl; + if (ISOTP_RECEIVE_STATUS_FULL == tp->phys_link.receive_status) { // recv not yet acked + *p_buf = tp->recv_buf; + return tp->phys_link.receive_size; + } + int ret = -1; + ret = peek_link(&tp->phys_link, tp->recv_buf, sizeof(tp->recv_buf), false); + UDS_A_TA_Type_t ta_type = UDS_A_TA_TYPE_PHYSICAL; + uint32_t ta = tp->phys_ta; + uint32_t sa = tp->phys_sa; + + if (ret > 0) { + printf("just got %d bytes\n", ret); + ta = tp->phys_sa; + sa = tp->phys_ta; + ta_type = UDS_A_TA_TYPE_PHYSICAL; + *p_buf = tp->recv_buf; + goto done; + } else if (ret < 0) { + goto done; + } else { + ret = peek_link(&tp->func_link, tp->recv_buf, sizeof(tp->recv_buf), true); + if (ret > 0) { + printf("just got %d bytes on func link \n", ret); + ta = tp->func_sa; + sa = tp->func_ta; + ta_type = UDS_A_TA_TYPE_FUNCTIONAL; + *p_buf = tp->recv_buf; + goto done; + } else if (ret < 0) { + goto done; + } + } +done: + if (ret > 0) { + if (info) { + info->A_TA = ta; + info->A_SA = sa; + info->A_TA_Type = ta_type; + } + } + return ret; +} + +static ssize_t tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, UDSSDU_t *info) { + assert(hdl); + ssize_t ret = -1; + UDSISOTpC_t *tp = (UDSISOTpC_t *)hdl; + IsoTpLink *link = NULL; + const UDSTpAddr_t ta_type = info ? info->A_TA_Type : UDS_A_TA_TYPE_PHYSICAL; + const uint32_t ta = ta_type == UDS_A_TA_TYPE_PHYSICAL ? tp->phys_ta : tp->func_ta; + switch (ta_type) { + case UDS_A_TA_TYPE_PHYSICAL: + link = &tp->phys_link; + break; + case UDS_A_TA_TYPE_FUNCTIONAL: + link = &tp->func_link; + if (len > 7) { + UDS_DBG_PRINT("Cannot send more than 7 bytes via functional addressing\n"); + ret = -3; + goto done; + } + break; + default: + ret = -4; + goto done; + } + + int send_status = isotp_send(link, buf, len); + switch (send_status) { + case ISOTP_RET_OK: + ret = len; + goto done; + case ISOTP_RET_INPROGRESS: + case ISOTP_RET_OVERFLOW: + default: + ret = send_status; + goto done; + } +done: + return ret; +} + +static void tp_ack_recv(UDSTpHandle_t *hdl) { + assert(hdl); + printf("ack recv\n"); + UDSISOTpC_t *tp = (UDSISOTpC_t *)hdl; + uint16_t out_size = 0; + isotp_receive(&tp->phys_link, tp->recv_buf, sizeof(tp->recv_buf), &out_size); +} + +static ssize_t tp_get_send_buf(UDSTpHandle_t *hdl, uint8_t **p_buf) { + assert(hdl); + UDSISOTpC_t *tp = (UDSISOTpC_t *)hdl; + *p_buf = tp->send_buf; + return sizeof(tp->send_buf); +} + +UDSErr_t UDSISOTpCInit(UDSISOTpC_t *tp, const UDSISOTpCConfig_t *cfg) { + if (cfg == NULL || tp == NULL) { + return UDS_ERR; + } + tp->hdl.poll = tp_poll; + tp->hdl.send = tp_send; + tp->hdl.peek = tp_peek; + tp->hdl.ack_recv = tp_ack_recv; + tp->hdl.get_send_buf = tp_get_send_buf; + tp->phys_sa = cfg->source_addr; + tp->phys_ta = cfg->target_addr; + tp->func_sa = cfg->source_addr_func; + tp->func_ta = cfg->target_addr; + + isotp_init_link(&tp->phys_link, tp->phys_ta, tp->send_buf, sizeof(tp->send_buf), tp->recv_buf, + sizeof(tp->recv_buf), UDSMillis, cfg->isotp_user_send_can, + cfg->isotp_user_debug, cfg->user_data); + isotp_init_link(&tp->func_link, tp->func_ta, tp->recv_buf, sizeof(tp->send_buf), tp->recv_buf, + sizeof(tp->recv_buf), UDSMillis, cfg->isotp_user_send_can, + cfg->isotp_user_debug, cfg->user_data); + return UDS_OK; +} + +#endif diff --git a/src/tp/isotp_c.h b/src/tp/isotp_c.h new file mode 100644 index 0000000..b525fce --- /dev/null +++ b/src/tp/isotp_c.h @@ -0,0 +1,37 @@ +#pragma once +#if defined(UDS_TP_ISOTP_C) + +#include "sys.h" +#include "config.h" +#include "uds.h" +#include "tp.h" +#include "tp/isotp-c/isotp.h" + +typedef struct { + UDSTpHandle_t hdl; + IsoTpLink phys_link; + IsoTpLink func_link; + uint8_t send_buf[UDS_ISOTP_MTU]; + uint8_t recv_buf[UDS_ISOTP_MTU]; + uint32_t phys_sa, phys_ta; + uint32_t func_sa, func_ta; +} UDSISOTpC_t; + +typedef struct { + uint32_t source_addr; + uint32_t target_addr; + uint32_t source_addr_func; + uint32_t target_addr_func; + int (*isotp_user_send_can)( + const uint32_t arbitration_id, const uint8_t *data, const uint8_t size, + void *user_data); /* send can message. should return ISOTP_RET_OK when success. */ + uint32_t (*isotp_user_get_ms)(void); /* get millisecond */ + void (*isotp_user_debug)(const char *message, ...); /* print debug message */ + void *user_data; /* user data */ +} UDSISOTpCConfig_t; + +UDSErr_t UDSISOTpCInit(UDSISOTpC_t *tp, const UDSISOTpCConfig_t *cfg); + +void UDSISOTpCDeinit(UDSISOTpC_t *tp); + +#endif diff --git a/src/tp/isotp_c_socketcan.c b/src/tp/isotp_c_socketcan.c new file mode 100644 index 0000000..4a569b3 --- /dev/null +++ b/src/tp/isotp_c_socketcan.c @@ -0,0 +1,285 @@ +#if defined(UDS_TP_ISOTP_C_SOCKETCAN) + +#include "tp/isotp_c_socketcan.h" +#include "iso14229.h" +#include "tp/isotp-c/isotp_defines.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int SetupSocketCAN(const char *ifname) { + UDS_DBG_PRINT("setting up CAN\n"); + struct sockaddr_can addr; + struct ifreq ifr; + int sockfd = -1; + + if ((sockfd = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)) < 0) { + perror("socket"); + goto done; + } + + strcpy(ifr.ifr_name, ifname); + ioctl(sockfd, SIOCGIFINDEX, &ifr); + memset(&addr, 0, sizeof(addr)); + addr.can_family = AF_CAN; + addr.can_ifindex = ifr.ifr_ifindex; + if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + perror("bind"); + } + +done: + return sockfd; +} + +uint32_t isotp_user_get_ms(void) { return UDSMillis(); } + +void isotp_user_debug(const char *message, ...) { + va_list args; + va_start(args, message); + vprintf(message, args); + va_end(args); +} + +int isotp_user_send_can(const uint32_t arbitration_id, const uint8_t *data, const uint8_t size, + void *user_data) { + assert(user_data); + int sockfd = *(int *)user_data; + struct can_frame frame = {0}; + frame.can_id = arbitration_id; + frame.can_dlc = size; + memmove(frame.data, data, size); + if (write(sockfd, &frame, sizeof(struct can_frame)) != sizeof(struct can_frame)) { + perror("Write err"); + return ISOTP_RET_ERROR; + } + return ISOTP_RET_OK; +} + +static void SocketCANRecv(UDSTpISOTpC_t *tp) { + assert(tp); + struct can_frame frame = {0}; + int nbytes = 0; + + for (;;) { + nbytes = read(tp->fd, &frame, sizeof(struct can_frame)); + if (nbytes < 0) { + if (EAGAIN == errno || EWOULDBLOCK == errno) { + break; + } else { + perror("read"); + } + } else if (nbytes == 0) { + break; + } else { + if (frame.can_id == tp->phys_sa) { + UDS_DBG_PRINT("phys recvd can\n"); + UDS_DBG_PRINTHEX(frame.data, frame.can_dlc); + isotp_on_can_message(&tp->phys_link, frame.data, frame.can_dlc); + } else if (frame.can_id == tp->func_sa) { + if (ISOTP_RECEIVE_STATUS_IDLE != tp->phys_link.receive_status) { + UDS_DBG_PRINT( + "func frame received but cannot process because link is not idle"); + return; + } + // TODO: reject if it's longer than a single frame + isotp_on_can_message(&tp->func_link, frame.data, frame.can_dlc); + } + } + } +} + +static UDSTpStatus_t isotp_c_socketcan_tp_poll(UDSTpHandle_t *hdl) { + assert(hdl); + UDSTpStatus_t status = 0; + UDSTpISOTpC_t *impl = (UDSTpISOTpC_t *)hdl; + SocketCANRecv(impl); + isotp_poll(&impl->phys_link); + if (impl->phys_link.send_status == ISOTP_SEND_STATUS_INPROGRESS) { + status |= UDS_TP_SEND_IN_PROGRESS; + } + return status; +} + +static int isotp_c_socketcan_tp_peek_link(IsoTpLink *link, uint8_t *buf, size_t bufsize, + bool functional) { + assert(link); + assert(buf); + int ret = -1; + switch (link->receive_status) { + case ISOTP_RECEIVE_STATUS_IDLE: + ret = 0; + goto done; + case ISOTP_RECEIVE_STATUS_INPROGRESS: + ret = 0; + goto done; + case ISOTP_RECEIVE_STATUS_FULL: + ret = link->receive_size; + printf("The link is full. Copying %d bytes\n", ret); + memmove(buf, link->receive_buffer, link->receive_size); + break; + default: + UDS_DBG_PRINT("receive_status %d not implemented\n", link->receive_status); + ret = -1; + goto done; + } +done: + return ret; +} + +static ssize_t isotp_c_socketcan_tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { + assert(hdl); + assert(p_buf); + UDSTpISOTpC_t *tp = (UDSTpISOTpC_t *)hdl; + if (ISOTP_RECEIVE_STATUS_FULL == tp->phys_link.receive_status) { // recv not yet acked + *p_buf = tp->recv_buf; + return tp->phys_link.receive_size; + } + int ret = -1; + ret = isotp_c_socketcan_tp_peek_link(&tp->phys_link, tp->recv_buf, sizeof(tp->recv_buf), false); + UDS_A_TA_Type_t ta_type = UDS_A_TA_TYPE_PHYSICAL; + uint32_t ta = tp->phys_ta; + uint32_t sa = tp->phys_sa; + + if (ret > 0) { + printf("just got %d bytes\n", ret); + ta = tp->phys_sa; + sa = tp->phys_ta; + ta_type = UDS_A_TA_TYPE_PHYSICAL; + *p_buf = tp->recv_buf; + goto done; + } else if (ret < 0) { + goto done; + } else { + ret = isotp_c_socketcan_tp_peek_link(&tp->func_link, tp->recv_buf, sizeof(tp->recv_buf), + true); + if (ret > 0) { + printf("just got %d bytes on func link \n", ret); + ta = tp->func_sa; + sa = tp->func_ta; + ta_type = UDS_A_TA_TYPE_FUNCTIONAL; + *p_buf = tp->recv_buf; + goto done; + } else if (ret < 0) { + goto done; + } + } +done: + if (ret > 0) { + if (info) { + info->A_TA = ta; + info->A_SA = sa; + info->A_TA_Type = ta_type; + } + fprintf(stdout, "%06d, %s recv, 0x%03x (%s), ", UDSMillis(), tp->tag, ta, + ta_type == UDS_A_TA_TYPE_PHYSICAL ? "phys" : "func"); + for (int i = 0; i < ret; i++) { + fprintf(stdout, "%02x ", (*p_buf)[i]); + } + fprintf(stdout, "\n"); + fflush(stdout); // flush every time in case of crash + } + return ret; +} + +static ssize_t isotp_c_socketcan_tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, + UDSSDU_t *info) { + assert(hdl); + ssize_t ret = -1; + UDSTpISOTpC_t *tp = (UDSTpISOTpC_t *)hdl; + IsoTpLink *link = NULL; + const UDSTpAddr_t ta_type = info ? info->A_TA_Type : UDS_A_TA_TYPE_PHYSICAL; + const uint32_t ta = ta_type == UDS_A_TA_TYPE_PHYSICAL ? tp->phys_ta : tp->func_ta; + switch (ta_type) { + case UDS_A_TA_TYPE_PHYSICAL: + link = &tp->phys_link; + break; + case UDS_A_TA_TYPE_FUNCTIONAL: + link = &tp->func_link; + if (len > 7) { + UDS_DBG_PRINT("Cannot send more than 7 bytes via functional addressing\n"); + ret = -3; + goto done; + } + break; + default: + ret = -4; + goto done; + } + + int send_status = isotp_send(link, buf, len); + switch (send_status) { + case ISOTP_RET_OK: + ret = len; + goto done; + case ISOTP_RET_INPROGRESS: + case ISOTP_RET_OVERFLOW: + default: + ret = send_status; + goto done; + } +done: + fprintf(stdout, "%06d, %s sends, 0x%03x (%s), ", UDSMillis(), tp->tag, ta, + ta_type == UDS_A_TA_TYPE_PHYSICAL ? "phys" : "func"); + for (unsigned i = 0; i < len; i++) { + fprintf(stdout, "%02x ", buf[i]); + } + fprintf(stdout, "\n"); + fflush(stdout); // flush every time in case of crash + + return ret; +} + +static void isotp_c_socketcan_tp_ack_recv(UDSTpHandle_t *hdl) { + assert(hdl); + printf("ack recv\n"); + UDSTpISOTpC_t *tp = (UDSTpISOTpC_t *)hdl; + uint16_t out_size = 0; + isotp_receive(&tp->phys_link, tp->recv_buf, sizeof(tp->recv_buf), &out_size); +} + +static ssize_t isotp_c_socketcan_tp_get_send_buf(UDSTpHandle_t *hdl, uint8_t **p_buf) { + assert(hdl); + UDSTpISOTpC_t *tp = (UDSTpISOTpC_t *)hdl; + *p_buf = tp->send_buf; + return sizeof(tp->send_buf); +} + +UDSErr_t UDSTpISOTpCInit(UDSTpISOTpC_t *tp, const char *ifname, uint32_t source_addr, + uint32_t target_addr, uint32_t source_addr_func, + uint32_t target_addr_func) { + assert(tp); + assert(ifname); + tp->hdl.poll = isotp_c_socketcan_tp_poll; + tp->hdl.send = isotp_c_socketcan_tp_send; + tp->hdl.peek = isotp_c_socketcan_tp_peek; + tp->hdl.ack_recv = isotp_c_socketcan_tp_ack_recv; + tp->hdl.get_send_buf = isotp_c_socketcan_tp_get_send_buf; + tp->phys_sa = source_addr; + tp->phys_ta = target_addr; + tp->func_sa = source_addr_func; + tp->func_ta = target_addr; + tp->fd = SetupSocketCAN(ifname); + + isotp_init_link(&tp->phys_link, target_addr, tp->send_buf, sizeof(tp->send_buf), tp->recv_buf, + sizeof(tp->recv_buf), isotp_user_get_ms, isotp_user_send_can, isotp_user_debug, + &tp->fd); + isotp_init_link(&tp->func_link, target_addr_func, tp->recv_buf, sizeof(tp->send_buf), + tp->recv_buf, sizeof(tp->recv_buf), isotp_user_get_ms, isotp_user_send_can, + isotp_user_debug, &tp->fd); + return UDS_OK; +} + +void UDSTpISOTpCDeinit(UDSTpISOTpC_t *tp) { + assert(tp); + close(tp->fd); + tp->fd = -1; +} + +#endif \ No newline at end of file diff --git a/src/tp/isotp_c_socketcan.h b/src/tp/isotp_c_socketcan.h new file mode 100644 index 0000000..29da3d2 --- /dev/null +++ b/src/tp/isotp_c_socketcan.h @@ -0,0 +1,25 @@ +#pragma once + +#if defined(UDS_TP_ISOTP_C_SOCKETCAN) + +#include "tp.h" +#include "tp/isotp-c/isotp.h" + +typedef struct { + UDSTpHandle_t hdl; + IsoTpLink phys_link; + IsoTpLink func_link; + uint8_t send_buf[UDS_ISOTP_MTU]; + uint8_t recv_buf[UDS_ISOTP_MTU]; + int fd; + uint32_t phys_sa, phys_ta; + uint32_t func_sa, func_ta; + char tag[16]; +} UDSTpISOTpC_t; + +UDSErr_t UDSTpISOTpCInit(UDSTpISOTpC_t *tp, const char *ifname, uint32_t source_addr, + uint32_t target_addr, uint32_t source_addr_func, + uint32_t target_addr_func); +void UDSTpISOTpCDeinit(UDSTpISOTpC_t *tp); + +#endif diff --git a/src/tp/isotp_sock.c b/src/tp/isotp_sock.c new file mode 100644 index 0000000..fdffa29 --- /dev/null +++ b/src/tp/isotp_sock.c @@ -0,0 +1,244 @@ +#if defined(UDS_TP_ISOTP_SOCK) + +#include "tp/isotp_sock.h" +#include "iso14229.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static UDSTpStatus_t isotp_sock_tp_poll(UDSTpHandle_t *hdl) { return 0; } + +static ssize_t tp_recv_once(int fd, uint8_t *buf, size_t size) { + ssize_t ret = read(fd, buf, size); + if (ret < 0) { + if (EAGAIN == errno || EWOULDBLOCK == errno) { + ret = 0; + } else { + UDS_DBG_PRINT("read failed: %ld with errno: %d\n", ret, errno); + if (EILSEQ == errno) { + UDS_DBG_PRINT("Perhaps I received multiple responses?\n"); + } + } + } + return ret; +} + +static ssize_t isotp_sock_tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { + assert(hdl); + assert(p_buf); + ssize_t ret = 0; + UDSTpIsoTpSock_t *impl = (UDSTpIsoTpSock_t *)hdl; + *p_buf = impl->recv_buf; + if (impl->recv_len) { // recv not yet acked + ret = impl->recv_len; + goto done; + } + + UDSSDU_t *msg = &impl->recv_info; + + // recv acked, OK to receive + ret = tp_recv_once(impl->phys_fd, impl->recv_buf, sizeof(impl->recv_buf)); + if (ret > 0) { + msg->A_TA = impl->phys_sa; + msg->A_SA = impl->phys_ta; + msg->A_TA_Type = UDS_A_TA_TYPE_PHYSICAL; + } else { + ret = tp_recv_once(impl->func_fd, impl->recv_buf, sizeof(impl->recv_buf)); + if (ret > 0) { + msg->A_TA = impl->func_sa; + msg->A_SA = impl->func_ta; + msg->A_TA_Type = UDS_A_TA_TYPE_FUNCTIONAL; + } + } + + if (ret > 0) { + fprintf(stdout, "%06d, %s recv, 0x%03x (%s), ", UDSMillis(), impl->tag, msg->A_TA, + msg->A_TA_Type == UDS_A_TA_TYPE_PHYSICAL ? "phys" : "func"); + for (unsigned i = 0; i < ret; i++) { + fprintf(stdout, "%02x ", impl->recv_buf[i]); + } + fprintf(stdout, "\n"); + fflush(stdout); // flush every time in case of crash + // UDS_DBG_PRINT("<<< "); + // UDS_DBG_PRINTHEX(, ret); + } + +done: + if (ret > 0) { + impl->recv_len = ret; + if (info) { + *info = *msg; + } + } + return ret; +} + +static void isotp_sock_tp_ack_recv(UDSTpHandle_t *hdl) { + assert(hdl); + UDSTpIsoTpSock_t *impl = (UDSTpIsoTpSock_t *)hdl; + impl->recv_len = 0; +} + +static ssize_t isotp_sock_tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, UDSSDU_t *info) { + assert(hdl); + ssize_t ret = -1; + UDSTpIsoTpSock_t *impl = (UDSTpIsoTpSock_t *)hdl; + int fd; + const UDSTpAddr_t ta_type = info ? info->A_TA_Type : UDS_A_TA_TYPE_PHYSICAL; + + if (UDS_A_TA_TYPE_PHYSICAL == ta_type) { + fd = impl->phys_fd; + } else if (UDS_A_TA_TYPE_FUNCTIONAL == ta_type) { + if (len > 7) { + UDS_DBG_PRINT("UDSTpIsoTpSock: functional request too large\n"); + return -1; + } + fd = impl->func_fd; + } else { + ret = -4; + goto done; + } + ret = write(fd, buf, len); + if (ret < 0) { + perror("write"); + } +done: + // UDS_DBG_PRINT(">>> "); + // UDS_DBG_PRINTHEX(buf, ret); + + fprintf(stdout, "%06d, %s sends, (%s), ", UDSMillis(), impl->tag, + ta_type == UDS_A_TA_TYPE_PHYSICAL ? "phys" : "func"); + for (unsigned i = 0; i < len; i++) { + fprintf(stdout, "%02x ", buf[i]); + } + fprintf(stdout, "\n"); + fflush(stdout); // flush every time in case of crash + + return ret; +} + +static ssize_t isotp_sock_tp_get_send_buf(UDSTpHandle_t *hdl, uint8_t **p_buf) { + assert(hdl); + UDSTpIsoTpSock_t *impl = (UDSTpIsoTpSock_t *)hdl; + *p_buf = impl->send_buf; + return sizeof(impl->send_buf); +} + +static int LinuxSockBind(const char *if_name, uint32_t rxid, uint32_t txid, bool functional) { + int fd = 0; + if ((fd = socket(AF_CAN, SOCK_DGRAM | SOCK_NONBLOCK, CAN_ISOTP)) < 0) { + perror("Socket"); + return -1; + } + + struct can_isotp_fc_options fcopts = { + .bs = 0x10, + .stmin = 3, + .wftmax = 0, + }; + if (setsockopt(fd, SOL_CAN_ISOTP, CAN_ISOTP_RECV_FC, &fcopts, sizeof(fcopts)) < 0) { + perror("setsockopt"); + return -1; + } + + if (functional) { + printf("configuring fd: %d as functional\n", fd); + // configure the socket as listen-only to avoid sending FC frames + struct can_isotp_options opts; + memset(&opts, 0, sizeof(opts)); + opts.flags |= CAN_ISOTP_LISTEN_MODE; + if (setsockopt(fd, SOL_CAN_ISOTP, CAN_ISOTP_OPTS, &opts, sizeof(opts)) < 0) { + perror("setsockopt (isotp_options):"); + return -1; + } + } + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)); + ioctl(fd, SIOCGIFINDEX, &ifr); + + struct sockaddr_can addr; + memset(&addr, 0, sizeof(addr)); + addr.can_family = AF_CAN; + addr.can_addr.tp.rx_id = rxid; + addr.can_addr.tp.tx_id = txid; + addr.can_ifindex = ifr.ifr_ifindex; + + if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + printf("Bind: %s %s\n", strerror(errno), if_name); + return -1; + } + return fd; +} + +UDSErr_t UDSTpIsoTpSockInitServer(UDSTpIsoTpSock_t *tp, const char *ifname, uint32_t source_addr, + uint32_t target_addr, uint32_t source_addr_func) { + assert(tp); + memset(tp, 0, sizeof(*tp)); + tp->hdl.peek = isotp_sock_tp_peek; + tp->hdl.send = isotp_sock_tp_send; + tp->hdl.poll = isotp_sock_tp_poll; + tp->hdl.ack_recv = isotp_sock_tp_ack_recv; + tp->hdl.get_send_buf = isotp_sock_tp_get_send_buf; + tp->phys_sa = source_addr; + tp->phys_ta = target_addr; + tp->func_sa = source_addr_func; + + tp->phys_fd = LinuxSockBind(ifname, source_addr, target_addr, false); + tp->func_fd = LinuxSockBind(ifname, source_addr_func, 0, true); + if (tp->phys_fd < 0 || tp->func_fd < 0) { + printf("foo\n"); + fflush(stdout); + return UDS_ERR; + } + UDS_DBG_PRINT("%s initialized phys link rx 0x%03x tx 0x%03x func link rx 0x%03x tx 0x%03x\n", + strlen(tp->tag) ? tp->tag : "server", source_addr, target_addr, source_addr_func, + target_addr); + return UDS_OK; +} + +UDSErr_t UDSTpIsoTpSockInitClient(UDSTpIsoTpSock_t *tp, const char *ifname, uint32_t source_addr, + uint32_t target_addr, uint32_t target_addr_func) { + assert(tp); + memset(tp, 0, sizeof(*tp)); + tp->hdl.peek = isotp_sock_tp_peek; + tp->hdl.send = isotp_sock_tp_send; + tp->hdl.poll = isotp_sock_tp_poll; + tp->hdl.ack_recv = isotp_sock_tp_ack_recv; + tp->hdl.get_send_buf = isotp_sock_tp_get_send_buf; + tp->func_ta = target_addr_func; + tp->phys_ta = target_addr; + tp->phys_sa = source_addr; + + tp->phys_fd = LinuxSockBind(ifname, source_addr, target_addr, false); + tp->func_fd = LinuxSockBind(ifname, 0, target_addr_func, true); + if (tp->phys_fd < 0 || tp->func_fd < 0) { + return UDS_ERR; + } + printf("%s initialized phys link (fd %d) rx 0x%03x tx 0x%03x func link (fd %d) rx 0x%03x tx " + "0x%03x\n", + strlen(tp->tag) ? tp->tag : "client", tp->phys_fd, source_addr, target_addr, tp->func_fd, + source_addr, target_addr_func); + return UDS_OK; +} + +void UDSTpIsoTpSockDeinit(UDSTpIsoTpSock_t *tp) { + if (tp) { + if (close(tp->phys_fd) < 0) { + perror("failed to close socket"); + } + if (close(tp->func_fd) < 0) { + perror("failed to close socket"); + } + } +} + +#endif diff --git a/src/tp/isotp_sock.h b/src/tp/isotp_sock.h new file mode 100644 index 0000000..ac189f4 --- /dev/null +++ b/src/tp/isotp_sock.h @@ -0,0 +1,25 @@ +#if defined(UDS_TP_ISOTP_SOCK) + +#pragma once +#include "iso14229.h" + +typedef struct { + UDSTpHandle_t hdl; + uint8_t recv_buf[UDS_ISOTP_MTU]; + uint8_t send_buf[UDS_ISOTP_MTU]; + size_t recv_len; + UDSSDU_t recv_info; + int phys_fd; + int func_fd; + uint32_t phys_sa, phys_ta; + uint32_t func_sa, func_ta; + char tag[16]; +} UDSTpIsoTpSock_t; + +UDSErr_t UDSTpIsoTpSockInitServer(UDSTpIsoTpSock_t *tp, const char *ifname, uint32_t source_addr, + uint32_t target_addr, uint32_t source_addr_func); +UDSErr_t UDSTpIsoTpSockInitClient(UDSTpIsoTpSock_t *tp, const char *ifname, uint32_t source_addr, + uint32_t target_addr, uint32_t target_addr_func); +void UDSTpIsoTpSockDeinit(UDSTpIsoTpSock_t *tp); + +#endif diff --git a/src/tp/mock.c b/src/tp/mock.c new file mode 100644 index 0000000..01b7b49 --- /dev/null +++ b/src/tp/mock.c @@ -0,0 +1,211 @@ +#if defined(UDS_TP_MOCK) + +#include "tp/mock.h" +#include "iso14229.h" +#include +#include +#include +#include +#include + +#define MAX_NUM_TP 16 +#define NUM_MSGS 8 +static TPMock_t *TPs[MAX_NUM_TP]; +static unsigned TPCount = 0; +static FILE *LogFile = NULL; +static struct Msg { + uint8_t buf[UDS_ISOTP_MTU]; + size_t len; + UDSSDU_t info; + uint32_t scheduled_tx_time; +} msgs[NUM_MSGS]; +static unsigned MsgCount = 0; + +static void LogMsg(const char *prefix, const uint8_t *buf, size_t len, UDSSDU_t *info) { + if (!LogFile) { + return; + } + fprintf(LogFile, "%06d, %s sends, 0x%03x (%s), ", UDSMillis(), prefix, info->A_TA, + info->A_TA_Type == UDS_A_TA_TYPE_PHYSICAL ? "phys" : "func"); + for (unsigned i = 0; i < len; i++) { + fprintf(LogFile, "%02x ", buf[i]); + } + fprintf(LogFile, "\n"); + fflush(LogFile); // flush every time in case of crash +} + +static void NetworkPoll(void) { + for (unsigned i = 0; i < MsgCount; i++) { + struct Msg *msg = &msgs[i]; + if (UDSTimeAfter(UDSMillis(), msg->scheduled_tx_time)) { + for (unsigned j = 0; j < TPCount; j++) { + TPMock_t *tp = TPs[j]; + if (tp->sa_phys == msg->info.A_TA || tp->sa_func == msg->info.A_TA) { + if (tp->recv_len > 0) { + fprintf(stderr, "TPMock: %s recv buffer is already full. Message dropped\n", + tp->name); + continue; + } + memmove(tp->recv_buf, msg->buf, msg->len); + tp->recv_len = msg->len; + tp->recv_info = msg->info; + } + } + LogMsg("network", msg->buf, msg->len, &msg->info); + for (unsigned j = i + 1; j < MsgCount; j++) { + msgs[j - 1] = msgs[j]; + } + MsgCount--; + i--; + } + } +} + +static ssize_t mock_tp_peek(struct UDSTpHandle *hdl, uint8_t **p_buf, UDSSDU_t *info) { + assert(hdl); + assert(p_buf); + TPMock_t *tp = (TPMock_t *)hdl; + if (p_buf) { + *p_buf = tp->recv_buf; + } + if (info) { + *info = tp->recv_info; + } + return tp->recv_len; +} + +static ssize_t mock_tp_send(struct UDSTpHandle *hdl, uint8_t *buf, size_t len, UDSSDU_t *info) { + assert(hdl); + TPMock_t *tp = (TPMock_t *)hdl; + if (MsgCount > NUM_MSGS) { + fprintf(stderr, "TPMock: too many messages in the queue\n"); + return -1; + } + struct Msg *m = &msgs[MsgCount++]; + UDSTpAddr_t ta_type = info == NULL ? UDS_A_TA_TYPE_PHYSICAL : info->A_TA_Type; + m->len = len; + m->info.A_AE = info == NULL ? 0 : info->A_AE; + if (UDS_A_TA_TYPE_PHYSICAL == ta_type) { + m->info.A_TA = tp->ta_phys; + m->info.A_SA = tp->sa_phys; + } else if (UDS_A_TA_TYPE_FUNCTIONAL == ta_type) { + m->info.A_TA = tp->ta_func; + m->info.A_SA = tp->sa_func; + } else { + fprintf(stderr, "TPMock: unknown TA type: %d\n", ta_type); + return -1; + } + m->info.A_TA_Type = ta_type; + m->scheduled_tx_time = UDSMillis() + tp->send_tx_delay_ms; + memmove(m->buf, buf, len); + LogMsg(tp->name, buf, len, &m->info); + return len; +} + +static UDSTpStatus_t mock_tp_poll(struct UDSTpHandle *hdl) { + NetworkPoll(); + // todo: make this status reflect TX time + return UDS_TP_IDLE; +} + +static ssize_t mock_tp_get_send_buf(struct UDSTpHandle *hdl, uint8_t **p_buf) { + assert(hdl); + assert(p_buf); + TPMock_t *tp = (TPMock_t *)hdl; + *p_buf = tp->send_buf; + return sizeof(tp->send_buf); +} + +static void mock_tp_ack_recv(struct UDSTpHandle *hdl) { + assert(hdl); + TPMock_t *tp = (TPMock_t *)hdl; + tp->recv_len = 0; +} + +static_assert(offsetof(TPMock_t, hdl) == 0, "TPMock_t must not have any members before hdl"); + +static void TPMockAttach(TPMock_t *tp, TPMockArgs_t *args) { + assert(tp); + assert(args); + assert(TPCount < MAX_NUM_TP); + TPs[TPCount++] = tp; + tp->hdl.peek = mock_tp_peek; + tp->hdl.send = mock_tp_send; + tp->hdl.poll = mock_tp_poll; + tp->hdl.get_send_buf = mock_tp_get_send_buf; + tp->hdl.ack_recv = mock_tp_ack_recv; + tp->sa_func = args->sa_func; + tp->sa_phys = args->sa_phys; + tp->ta_func = args->ta_func; + tp->ta_phys = args->ta_phys; + tp->recv_len = 0; +} + +static void TPMockDetach(TPMock_t *tp) { + assert(tp); + for (unsigned i = 0; i < TPCount; i++) { + if (TPs[i] == tp) { + for (unsigned j = i + 1; j < TPCount; j++) { + TPs[j - 1] = TPs[j]; + } + TPCount--; + printf("TPMock: detached %s. TPCount: %d\n", tp->name, TPCount); + return; + } + } + assert(false); +} + +UDSTpHandle_t *TPMockNew(const char *name, TPMockArgs_t *args) { + if (TPCount >= MAX_NUM_TP) { + printf("TPCount: %d, too many TPs\n", TPCount); + return NULL; + } + TPMock_t *tp = malloc(sizeof(TPMock_t)); + if (name) { + strncpy(tp->name, name, sizeof(tp->name)); + } else { + snprintf(tp->name, sizeof(tp->name), "TPMock%d", TPCount); + } + TPMockAttach(tp, args); + return &tp->hdl; +} + +void TPMockConnect(UDSTpHandle_t *tp1, UDSTpHandle_t *tp2); + +void TPMockLogToFile(const char *filename) { + if (LogFile) { + fprintf(stderr, "Log file is already open\n"); + return; + } + if (!filename) { + fprintf(stderr, "Filename is NULL\n"); + return; + } + // create file + LogFile = fopen(filename, "w"); + if (!LogFile) { + fprintf(stderr, "Failed to open log file %s\n", filename); + return; + } +} + +void TPMockLogToStdout(void) { + if (LogFile) { + return; + } + LogFile = stdout; +} + +void TPMockReset(void) { + memset(TPs, 0, sizeof(TPs)); + TPCount = 0; +} + +void TPMockFree(UDSTpHandle_t *tp) { + TPMock_t *tpm = (TPMock_t *)tp; + TPMockDetach(tpm); + free(tp); +} + +#endif diff --git a/src/tp/mock.h b/src/tp/mock.h new file mode 100644 index 0000000..8b3d16e --- /dev/null +++ b/src/tp/mock.h @@ -0,0 +1,66 @@ +/** + * @file tp_mock.h + * @brief in-memory transport layer implementation for testing + * @date 2023-10-21 + * + */ +#if defined(UDS_TP_MOCK) + +#pragma once + +#include "iso14229.h" + +typedef struct TPMock { + UDSTpHandle_t hdl; + uint8_t recv_buf[UDS_TP_MTU]; + uint8_t send_buf[UDS_TP_MTU]; + size_t recv_len; + UDSSDU_t recv_info; + uint32_t sa_phys; // source address - physical messages are sent from this address + uint32_t ta_phys; // target address - physical messages are sent to this address + uint32_t sa_func; // source address - functional messages are sent from this address + uint32_t ta_func; // target address - functional messages are sent to this address + uint32_t send_tx_delay_ms; // simulated delay + uint32_t send_buf_size; // simulated size of the send buffer + char name[32]; // name for logging +} TPMock_t; + +typedef struct { + uint32_t sa_phys; // source address - physical messages are sent from this address + uint32_t ta_phys; // target address - physical messages are sent to this address + uint32_t sa_func; // source address - functional messages are sent from this address + uint32_t ta_func; // target address - functional messages are sent to this address +} TPMockArgs_t; + +#define TPMOCK_DEFAULT_CLIENT_ARGS \ + &(TPMockArgs_t) { \ + .sa_phys = 0x7E8, .ta_phys = 0x7E0, .sa_func = UDS_TP_NOOP_ADDR, .ta_func = 0x7DF \ + } +#define TPMOCK_DEFAULT_SERVER_ARGS \ + &(TPMockArgs_t) { \ + .sa_phys = 0x7E0, .ta_phys = 0x7E8, .sa_func = 0x7DF, .ta_func = UDS_TP_NOOP_ADDR \ + } + +/** + * @brief Create a mock transport. It is connected by default to a broadcast network of all other + * mock transports in the same process. + * @param name optional name of the transport (can be NULL) + * @return UDSTpHandle_t* + */ +UDSTpHandle_t *TPMockNew(const char *name, TPMockArgs_t *args); +void TPMockFree(UDSTpHandle_t *tp); + +/** + * @brief write all messages to a file + * @note uses UDSMillis() to get the current time + * @param filename log file name (will be overwritten) + */ +void TPMockLogToFile(const char *filename); +void TPMockLogToStdout(void); + +/** + * @brief clear all transports and close the log file + */ +void TPMockReset(void); + +#endif diff --git a/src/uds.h b/src/uds.h new file mode 100644 index 0000000..269b6b2 --- /dev/null +++ b/src/uds.h @@ -0,0 +1,222 @@ +#pragma once + +enum UDSServerEvent { + UDS_SRV_EVT_DiagSessCtrl, // UDSDiagSessCtrlArgs_t * + UDS_SRV_EVT_EcuReset, // UDSECUResetArgs_t * + UDS_SRV_EVT_ReadDataByIdent, // UDSRDBIArgs_t * + UDS_SRV_EVT_ReadMemByAddr, // UDSReadMemByAddrArgs_t * + UDS_SRV_EVT_CommCtrl, // UDSCommCtrlArgs_t * + UDS_SRV_EVT_SecAccessRequestSeed, // UDSSecAccessRequestSeedArgs_t * + UDS_SRV_EVT_SecAccessValidateKey, // UDSSecAccessValidateKeyArgs_t * + UDS_SRV_EVT_WriteDataByIdent, // UDSWDBIArgs_t * + UDS_SRV_EVT_RoutineCtrl, // UDSRoutineCtrlArgs_t* + UDS_SRV_EVT_RequestDownload, // UDSRequestDownloadArgs_t* + UDS_SRV_EVT_RequestUpload, // UDSRequestUploadArgs_t * + UDS_SRV_EVT_TransferData, // UDSTransferDataArgs_t * + UDS_SRV_EVT_RequestTransferExit, // UDSRequestTransferExitArgs_t * + UDS_SRV_EVT_SessionTimeout, // NULL + UDS_SRV_EVT_DoScheduledReset, // enum UDSEcuResetType * + UDS_SRV_EVT_Err, // UDSErr_t * + UDS_EVT_IDLE, + UDS_EVT_RESP_RECV, +}; + +typedef int UDSServerEvent_t; +typedef UDSServerEvent_t UDSEvent_t; + +typedef enum { + UDS_ERR = -1, // 通用错误 + UDS_OK = 0, // 成功 + UDS_ERR_TIMEOUT, // 请求超时 + UDS_ERR_NEG_RESP, // 否定响应 + UDS_ERR_DID_MISMATCH, // 响应DID对不上期待的DID + UDS_ERR_SID_MISMATCH, // 请求和响应SID对不上 + UDS_ERR_SUBFUNCTION_MISMATCH, // 请求和响应SubFunction对不上 + UDS_ERR_TPORT, // 传输层错误 + UDS_ERR_FILE_IO, // 文件IO错误 + UDS_ERR_RESP_TOO_SHORT, // 响应太短 + UDS_ERR_BUFSIZ, // 缓冲器不够大 + UDS_ERR_INVALID_ARG, // 参数不对、没发 + UDS_ERR_BUSY, // 正在忙、没发 +} UDSErr_t; + +typedef enum { + UDSSeqStateDone = 0, + UDSSeqStateRunning = 1, + UDSSeqStateGotoNext = 2, +} UDSSeqState_t; + +enum UDSDiagnosticSessionType { + kDefaultSession = 0x01, + kProgrammingSession = 0x02, + kExtendedDiagnostic = 0x03, + kSafetySystemDiagnostic = 0x04, +}; + +enum { + kPositiveResponse = 0, + kGeneralReject = 0x10, + kServiceNotSupported = 0x11, + kSubFunctionNotSupported = 0x12, + kIncorrectMessageLengthOrInvalidFormat = 0x13, + kResponseTooLong = 0x14, + kBusyRepeatRequest = 0x21, + kConditionsNotCorrect = 0x22, + kRequestSequenceError = 0x24, + kNoResponseFromSubnetComponent = 0x25, + kFailurePreventsExecutionOfRequestedAction = 0x26, + kRequestOutOfRange = 0x31, + kSecurityAccessDenied = 0x33, + kInvalidKey = 0x35, + kExceedNumberOfAttempts = 0x36, + kRequiredTimeDelayNotExpired = 0x37, + kUploadDownloadNotAccepted = 0x70, + kTransferDataSuspended = 0x71, + kGeneralProgrammingFailure = 0x72, + kWrongBlockSequenceCounter = 0x73, + kRequestCorrectlyReceived_ResponsePending = 0x78, + kSubFunctionNotSupportedInActiveSession = 0x7E, + kServiceNotSupportedInActiveSession = 0x7F, + kRpmTooHigh = 0x81, + kRpmTooLow = 0x82, + kEngineIsRunning = 0x83, + kEngineIsNotRunning = 0x84, + kEngineRunTimeTooLow = 0x85, + kTemperatureTooHigh = 0x86, + kTemperatureTooLow = 0x87, + kVehicleSpeedTooHigh = 0x88, + kVehicleSpeedTooLow = 0x89, + kThrottlePedalTooHigh = 0x8A, + kThrottlePedalTooLow = 0x8B, + kTransmissionRangeNotInNeutral = 0x8C, + kTransmissionRangeNotInGear = 0x8D, + kISOSAEReserved = 0x8E, + kBrakeSwitchNotClosed = 0x8F, + kShifterLeverNotInPark = 0x90, + kTorqueConverterClutchLocked = 0x91, + kVoltageTooHigh = 0x92, + kVoltageTooLow = 0x93, +}; + +/** + * @brief LEV_RT_ + * @addtogroup ecuReset_0x11 + */ +enum UDSECUResetType { + kHardReset = 1, + kKeyOffOnReset = 2, + kSoftReset = 3, + kEnableRapidPowerShutDown = 4, + kDisableRapidPowerShutDown = 5, +}; + +typedef uint8_t UDSECUReset_t; + +/** + * @addtogroup securityAccess_0x27 + */ +enum UDSSecurityAccessType { + kRequestSeed = 0x01, + kSendKey = 0x02, +}; + +/** + * @addtogroup communicationControl_0x28 + */ +enum UDSCommunicationControlType { + kEnableRxAndTx = 0, + kEnableRxAndDisableTx = 1, + kDisableRxAndEnableTx = 2, + kDisableRxAndTx = 3, +}; + +/** + * @addtogroup communicationControl_0x28 + */ +enum UDSCommunicationType { + kNormalCommunicationMessages = 0x1, + kNetworkManagementCommunicationMessages = 0x2, + kNetworkManagementCommunicationMessagesAndNormalCommunicationMessages = 0x3, +}; + +/** + * @addtogroup routineControl_0x31 + */ +enum RoutineControlType { + kStartRoutine = 1, + kStopRoutine = 2, + kRequestRoutineResults = 3, +}; + +/** + * @addtogroup controlDTCSetting_0x85 + */ +enum DTCSettingType { + kDTCSettingON = 0x01, + kDTCSettingOFF = 0x02, +}; + +// ISO-14229-1:2013 Table 2 +#define UDS_MAX_DIAGNOSTIC_SERVICES 0x7F + +#define UDS_RESPONSE_SID_OF(request_sid) (request_sid + 0x40) +#define UDS_REQUEST_SID_OF(response_sid) (response_sid - 0x40) + +#define UDS_NEG_RESP_LEN 3U +#define UDS_0X10_REQ_LEN 2U +#define UDS_0X10_RESP_LEN 6U +#define UDS_0X11_REQ_MIN_LEN 2U +#define UDS_0X11_RESP_BASE_LEN 2U +#define UDS_0X23_REQ_MIN_LEN 4U +#define UDS_0X23_RESP_BASE_LEN 1U +#define UDS_0X22_RESP_BASE_LEN 1U +#define UDS_0X27_REQ_BASE_LEN 2U +#define UDS_0X27_RESP_BASE_LEN 2U +#define UDS_0X28_REQ_BASE_LEN 3U +#define UDS_0X28_RESP_LEN 2U +#define UDS_0X2E_REQ_BASE_LEN 3U +#define UDS_0X2E_REQ_MIN_LEN 4U +#define UDS_0X2E_RESP_LEN 3U +#define UDS_0X31_REQ_MIN_LEN 4U +#define UDS_0X31_RESP_MIN_LEN 4U +#define UDS_0X34_REQ_BASE_LEN 3U +#define UDS_0X34_RESP_BASE_LEN 2U +#define UDS_0X35_REQ_BASE_LEN 3U +#define UDS_0X35_RESP_BASE_LEN 2U +#define UDS_0X36_REQ_BASE_LEN 2U +#define UDS_0X36_RESP_BASE_LEN 2U +#define UDS_0X37_REQ_BASE_LEN 1U +#define UDS_0X37_RESP_BASE_LEN 1U +#define UDS_0X3E_REQ_MIN_LEN 2U +#define UDS_0X3E_REQ_MAX_LEN 2U +#define UDS_0X3E_RESP_LEN 2U +#define UDS_0X85_REQ_BASE_LEN 2U +#define UDS_0X85_RESP_LEN 2U + +enum UDSDiagnosticServiceId { + kSID_DIAGNOSTIC_SESSION_CONTROL = 0x10, + kSID_ECU_RESET = 0x11, + kSID_CLEAR_DIAGNOSTIC_INFORMATION = 0x14, + kSID_READ_DTC_INFORMATION = 0x19, + kSID_READ_DATA_BY_IDENTIFIER = 0x22, + kSID_READ_MEMORY_BY_ADDRESS = 0x23, + kSID_READ_SCALING_DATA_BY_IDENTIFIER = 0x24, + kSID_SECURITY_ACCESS = 0x27, + kSID_COMMUNICATION_CONTROL = 0x28, + kSID_READ_PERIODIC_DATA_BY_IDENTIFIER = 0x2A, + kSID_DYNAMICALLY_DEFINE_DATA_IDENTIFIER = 0x2C, + kSID_WRITE_DATA_BY_IDENTIFIER = 0x2E, + kSID_INPUT_CONTROL_BY_IDENTIFIER = 0x2F, + kSID_ROUTINE_CONTROL = 0x31, + kSID_REQUEST_DOWNLOAD = 0x34, + kSID_REQUEST_UPLOAD = 0x35, + kSID_TRANSFER_DATA = 0x36, + kSID_REQUEST_TRANSFER_EXIT = 0x37, + kSID_REQUEST_FILE_TRANSFER = 0x38, + kSID_WRITE_MEMORY_BY_ADDRESS = 0x3D, + kSID_TESTER_PRESENT = 0x3E, + kSID_ACCESS_TIMING_PARAMETER = 0x83, + kSID_SECURED_DATA_TRANSMISSION = 0x84, + kSID_CONTROL_DTC_SETTING = 0x85, + kSID_RESPONSE_ON_EVENT = 0x86, +}; diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..d47ba5b --- /dev/null +++ b/src/util.c @@ -0,0 +1,30 @@ +#include "util.h" + +#if UDS_CUSTOM_MILLIS +#else +uint32_t UDSMillis(void) { +#if UDS_SYS == UDS_SYS_UNIX + struct timeval te; + gettimeofday(&te, NULL); + long long milliseconds = te.tv_sec * 1000LL + te.tv_usec / 1000; + return milliseconds; +#elif UDS_SYS == UDS_SYS_WINDOWS + struct timespec ts; + timespec_get(&ts, TIME_UTC); + long long milliseconds = ts.tv_sec * 1000LL + ts.tv_nsec / 1000000; + return milliseconds; +#elif UDS_SYS == UDS_SYS_ARDUINO + return millis(); +#elif UDS_SYS == UDS_SYS_ESP32 + return esp_timer_get_time() / 1000; +#else +#error "UDSMillis() undefined!" +#endif +} +#endif + +bool UDSSecurityAccessLevelIsReserved(uint8_t securityLevel) { + securityLevel &= 0x3f; + return (0 == securityLevel || (0x43 <= securityLevel && securityLevel >= 0x5E) || + 0x7F == securityLevel); +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..01e1d6c --- /dev/null +++ b/src/util.h @@ -0,0 +1,40 @@ +#pragma once + +#include "sys.h" +#include "config.h" + +#if UDS_ENABLE_ASSERT +#include +#else +#define assert(x) +#endif + +#if UDS_ENABLE_DBG_PRINT +#if defined(UDS_DBG_PRINT_IMPL) +#define UDS_DBG_PRINT UDS_DBG_PRINT_IMPL +#else +#include +#define UDS_DBG_PRINT printf +#endif +#else +#define UDS_DBG_PRINT(fmt, ...) ((void)fmt) +#endif + +#define UDS_DBG_PRINTHEX(addr, len) \ + for (int i = 0; i < len; i++) { \ + UDS_DBG_PRINT("%02x,", ((uint8_t *)addr)[i]); \ + } \ + UDS_DBG_PRINT("\n"); + +/* returns true if `a` is after `b` */ +static inline bool UDSTimeAfter(uint32_t a, uint32_t b) { + return ((int32_t)((int32_t)(b) - (int32_t)(a)) < 0); +} + +/** + * @brief Get time in milliseconds + * @return current time in milliseconds + */ +uint32_t UDSMillis(void); + +bool UDSSecurityAccessLevelIsReserved(uint8_t securityLevel); diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..9518c32 --- /dev/null +++ b/src/version.h @@ -0,0 +1,2 @@ +#pragma once +#define UDS_VERSION "0.7.0" diff --git a/test/BUILD b/test/BUILD new file mode 100644 index 0000000..c6594fe --- /dev/null +++ b/test/BUILD @@ -0,0 +1,142 @@ +cc_library( + name = "env", + srcs = [ + "env.c", + "env.h", + "test.h", + "//:iso14229.h", + "//:iso14229.c", + ], + deps = [ "@cmocka" ], + defines = [ + "UDS_TP_ISOTP_C_SOCKETCAN", + "UDS_TP_ISOTP_SOCK", + "UDS_TP_MOCK", + "UDS_CUSTOM_MILLIS", + "UDS_ENABLE_DBG_PRINT", + "UDS_ENABLE_ASSERT", + # "UDS_LINES", + ], + copts = [ "-g", ], +) + +TEST_SRCS = [ + "test_client_0x11_ECU_reset.c", + "test_client_0x22_RDBI_unpack_response.c", + "test_client_0x31_RCRRP.c", + "test_client_0x34_request_download.c", + "test_client_busy.c", + "test_client_p2.c", + "test_client_suppress_positive_response.c", + "test_server_0x10_diag_sess_ctrl_functional_request.c", + "test_server_0x10_diag_sess_ctrl_is_disabled_by_default.c", + "test_server_0x11_no_send_recv_after_ECU_reset.c", + "test_server_0x22_RDBI.c", + "test_server_0x23_read_memory_by_address.c", + "test_server_0x27_security_access.c", + "test_server_0x31_RCRRP.c", + "test_server_0x34.c", + "test_server_0x3E_suppress_positive_response.c", + "test_server_0x83_diagnostic_session_control.c", + "test_server_session_timeout.c", + "test_tp_compliance.c", +] + +TEST_NAMES = [ src.split(".c")[0] for src in TEST_SRCS ] + +# Generic unit tests +# These should pass on all architectures and transports +[ + cc_test( + name=name + "_tp_mock", + srcs=[src], + deps=[":env"], + size = "small", + env = { "UDS_TP_TYPE": "0", }, + copts = ["-g"], + tags = [ "exclusive", ], + ) for name, src in zip(TEST_NAMES, TEST_SRCS) +] + +[ + cc_test( + name=name + "_tp_sock", + srcs=[src], + deps=[":env"], + size = "small", + env = { "UDS_TP_TYPE": "1"}, + copts = ["-g"], + tags = [ "exclusive", "vcan", "isotp_sock"], + ) for name, src in zip(TEST_NAMES, TEST_SRCS) +] + +[ + cc_test( + name=name + "_tp_c", + srcs=[src], + deps=[":env"], + size = "small", + env = { "UDS_TP_TYPE": "2"}, + copts = ["-g"], + tags = [ "exclusive", "vcan"], + ) for name, src in zip(TEST_NAMES, TEST_SRCS) +] + +cc_test( + name = "test_fuzz_server", + srcs = [ + "test_fuzz_server.c", + "//:iso14229.h", + "//:iso14229.c", + ], + defines = [ + "UDS_TP_MOCK", + "UDS_CUSTOM_MILLIS", + # "UDS_DBG_PRINT=printf", + ], + copts = [ + "-g", + "-O1", + "-fsanitize=fuzzer,signed-integer-overflow,address,undefined", + "-fprofile-instr-generate", + "-fcoverage-mapping", + ], + linkopts = [ + "-fsanitize=fuzzer,signed-integer-overflow,address,undefined", + ], + size = "enormous", + target_compatible_with = ["//platforms/compiler:clang"], +) + +sh_test( + name = "test_prefix", + srcs = ["test_prefix.sh"], + data = ["//:iso14229"], + args = ["$(locations //:iso14229)"], + size = "small", + + # Not exactly right. It's to prevent this test from being run under qemu + target_compatible_with = ["@platforms//cpu:x86_64"], +) + +cc_library( + name = "ultra_strict", + srcs = [ + "//:iso14229.h", + "//:iso14229.c", + ], + copts = [ + "-Werror", + "-Wall", + "-Wextra", + "-Wpedantic", + "-Wno-unused-parameter", + "-Wno-gnu-zero-variadic-macro-arguments", + ], + defines = [ + "UDS_TP_ISOTP_C_SOCKETCAN", + "UDS_TP_ISOTP_SOCK", + "UDS_TP_MOCK", + ], + target_compatible_with = ["//platforms/compiler:clang"], +) diff --git a/test/README.md b/test/README.md new file mode 100644 index 0000000..9a66df7 --- /dev/null +++ b/test/README.md @@ -0,0 +1,48 @@ + +Testing aims to cover: +- processor architecture (x86, ARM M4, PPC, ...) +- transport layer implementation +- fault injection +- fuzzing +- style + + +```sh +#!/bin/bash + +bazel test --test_output=all --local_test_jobs=1 //test:all + +# If you want to run the qemu tests, you'll need +sudo apt install -y \ +qemu-system-arm \ +qemu-user \ +gcc-arm-linux-gnueabihf \ +gcc-powerpc-linux-gnu \ +gcc-powerpc64-linux-gnu \ +gcc-powerpc64le-linux-gnu \ +clang-15 \ +``` + +```sh +# run host tests +bazel test //test:all + +# run tests in qemu +bazel test --config=arm_linux //test:all +bazel test --config=ppc //test:all +bazel test --config=ppc64 //test:all +bazel test --config=ppc64le //test:all + +# build the fuzzer +bazel build -s --verbose_failures --config=x85_64_clang //test:test_fuzz_server +# run the fuzzer +mkdir .libfuzzer_artifacts .libfuzzer_corpus +bazel-bin/test/test_fuzz_server -jobs=7 -artifact_prefix=./.libfuzzer_artifacts/ .libfuzzer_corpus +``` + +If building fails with `/usr/bin/ld: cannot find -lstdc++: No such file or directory` + +```sh +sudo apt install libc++-15-dev libc++abi-15-dev +sudo ln -s /usr/lib/x86_64-linux-gnu/libstdc++.so /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.30 +``` \ No newline at end of file diff --git a/test/cmocka.BUILD b/test/cmocka.BUILD new file mode 100644 index 0000000..0d0fbc4 --- /dev/null +++ b/test/cmocka.BUILD @@ -0,0 +1,17 @@ +package(default_visibility = ["//visibility:public"]) + + +cc_library( + name = "cmocka", + srcs = [ + "include/cmocka.h", + "include/cmocka_private.h", + "src/cmocka.c", + ], + copts = [ + "-DHAVE_SIGNAL_H", + ], + includes = [ + "include", + ], +) \ No newline at end of file diff --git a/test/env.c b/test/env.c new file mode 100644 index 0000000..f70c8d3 --- /dev/null +++ b/test/env.c @@ -0,0 +1,186 @@ +#include "test/env.h" +#include "iso14229.h" +#include +#include +#include +#include +#include + +static UDSServer_t *registeredServer = NULL; +static UDSClient_t *registeredClient = NULL; +#define MAX_NUM_TP 16 +static UDSTpHandle_t *registeredTps[MAX_NUM_TP]; +static unsigned TPCount = 0; +static uint32_t TimeNowMillis = 0; + +static ENV_Opts_t opts = { + .tp_type = ENV_TP_TYPE_MOCK, + .ifname = "vcan0", + .srv_src_addr = 0x7E8, + .srv_target_addr = 0x7E0, + .srv_src_addr_func = 0x7DF, + .cli_src_addr = 0x7E0, + .cli_target_addr = 0x7E8, + .cli_tgt_addr_func = 0x7DF, +}; + +static const char *parse_env_var(const char *name, const char *default_val) { + const char *val = getenv(name); + if (val) { + return val; + } + return default_val; +} + +static const int parse_int_env_var(const char *name, const int default_val) { + const char *val = getenv(name); + if (val) { + return atoi(val); + } + return default_val; +} + +static void ENV_ParseOpts() { + opts.ifname = parse_env_var("UDS_IFNAME", opts.ifname); + opts.tp_type = parse_int_env_var("UDS_TP_TYPE", opts.tp_type); + opts.srv_src_addr = parse_int_env_var("UDS_SRV_SRC_ADDR", opts.srv_src_addr); + opts.srv_target_addr = parse_int_env_var("UDS_SRV_TARGET_ADDR", opts.srv_target_addr); + opts.srv_src_addr_func = parse_int_env_var("UDS_SRV_SRC_ADDR_FUNC", opts.srv_src_addr_func); + opts.cli_src_addr = parse_int_env_var("UDS_CLI_SRC_ADDR", opts.cli_src_addr); + opts.cli_target_addr = parse_int_env_var("UDS_CLI_TARGET_ADDR", opts.cli_target_addr); + opts.cli_tgt_addr_func = parse_int_env_var("UDS_CLI_TGT_ADDR_FUNC", opts.cli_tgt_addr_func); +} + +void ENV_ServerInit(UDSServer_t *srv) { + ENV_ParseOpts(); + UDSServerInit(srv); + srv->tp = ENV_TpNew("server"); + ENV_RegisterServer(srv); +} + +void ENV_ClientInit(UDSClient_t *cli) { + ENV_ParseOpts(); + UDSClientInit(cli); + cli->tp = ENV_TpNew("client"); + ENV_RegisterClient(cli); +} + +void ENV_RegisterServer(UDSServer_t *server) { registeredServer = server; } + +void ENV_RegisterClient(UDSClient_t *client) { registeredClient = client; } + +uint32_t UDSMillis() { return TimeNowMillis; } + +// actually sleep for milliseconds +void msleep(int ms) { usleep(ms * 1000); } + +static bool IsNetworkedTransport(int tp_type) { + return tp_type == ENV_TP_TYPE_ISOTP_SOCK || tp_type == ENV_TP_TYPE_ISOTPC; +} + +void ENV_RunMillis(uint32_t millis) { + uint32_t end = UDSMillis() + millis; + while (UDSMillis() < end) { + if (registeredServer) { + UDSServerPoll(registeredServer); + } + if (registeredClient) { + UDSClientPoll(registeredClient); + } + for (unsigned i = 0; i < TPCount; i++) { + UDSTpPoll(registeredTps[i]); + } + TimeNowMillis++; + + // uses vcan, needs delay + if (IsNetworkedTransport(opts.tp_type)) { + // usleep(10); + msleep(1); + } + } +} + +UDSTpHandle_t *ENV_TpNew(const char *name) { + ENV_ParseOpts(); + UDSTpHandle_t *tp = NULL; + if (MAX_NUM_TP == TPCount) { + printf("too many TPs\n"); + return NULL; + } + if (0 == strcasecmp(name, "server")) + switch (opts.tp_type) { + case ENV_TP_TYPE_MOCK: + tp = TPMockNew("server", TPMOCK_DEFAULT_SERVER_ARGS); + break; + case ENV_TP_TYPE_ISOTP_SOCK: { + UDSTpIsoTpSock_t *isotp = malloc(sizeof(UDSTpIsoTpSock_t)); + strcpy(isotp->tag, "server"); + assert(UDS_OK == UDSTpIsoTpSockInitServer(isotp, opts.ifname, opts.srv_src_addr, + opts.srv_target_addr, + opts.srv_src_addr_func)); + tp = (UDSTpHandle_t *)isotp; + break; + } + case ENV_TP_TYPE_ISOTPC: { + UDSTpISOTpC_t *isotp = malloc(sizeof(UDSTpISOTpC_t)); + strcpy(isotp->tag, "server"); + + assert(UDS_OK == UDSTpISOTpCInit(isotp, opts.ifname, opts.srv_src_addr, + opts.srv_target_addr, opts.srv_src_addr_func, 0)); + tp = (UDSTpHandle_t *)isotp; + break; + } + default: + printf("unknown TP type: %d\n", opts.tp_type); + return NULL; + } + else if (0 == strcasecmp(name, "client")) { + switch (opts.tp_type) { + case ENV_TP_TYPE_MOCK: + tp = TPMockNew("client", TPMOCK_DEFAULT_CLIENT_ARGS); + break; + case ENV_TP_TYPE_ISOTP_SOCK: { + UDSTpIsoTpSock_t *isotp = malloc(sizeof(UDSTpIsoTpSock_t)); + strcpy(isotp->tag, "client"); + assert(UDS_OK == UDSTpIsoTpSockInitClient(isotp, opts.ifname, opts.cli_src_addr, + opts.cli_target_addr, + opts.cli_tgt_addr_func)); + tp = (UDSTpHandle_t *)isotp; + break; + } + case ENV_TP_TYPE_ISOTPC: { + UDSTpISOTpC_t *isotp = malloc(sizeof(UDSTpISOTpC_t)); + strcpy(isotp->tag, "client"); + assert(UDS_OK == UDSTpISOTpCInit(isotp, opts.ifname, opts.cli_src_addr, + opts.cli_target_addr, 0, opts.cli_tgt_addr_func)); + tp = (UDSTpHandle_t *)isotp; + break; + } + default: + printf("unknown TP type: %d\n", opts.tp_type); + return NULL; + } + } else { + printf("unknown mock tp: %s\n", name); + return NULL; + } + registeredTps[TPCount++] = tp; + return tp; +} + +void ENV_TpFree(UDSTpHandle_t *tp) { + switch (opts.tp_type) { + case ENV_TP_TYPE_MOCK: + TPMockFree(tp); + break; + case ENV_TP_TYPE_ISOTP_SOCK: + UDSTpIsoTpSockDeinit((UDSTpIsoTpSock_t *)tp); + break; + case ENV_TP_TYPE_ISOTPC: + UDSTpISOTpCDeinit((UDSTpISOTpC_t *)tp); + free(tp); + break; + } +} + +const ENV_Opts_t *ENV_GetOpts() { return &opts; } \ No newline at end of file diff --git a/test/env.h b/test/env.h new file mode 100644 index 0000000..993609e --- /dev/null +++ b/test/env.h @@ -0,0 +1,47 @@ +#ifndef ENV_H +#define ENV_H + +#include "iso14229.h" +#include +#include +#include + +enum ENV_TpType { + ENV_TP_TYPE_MOCK, // tp/mock.h + ENV_TP_TYPE_ISOTP_SOCK, // tp/isotp_sock.h + ENV_TP_TYPE_ISOTPC, // tp/isotp_c_socketcan.h +}; + +/** + * @brief Environment options + */ +typedef struct { + enum ENV_TpType tp_type; // transport type + const char *ifname; // CAN interface name used for isotp_sock and socketcan + uint32_t srv_src_addr, srv_target_addr, srv_src_addr_func; + uint32_t cli_src_addr, cli_target_addr, cli_tgt_addr_func; +} ENV_Opts_t; + +void ENV_ServerInit(UDSServer_t *srv); +void ENV_ClientInit(UDSClient_t *client); + +#define ENV_SERVER_INIT(srv) \ + ENV_ServerInit(&srv); \ + TPMockLogToStdout(); + +#define ENV_CLIENT_INIT(client) \ + ENV_ClientInit(&client); \ + TPMockLogToStdout(); + +/** + * @brief return a transport configured as client + * @return UDSTpHandle_t* + */ +UDSTpHandle_t *ENV_TpNew(const char *name); +void ENV_TpFree(UDSTpHandle_t *tp); +void ENV_RegisterServer(UDSServer_t *server); +void ENV_RegisterClient(UDSClient_t *client); +void ENV_RunMillis(uint32_t millis); +const ENV_Opts_t *ENV_GetOpts(); + +#endif diff --git a/test/test.h b/test/test.h new file mode 100644 index 0000000..61313d1 --- /dev/null +++ b/test/test.h @@ -0,0 +1,107 @@ +#ifndef TEST_TEST_H +#define TEST_TEST_H + +#include +#include +#include +#include +#include "iso14229.h" +#include "test/env.h" +#include + +#define _TEST_INT_COND(a, b, cond) \ + { \ + int _a = a; \ + int _b = b; \ + if (!((_a)cond(_b))) { \ + printf("%s:%d (%d %s %d)\n", __FILE__, __LINE__, _a, #cond, _b); \ + fflush(stdout); \ + assert_true(a cond b); \ + } \ + } + +#define TEST_INT_LT(a, b) _TEST_INT_COND(a, b, <) +#define TEST_INT_LE(a, b) _TEST_INT_COND(a, b, <=) +#define TEST_INT_GE(a, b) _TEST_INT_COND(a, b, >=) +#define TEST_INT_EQUAL(a, b) _TEST_INT_COND(a, b, ==) +#define TEST_INT_NE(a, b) _TEST_INT_COND(a, b, !=) + +#define TEST_PTR_EQUAL(a, b) \ + { \ + const void *_a = a; \ + const void *_b = b; \ + if ((_a) != (_b)) { \ + printf("%s:%d (%p != %p)\n", __FILE__, __LINE__, _a, _b); \ + fflush(stdout); \ + assert(a == b); \ + } \ + } + +#define TEST_MEMORY_EQUAL(a, b, len) \ + { \ + const uint8_t *_a = (const uint8_t *)a; \ + const uint8_t *_b = (const uint8_t *)b; \ + if (memcmp(_a, _b, len)) { \ + printf("A:"); \ + for (unsigned int i = 0; i < len; i++) { \ + printf("%02x,", _a[i]); \ + } \ + printf(" (%s)\nB:", #a); \ + for (unsigned int i = 0; i < len; i++) { \ + printf("%02x,", _b[i]); \ + } \ + printf(" (%s)\n", #b); \ + fflush(stdout); \ + assert_true(0); \ + } \ + } + +#define EXPECT_OK(stmt) \ + { \ + int _ret = stmt; \ + if (_ret != UDS_OK) { \ + printf("%s:%d (%d != %d)\n", __FILE__, __LINE__, _ret, UDS_OK); \ + fflush(stdout); \ + assert_true(_ret == UDS_OK); \ + } \ + } + +// Expect that a condition is true within a timeout +#define EXPECT_WITHIN_MS(cond, timeout_ms) \ + { \ + uint32_t deadline = UDSMillis() + timeout_ms + 1; \ + while (!(cond)) { \ + TEST_INT_LE(UDSMillis(), deadline); \ + ENV_RunMillis(1); \ + } \ + } + +// Expect that a condition is true for a duration +#define EXPECT_WHILE_MS(cond, duration) \ + { \ + uint32_t deadline = UDSMillis() + duration; \ + while (UDSTimeAfter(deadline, UDSMillis())) { \ + assert_true(cond); \ + ENV_RunMillis(1); \ + } \ + } + +// Expect that a condition +// - is false until 90% of a duration has passed, +// - and true before 110% of the duration has passed +#define EXPECT_IN_APPROX_MS(cond, duration) \ + { \ + const float tolerance = 0.1f; \ + uint32_t pre_deadline = UDSMillis() + (int)((duration) * (1.0f - tolerance)); \ + uint32_t post_deadline = UDSMillis() + (int)((duration) * (1.0f + tolerance)); \ + while (UDSTimeAfter(pre_deadline, UDSMillis())) { \ + assert_true(!(cond)); \ + ENV_RunMillis(1); \ + } \ + while (!(cond)) { \ + TEST_INT_LE(UDSMillis(), post_deadline); \ + ENV_RunMillis(1); \ + } \ + } + +#endif diff --git a/test/test_client_0x11_ECU_reset.c b/test/test_client_0x11_ECU_reset.c new file mode 100644 index 0000000..6631df9 --- /dev/null +++ b/test/test_client_0x11_ECU_reset.c @@ -0,0 +1,55 @@ +#include "test/test.h" +#include + +int main() { + UDSClient_t client; + + const uint8_t GOOD[] = {0x51, 0x01}; + const uint8_t BAD_SID[] = {0x50, 0x01}; + const uint8_t TOO_SHORT[] = {0x51}; + const uint8_t BAD_SUBFUNC[] = {0x51, 0x02}; + const uint8_t NEG[] = {0x7F, 0x11, 0x10}; + +#define CASE(d1, opt, err) \ + { \ + .tag = "client options:" #opt ", server resp: " #d1 ", expected_err: " #err, .resp = d1, \ + .resp_len = sizeof(d1), .options = opt, .expected_err = err \ + } + + struct { + const char *tag; + const uint8_t *resp; + size_t resp_len; + uint8_t options; + UDSErr_t expected_err; + } p[] = { + CASE(GOOD, 0, UDS_OK), + CASE(BAD_SID, 0, UDS_ERR_SID_MISMATCH), + CASE(TOO_SHORT, 0, UDS_ERR_RESP_TOO_SHORT), + CASE(BAD_SUBFUNC, 0, UDS_ERR_SUBFUNCTION_MISMATCH), + CASE(NEG, 0, UDS_OK), + CASE(NEG, UDS_NEG_RESP_IS_ERR, UDS_ERR_NEG_RESP), + CASE(GOOD, UDS_NEG_RESP_IS_ERR, UDS_OK), + CASE(GOOD, UDS_SUPPRESS_POS_RESP, UDS_OK), // Should this case pass? + }; + + for (size_t i = 0; i < sizeof(p) / sizeof(p[0]); i++) { + ENV_CLIENT_INIT(client); + UDSTpHandle_t *srv = ENV_TpNew("server"); + + // when the client sends a ECU reset request with these options + client.options = p[i].options; + UDSSendECUReset(&client, kHardReset); + + // and the server responds with this message + UDSTpSend(srv, p[i].resp, p[i].resp_len, NULL); + + // then the client should receive a response with this error code + ENV_RunMillis(50); + TEST_INT_EQUAL(client.state, kRequestStateIdle); + TEST_INT_EQUAL(client.err, p[i].expected_err); + // assert(client.err == p[i].expected_err); + ENV_TpFree(srv); + ENV_TpFree(client.tp); + } +} \ No newline at end of file diff --git a/test/test_client_0x22_RDBI_unpack_response.c b/test/test_client_0x22_RDBI_unpack_response.c new file mode 100644 index 0000000..8591f4d --- /dev/null +++ b/test/test_client_0x22_RDBI_unpack_response.c @@ -0,0 +1,23 @@ +#include "test/test.h" + +int main() { + uint8_t RESP[] = {0x72, 0x12, 0x34, 0x00, 0x00, 0xAA, 0x00, 0x56, 0x78, 0xAA, 0xBB}; + uint8_t buf[4]; + uint16_t offset = 0; + int err = 0; + err = UDSUnpackRDBIResponse(RESP, sizeof(RESP), 0x1234, buf, 4, &offset); + TEST_INT_EQUAL(err, UDS_OK); + uint32_t d0 = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; + TEST_INT_EQUAL(d0, 0x0000AA00); + err = UDSUnpackRDBIResponse(RESP, sizeof(RESP), 0x1234, buf, 2, &offset); + TEST_INT_EQUAL(err, UDS_ERR_DID_MISMATCH); + err = UDSUnpackRDBIResponse(RESP, sizeof(RESP), 0x5678, buf, 20, &offset); + TEST_INT_EQUAL(err, UDS_ERR_RESP_TOO_SHORT); + err = UDSUnpackRDBIResponse(RESP, sizeof(RESP), 0x5678, buf, 2, &offset); + TEST_INT_EQUAL(err, UDS_OK); + uint16_t d1 = (buf[0] << 8) + buf[1]; + TEST_INT_EQUAL(d1, 0xAABB); + err = UDSUnpackRDBIResponse(RESP, sizeof(RESP), 0x5678, buf, 1, &offset); + TEST_INT_EQUAL(err, UDS_ERR_RESP_TOO_SHORT); + TEST_INT_EQUAL(offset, sizeof(RESP)); +} \ No newline at end of file diff --git a/test/test_client_0x31_RCRRP.c b/test/test_client_0x31_RCRRP.c new file mode 100644 index 0000000..3a3aa26 --- /dev/null +++ b/test/test_client_0x31_RCRRP.c @@ -0,0 +1,69 @@ +#include "test/test.h" + +static UDSClient_t client; +static UDSTpHandle_t *mock_srv; + +int setup(void **state) { + ENV_CLIENT_INIT(client) + mock_srv = ENV_TpNew("server"); + return 0; +} + +int teardown(void **state) { + ENV_TpFree(mock_srv); + ENV_TpFree(client.tp); + return 0; +} + +void test_RCRRP_timeout(void **state) { + ENV_CLIENT_INIT(client); + // When a request is sent + UDSSendRoutineCtrl(&client, kStartRoutine, 0x1234, NULL, 0); + + // that receives an RCRRP response + const uint8_t RCRRP[] = {0x7F, 0x31, 0x78}; // RequestCorrectly-ReceievedResponsePending + UDSTpSend(mock_srv, RCRRP, sizeof(RCRRP), NULL); + + // that remains unresolved at a time between p2 ms and p2 star ms + ENV_RunMillis(UDS_CLIENT_DEFAULT_P2_MS + 10); + // the client should still be pending. + TEST_INT_EQUAL(kRequestStateAwaitResponse, client.state) + + // after p2_star has elapsed, the client should timeout + ENV_RunMillis(UDS_CLIENT_DEFAULT_P2_STAR_MS + 10); + TEST_INT_EQUAL(kRequestStateIdle, client.state) + TEST_INT_EQUAL(client.err, UDS_ERR_TIMEOUT); +} + +void test_RCRRP_positive_response(void **state) { + // When a request is sent + UDSSendRoutineCtrl(&client, kStartRoutine, 0x1234, NULL, 0); + + // that receives an RCRRP response + const uint8_t RCRRP[] = {0x7F, 0x31, 0x78}; // RequestCorrectly-ReceievedResponsePending + UDSTpSend(mock_srv, RCRRP, sizeof(RCRRP), NULL); + + // that remains unresolved at a time between p2 ms and p2 star ms + ENV_RunMillis(UDS_CLIENT_DEFAULT_P2_MS + 10); + // the client should still be pending. + TEST_INT_EQUAL(client.err, UDS_OK); + TEST_INT_EQUAL(kRequestStateAwaitResponse, client.state) + + // When the client receives a positive response from the server + const uint8_t POSITIVE_RESPONSE[] = {0x71, 0x01, 0x12, 0x34}; + UDSTpSend(mock_srv, POSITIVE_RESPONSE, sizeof(POSITIVE_RESPONSE), NULL); + + ENV_RunMillis(5); + + // the client should return to the idle state with no error + TEST_INT_EQUAL(kRequestStateIdle, client.state) + TEST_INT_EQUAL(client.err, UDS_OK); +} + +int main() { + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_RCRRP_timeout, setup, teardown), + cmocka_unit_test_setup_teardown(test_RCRRP_positive_response, setup, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} \ No newline at end of file diff --git a/test/test_client_0x34_request_download.c b/test/test_client_0x34_request_download.c new file mode 100644 index 0000000..e799c8f --- /dev/null +++ b/test/test_client_0x34_request_download.c @@ -0,0 +1,13 @@ +#include "test/test.h" + +int main() { + UDSClient_t client; + ENV_CLIENT_INIT(client); + + // When the client sends a request download request + EXPECT_OK(UDSSendRequestDownload(&client, 0x11, 0x33, 0x602000, 0x00FFFF)); + + // the bytes sent should match UDS-1 2013 Table 415 + const uint8_t CORRECT_REQUEST[] = {0x34, 0x11, 0x33, 0x60, 0x20, 0x00, 0x00, 0xFF, 0xFF}; + TEST_MEMORY_EQUAL(client.send_buf, CORRECT_REQUEST, sizeof(CORRECT_REQUEST)); +} \ No newline at end of file diff --git a/test/test_client_busy.c b/test/test_client_busy.c new file mode 100644 index 0000000..de2dec9 --- /dev/null +++ b/test/test_client_busy.c @@ -0,0 +1,12 @@ +#include "test/test.h" + +int main() { + UDSClient_t client; + ENV_CLIENT_INIT(client); + + // Sending a request should not return an error + EXPECT_OK(UDSSendECUReset(&client, kHardReset)); + + // unless there is an existing unresolved request + TEST_INT_EQUAL(UDS_ERR_BUSY, UDSSendECUReset(&client, kHardReset)); +} \ No newline at end of file diff --git a/test/test_client_p2.c b/test/test_client_p2.c new file mode 100644 index 0000000..c45d1a9 --- /dev/null +++ b/test/test_client_p2.c @@ -0,0 +1,39 @@ +#include "iso14229.h" +#include "test/test.h" + +int main() { + UDSClient_t client; + UDSTpHandle_t *tp = ENV_TpNew("server"); + + { // Case 1: P2 not exceeded + ENV_CLIENT_INIT(client); + + // When the client sends a request + EXPECT_OK(UDSSendECUReset(&client, kHardReset)); + + // which receives a positive response + const uint8_t POSITIVE_RESPONSE[] = {0x51, 0x01}; + UDSTpSend(tp, POSITIVE_RESPONSE, sizeof(POSITIVE_RESPONSE), NULL); + ENV_RunMillis(20); + + // after p2 ms has elapsed, the client should have a timeout error + TEST_INT_EQUAL(UDS_OK, client.err); + + UDSTpAckRecv(tp); + } + { // Case 2: P2 exceeded + ENV_CLIENT_INIT(client); + + // When the client sends a request + EXPECT_OK(UDSSendECUReset(&client, kHardReset)); + + ENV_RunMillis(UDS_CLIENT_DEFAULT_P2_MS - 10); + // before p2 ms has elapsed, the client should have no error + TEST_INT_EQUAL(UDS_OK, client.err); + + ENV_RunMillis(20); + TEST_INT_EQUAL(UDS_ERR_TIMEOUT, client.err); + + UDSTpAckRecv(tp); + } +} \ No newline at end of file diff --git a/test/test_client_suppress_positive_response.c b/test/test_client_suppress_positive_response.c new file mode 100644 index 0000000..2b761b6 --- /dev/null +++ b/test/test_client_suppress_positive_response.c @@ -0,0 +1,17 @@ +#include "test/test.h" + +int main() { + UDSClient_t client; + ENV_CLIENT_INIT(client); + + // Setting the suppressPositiveResponse flag before sending a request + client.options |= UDS_SUPPRESS_POS_RESP; + UDSSendECUReset(&client, kHardReset); + + // and not receiving a response after approximately p2 ms + ENV_RunMillis(UDS_CLIENT_DEFAULT_P2_MS + 10); + + // should not result in an error. + TEST_INT_EQUAL(UDS_OK, client.err); + TEST_INT_EQUAL(kRequestStateIdle, client.state); +} diff --git a/test/test_fuzz_server.c b/test/test_fuzz_server.c new file mode 100644 index 0000000..2a3c870 --- /dev/null +++ b/test/test_fuzz_server.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include +#include +#include +#include "iso14229.h" + +#ifdef __cplusplus +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *, size_t); +#else +int LLVMFuzzerTestOneInput(const uint8_t *, size_t); +#endif + +typedef struct { + uint8_t srv_retval; + uint16_t client_sa; + uint16_t client_ta; + uint8_t client_func_req; + uint8_t msg[UDS_TP_MTU]; +} StuffToFuzz_t; + +static StuffToFuzz_t fuzz; +static uint8_t client_recv_buf[UDS_TP_MTU]; + +static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + printf("Whoah, got event %d\n", ev); + return fuzz.srv_retval; +} + +static uint32_t g_ms = 0; +uint32_t UDSMillis() { return g_ms; } +static UDSServer_t srv; +static UDSTpHandle_t *mock_client = NULL; + +void DoInitialization() { + UDSServerInit(&srv); + srv.tp = TPMockNew("server", TPMOCK_DEFAULT_SERVER_ARGS); + mock_client = TPMockNew("client", TPMOCK_DEFAULT_CLIENT_ARGS); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + static bool initialized = false; + if (!initialized) { + DoInitialization(); + initialized = true; + } + memset(&fuzz, 0, sizeof(fuzz)); + memmove(&fuzz, data, size); + + UDSSDU_t msg = { + .A_Mtype = UDS_A_MTYPE_DIAG, + .A_SA = fuzz.client_sa, + .A_TA = fuzz.client_ta, + .A_TA_Type = fuzz.client_func_req ? UDS_A_TA_TYPE_FUNCTIONAL : UDS_A_TA_TYPE_PHYSICAL, + .A_Length = size > offsetof(StuffToFuzz_t, msg) ? size - offsetof(StuffToFuzz_t, msg) : 0, + .A_Data = (uint8_t *)data + offsetof(StuffToFuzz_t, msg), + .A_DataBufSize = sizeof(fuzz.msg), + }; + mock_client->send(mock_client, &msg); + + for (g_ms = 0; g_ms < 100; g_ms++) { + UDSServerPoll(&srv); + } + + { + UDSSDU_t msg2 = { + .A_Data = client_recv_buf, + .A_DataBufSize = sizeof(client_recv_buf), + }; + mock_client->recv(mock_client, &msg2); + } + TPMockReset(); + return 0; +} diff --git a/test/test_prefix.sh b/test/test_prefix.sh new file mode 100755 index 0000000..c3018a7 --- /dev/null +++ b/test/test_prefix.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +# test fails if any exported symbols do not start with " UDS" +nm $1 | grep ' T ' | grep -v ' UDS' +test $? = 1 \ No newline at end of file diff --git a/test/test_server_0x10_diag_sess_ctrl_functional_request.c b/test/test_server_0x10_diag_sess_ctrl_functional_request.c new file mode 100644 index 0000000..a15cf3d --- /dev/null +++ b/test/test_server_0x10_diag_sess_ctrl_functional_request.c @@ -0,0 +1,16 @@ +#include "test/test.h" + +int main() { + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + + // When server is sent a diagnostic session control request + const uint8_t REQ[] = {0x10, 0x02}; + UDSTpSend(mock_client, REQ, sizeof(REQ), &(UDSSDU_t){.A_TA_Type = UDS_A_TA_TYPE_FUNCTIONAL}); + + // the server should respond with a negative response within p2 ms + const uint8_t EXP_RESP[] = {0x7f, 0x10, 0x11}; + EXPECT_IN_APPROX_MS((UDSTpGetRecvLen(mock_client) > 0), srv.p2_ms) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), EXP_RESP, sizeof(EXP_RESP)); +} diff --git a/test/test_server_0x10_diag_sess_ctrl_is_disabled_by_default.c b/test/test_server_0x10_diag_sess_ctrl_is_disabled_by_default.c new file mode 100644 index 0000000..61b12a0 --- /dev/null +++ b/test/test_server_0x10_diag_sess_ctrl_is_disabled_by_default.c @@ -0,0 +1,16 @@ +#include "test/test.h" + +int main() { + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + + // When server is sent a diagnostic session control request + const uint8_t REQ[] = {0x10, 0x02}; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + + // the server should respond with a negative response within p2 ms + const uint8_t EXP_RESP[] = {0x7f, 0x10, 0x11}; + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) > 0, srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), EXP_RESP, sizeof(EXP_RESP)); +} diff --git a/test/test_server_0x11_no_send_recv_after_ECU_reset.c b/test/test_server_0x11_no_send_recv_after_ECU_reset.c new file mode 100644 index 0000000..a822640 --- /dev/null +++ b/test/test_server_0x11_no_send_recv_after_ECU_reset.c @@ -0,0 +1,39 @@ +#include "iso14229.h" +#include "test/test.h" + +uint8_t fn_callCount = 0; +uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + switch (ev) { + case UDS_SRV_EVT_EcuReset: + fn_callCount += 1; + return kPositiveResponse; + default: + TEST_INT_EQUAL(UDS_SRV_EVT_DoScheduledReset, ev); + return kPositiveResponse; + } +} + +int main() { + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + srv.fn = fn; + + const uint8_t REQ[] = {0x11, 0x01}; + const uint8_t RESP[] = {0x51, 0x01}; + + // Sending the first ECU reset should result in a response + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client), srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RESP, sizeof(RESP)); + + UDSTpAckRecv(mock_client); + + // Sending subsequent ECU reset requests should never receive any response + const unsigned LONG_TIME_MS = 5000; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + EXPECT_WHILE_MS(UDSTpGetRecvLen(mock_client) == 0, LONG_TIME_MS); + + // Additionally the ECU reset handler should have been called exactly once. + TEST_INT_EQUAL(fn_callCount, 1); +} diff --git a/test/test_server_0x22_RDBI.c b/test/test_server_0x22_RDBI.c new file mode 100644 index 0000000..a8ad7a6 --- /dev/null +++ b/test/test_server_0x22_RDBI.c @@ -0,0 +1,47 @@ +#include "iso14229.h" +#include "test/test.h" + +uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + TEST_INT_EQUAL(UDS_SRV_EVT_ReadDataByIdent, ev); + const uint8_t vin[] = {0x57, 0x30, 0x4C, 0x30, 0x30, 0x30, 0x30, 0x34, 0x33, + 0x4D, 0x42, 0x35, 0x34, 0x31, 0x33, 0x32, 0x36}; + const uint8_t data_0x010A[] = {0xA6, 0x66, 0x07, 0x50, 0x20, 0x1A, + 0x00, 0x63, 0x4A, 0x82, 0x7E}; + const uint8_t data_0x0110[] = {0x8C}; + + UDSRDBIArgs_t *r = (UDSRDBIArgs_t *)arg; + switch (r->dataId) { + case 0xF190: + return r->copy(srv, vin, sizeof(vin)); + case 0x010A: + return r->copy(srv, data_0x010A, sizeof(data_0x010A)); + case 0x0110: + return r->copy(srv, data_0x0110, sizeof(data_0x0110)); + default: + return kRequestOutOfRange; + } + return kPositiveResponse; +} + +int main() { + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + srv.fn = fn; + { // 11.2.5.2 Example #1 read single dataIdentifier 0xF190 + uint8_t REQ[] = {0x22, 0xF1, 0x90}; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + uint8_t RESP[] = {0x62, 0xF1, 0x90, 0x57, 0x30, 0x4C, 0x30, 0x30, 0x30, 0x30, + 0x34, 0x33, 0x4D, 0x42, 0x35, 0x34, 0x31, 0x33, 0x32, 0x36}; + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) == sizeof(RESP), srv.p2_ms) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RESP, sizeof(RESP)); + UDSTpAckRecv(mock_client); + } + { // Read a nonexistent dataIdentifier 0xF191 + uint8_t REQ[] = {0x22, 0xF1, 0x91}; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + uint8_t RESP[] = {0x7F, 0x22, 0x31}; + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) == sizeof(RESP), srv.p2_ms) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RESP, sizeof(RESP)); + } +} diff --git a/test/test_server_0x23_read_memory_by_address.c b/test/test_server_0x23_read_memory_by_address.c new file mode 100644 index 0000000..364955a --- /dev/null +++ b/test/test_server_0x23_read_memory_by_address.c @@ -0,0 +1,56 @@ +#include "iso14229.h" +#include "test/test.h" + +uint8_t FakeData[259]; + +uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + TEST_INT_EQUAL(ev, UDS_SRV_EVT_ReadMemByAddr); + UDSReadMemByAddrArgs_t *r = (UDSReadMemByAddrArgs_t *)arg; + TEST_PTR_EQUAL(r->memAddr, (void *)0x20481392); + TEST_INT_EQUAL(r->memSize, 259); + return r->copy(srv, FakeData, r->memSize); +} + +int main() { + UDSTpHandle_t *client_tp = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + srv.fn = fn; + + // Prepare fake data + for (int i = 0; i < sizeof(FakeData); i++) { + FakeData[i] = i % 256; + } + + // Prepare expected response + uint8_t EXPECTED_RESP[sizeof(FakeData) + 1]; + EXPECTED_RESP[0] = 0x63; // SID 0x23 + 0x40 + for (int i = 0; i < sizeof(FakeData); i++) { + EXPECTED_RESP[i + 1] = FakeData[i]; + } + + // Request per ISO14229-1 2020 Table 200 + const uint8_t REQ[] = { + 0x23, // SID + 0x24, // AddressAndLengthFormatIdentifier + 0x20, // memoryAddress byte #1 (MSB) + 0x48, // memoryAddress byte #2 + 0x13, // memoryAddress byte #3 + 0x92, // memoryAddress byte #4 (LSB) + 0x01, // memorySize byte #1 (MSB) + 0x03, // memorySize byte #2 (LSB) + }; + UDSTpSend(client_tp, REQ, sizeof(REQ), NULL); + + // the client transport should receive a positive response within client_p2 ms + // EXPECT_WITHIN_MS(UDSTpGetRecvLen(client_tp) > 0, UDS_CLIENT_DEFAULT_P2_MS) + uint32_t deadline = UDSMillis() + UDS_CLIENT_DEFAULT_P2_MS + 1; + while (!(UDSTpGetRecvLen(client_tp) > 0)) { + printf("UDSTpGetRecvLen(client_tp) = %ld\n", UDSTpGetRecvLen(client_tp)); + TEST_INT_LE(UDSMillis(), deadline); + ENV_RunMillis(1); + } + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(client_tp, NULL), EXPECTED_RESP, sizeof(EXPECTED_RESP)); +} + +// TODO: Tables 202-205 \ No newline at end of file diff --git a/test/test_server_0x27_security_access.c b/test/test_server_0x27_security_access.c new file mode 100644 index 0000000..e46e661 --- /dev/null +++ b/test/test_server_0x27_security_access.c @@ -0,0 +1,150 @@ +#include "test/test.h" + +static UDSTpHandle_t *mock_client = NULL; +static UDSServer_t srv; + +// UDS-1 2013 9.4.5.2 +// UDS-1 2013 9.4.5.3 +uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + switch (ev) { + case UDS_SRV_EVT_SecAccessRequestSeed: { + UDSSecAccessRequestSeedArgs_t *r = (UDSSecAccessRequestSeedArgs_t *)arg; + const uint8_t seed[] = {0x36, 0x57}; + TEST_INT_NE(r->level, srv->securityLevel); + return r->copySeed(srv, seed, sizeof(seed)); + break; + } + case UDS_SRV_EVT_SecAccessValidateKey: { + UDSSecAccessValidateKeyArgs_t *r = (UDSSecAccessValidateKeyArgs_t *)arg; + const uint8_t expected_key[] = {0xC9, 0xA9}; + if (memcmp(r->key, expected_key, sizeof(expected_key))) { + return kSecurityAccessDenied; + } else { + return kPositiveResponse; + } + break; + } + default: + assert(0); + } + return kPositiveResponse; +} + +int setup(void **state) { + mock_client = ENV_TpNew("client"); + ENV_SERVER_INIT(srv); + srv.fn = fn; + return 0; +} + +int teardown(void **state) { + ENV_TpFree(mock_client); + ENV_TpFree(srv.tp); + return 0; +} + +void TestSAInit(void **state) { + // the server security level after initialization should be 0 + TEST_INT_EQUAL(srv.securityLevel, 0); +} + + +void TestSABruteForcePrevention1(void **state) { + // sending a seed request should get this response + const uint8_t SEED_REQUEST[] = {0x27, 0x01}; + const uint8_t NEG_RESPONSE[] = {0x7F, 0x27, 0x37}; + UDSTpSend(mock_client, SEED_REQUEST, sizeof(SEED_REQUEST), NULL); + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) != 0, srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), NEG_RESPONSE, sizeof(NEG_RESPONSE)); + UDSTpAckRecv(mock_client); + + // the server security level should still be 0 + TEST_INT_EQUAL(srv.securityLevel, 0); +} + +void TestSABruteForcePrevention2(void **state) { + // the server security level after initialization should be 0 + TEST_INT_EQUAL(srv.securityLevel, 0); + + // Wait for the anti-brute-force timeout to expire + ENV_RunMillis(1500); + + // sending a seed request should get this response + const uint8_t SEED_REQUEST[] = {0x27, 0x01}; + const uint8_t SEED_RESPONSE[] = {0x67, 0x01, 0x36, 0x57}; + UDSTpSend(mock_client, SEED_REQUEST, sizeof(SEED_REQUEST), NULL); + EXPECT_WITHIN_MS(UDSTpGetRecvLen(mock_client) != 0, UDS_SERVER_DEFAULT_P2_MS); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), SEED_RESPONSE, sizeof(SEED_RESPONSE)); + UDSTpAckRecv(mock_client); + + // the server security level should still be 0 + TEST_INT_EQUAL(srv.securityLevel, 0); + + // subsequently sending an unlock request should get this response + const uint8_t BAD_UNLOCK_REQUEST[] = {0x27, 0x02, 0xFF, 0xFF}; + const uint8_t UNLOCK_FAIL[] = {0x7F, 0x27, 0x33}; + UDSTpSend(mock_client, BAD_UNLOCK_REQUEST, sizeof(BAD_UNLOCK_REQUEST), NULL); + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) != 0, srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), UNLOCK_FAIL, sizeof(UNLOCK_FAIL)); + UDSTpAckRecv(mock_client); + + // the server security level should still be 0 + TEST_INT_EQUAL(srv.securityLevel, 0); + + // sending another seed request should get denied + const uint8_t DENIED[] = {0x7F, 0x27, 0x36}; + UDSTpSend(mock_client, SEED_REQUEST, sizeof(SEED_REQUEST), NULL); + EXPECT_WITHIN_MS(UDSTpGetRecvLen(mock_client) != 0, UDS_SERVER_DEFAULT_P2_MS); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), DENIED, sizeof(DENIED)); + UDSTpAckRecv(mock_client); +} + +void TestSAUnlock(void **state) { + // Wait for the anti-brute-force timeout to expire + ENV_RunMillis(1500); + + // sending a seed request should get this response + const uint8_t SEED_REQUEST[] = {0x27, 0x01}; + const uint8_t SEED_RESPONSE[] = {0x67, 0x01, 0x36, 0x57}; + UDSTpSend(mock_client, SEED_REQUEST, sizeof(SEED_REQUEST), NULL); + EXPECT_WITHIN_MS(UDSTpGetRecvLen(mock_client) != 0, UDS_SERVER_DEFAULT_P2_MS); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), SEED_RESPONSE, sizeof(SEED_RESPONSE)); + UDSTpAckRecv(mock_client); + + // the server security level should still be 0 + TEST_INT_EQUAL(srv.securityLevel, 0); + + // subsequently sending an unlock request should get this response + const uint8_t UNLOCK_REQUEST[] = {0x27, 0x02, 0xC9, 0xA9}; + const uint8_t UNLOCK_RESPONSE[] = {0x67, 0x02}; + UDSTpSend(mock_client, UNLOCK_REQUEST, sizeof(UNLOCK_REQUEST), NULL); + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) != 0, srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), UNLOCK_RESPONSE, sizeof(UNLOCK_RESPONSE)); + UDSTpAckRecv(mock_client); + + // the server security level should now be 1 + TEST_INT_EQUAL(srv.securityLevel, 1); + + // sending the same seed request should now result in the "already unlocked" response + const uint8_t ALREADY_UNLOCKED_RESPONSE[] = {0x67, 0x01, 0x00, 0x00}; + UDSTpSend(mock_client, SEED_REQUEST, sizeof(SEED_REQUEST), NULL); + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) != 0, srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), ALREADY_UNLOCKED_RESPONSE, + sizeof(ALREADY_UNLOCKED_RESPONSE)); + + // Additionally, the security level should still be 1 + TEST_INT_EQUAL(srv.securityLevel, 1); +} + + +int main() { + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(TestSAInit, setup, teardown), + cmocka_unit_test_setup_teardown(TestSABruteForcePrevention1, setup, teardown), + cmocka_unit_test_setup_teardown(TestSABruteForcePrevention2, setup, teardown), + cmocka_unit_test_setup_teardown(TestSAUnlock, setup, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} + + diff --git a/test/test_server_0x31_RCRRP.c b/test/test_server_0x31_RCRRP.c new file mode 100644 index 0000000..6b990ca --- /dev/null +++ b/test/test_server_0x31_RCRRP.c @@ -0,0 +1,52 @@ +#include "iso14229.h" +#include "test/test.h" + +static uint8_t resp; +static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { return resp; } + +// ISO-14229-1 2013 Table A.1 Byte Value 0x78: requestCorrectlyReceived-ResponsePending +// "This NRC is in general supported by each diagnostic service". +int main() { + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + srv.fn = fn; + + // When a server handler func initially returns RRCRP + resp = kRequestCorrectlyReceived_ResponsePending; + + // sending a request to the server should return RCRRP within p2 ms + const uint8_t REQUEST[] = {0x31, 0x01, 0x12, 0x34}; + UDSTpSend(mock_client, REQUEST, sizeof(REQUEST), NULL); + + const uint8_t RCRRP[] = {0x7F, 0x31, 0x78}; + + // There should be no response until P2_server has elapsed + EXPECT_IN_APPROX_MS((UDSTpGetRecvLen(mock_client) > 0), srv.p2_ms) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RCRRP, sizeof(RCRRP)); + UDSTpAckRecv(mock_client); + + // The server should again respond within p2_star ms + EXPECT_IN_APPROX_MS((UDSTpGetRecvLen(mock_client) > 0), srv.p2_star_ms * 0.3) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RCRRP, sizeof(RCRRP)); + UDSTpAckRecv(mock_client); + + // and keep responding at intervals of p2_star ms + EXPECT_IN_APPROX_MS((UDSTpGetRecvLen(mock_client) > 0), srv.p2_star_ms * 0.3) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RCRRP, sizeof(RCRRP)); + UDSTpAckRecv(mock_client); + + EXPECT_IN_APPROX_MS((UDSTpGetRecvLen(mock_client) > 0), srv.p2_star_ms * 0.3) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RCRRP, sizeof(RCRRP)); + UDSTpAckRecv(mock_client); + + // When the user func now returns a positive response + resp = kPositiveResponse; + + // the server's next response should be a positive one + // and it should arrive within p2 ms + const uint8_t POSITIVE_RESPONSE[] = {0x71, 0x01, 0x12, 0x34}; + EXPECT_IN_APPROX_MS((UDSTpGetRecvLen(mock_client) > 0), srv.p2_ms) + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), POSITIVE_RESPONSE, + sizeof(POSITIVE_RESPONSE)); +} diff --git a/test/test_server_0x34.c b/test/test_server_0x34.c new file mode 100644 index 0000000..1088913 --- /dev/null +++ b/test/test_server_0x34.c @@ -0,0 +1,48 @@ +#include "test/test.h" + +uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + TEST_INT_EQUAL(ev, UDS_SRV_EVT_RequestDownload); + UDSRequestDownloadArgs_t *r = (UDSRequestDownloadArgs_t *)arg; + TEST_INT_EQUAL(0x11, r->dataFormatIdentifier); + TEST_PTR_EQUAL((void *)0x602000, r->addr); + TEST_INT_EQUAL(0x00FFFF, r->size); + TEST_INT_EQUAL(r->maxNumberOfBlockLength, UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH); + r->maxNumberOfBlockLength = 0x0081; + return kPositiveResponse; +} + +int main() { + { // case 0: No handler + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + + // when no handler function is installed, sending this request to the server + uint8_t REQ[] = {0x34, 0x11, 0x33, 0x60, 0x20, 0x00, 0x00, 0xFF, 0xFF}; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + + // should return a kServiceNotSupported response + uint8_t RESP[] = {0x7F, 0x34, 0x11}; + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) > 0, srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RESP, sizeof(RESP)); + TPMockReset(); + } + + { // case 1: handler installed + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + // when a handler is installed that implements UDS-1:2013 Table 415 + srv.fn = fn; + + // sending this request to the server + uint8_t REQ[] = {0x34, 0x11, 0x33, 0x60, 0x20, 0x00, 0x00, 0xFF, 0xFF}; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + + // should receive a positive response matching UDS-1:2013 Table 415 + uint8_t RESP[] = {0x74, 0x20, 0x00, 0x81}; + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(mock_client) > 0, srv.p2_ms); + TEST_MEMORY_EQUAL(UDSTpGetRecvBuf(mock_client, NULL), RESP, sizeof(RESP)); + TPMockReset(); + } +} diff --git a/test/test_server_0x3E_suppress_positive_response.c b/test/test_server_0x3E_suppress_positive_response.c new file mode 100644 index 0000000..67dfa65 --- /dev/null +++ b/test/test_server_0x3E_suppress_positive_response.c @@ -0,0 +1,20 @@ +#include "test/test.h" + +static uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + return kPositiveResponse; +} + +int main() { + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + srv.fn = fn; + + // when the suppressPositiveResponse bit is set + const uint8_t REQ[] = {0x3E, 0x80}; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + + // there should be no response + ENV_RunMillis(5000); + TEST_INT_EQUAL(UDSTpGetRecvLen(mock_client), 0); +} diff --git a/test/test_server_0x83_diagnostic_session_control.c b/test/test_server_0x83_diagnostic_session_control.c new file mode 100644 index 0000000..53bcc2c --- /dev/null +++ b/test/test_server_0x83_diagnostic_session_control.c @@ -0,0 +1,27 @@ +#include "test/test.h" + +static uint8_t ReturnPositiveResponse(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + return kPositiveResponse; +} + +int main() { + UDSTpHandle_t *mock_client = ENV_TpNew("client"); + UDSServer_t srv; + ENV_SERVER_INIT(srv); + srv.fn = ReturnPositiveResponse; + + // the server sessionType after initialization should be kDefaultSession. + TEST_INT_EQUAL(srv.sessionType, kDefaultSession); + + // when a request is sent with the suppressPositiveResponse bit set + const uint8_t REQ[] = {0x10, 0x83}; + UDSTpSend(mock_client, REQ, sizeof(REQ), NULL); + + // even after running for a long time + ENV_RunMillis(5000); + // there should be no response from the server + TEST_INT_EQUAL(UDSTpGetRecvLen(mock_client), 0); + + // however, the server sessionType should have changed + TEST_INT_EQUAL(srv.sessionType, kExtendedDiagnostic); +} \ No newline at end of file diff --git a/test/test_server_session_timeout.c b/test/test_server_session_timeout.c new file mode 100644 index 0000000..38831c3 --- /dev/null +++ b/test/test_server_session_timeout.c @@ -0,0 +1,33 @@ +#include "test/test.h" + +static int call_count = 0; + +uint8_t fn(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { + TEST_INT_EQUAL(UDS_SRV_EVT_SessionTimeout, ev); + call_count++; + return kPositiveResponse; +} + +int main() { + UDSServer_t srv; + + struct { + const char *tag; + uint8_t (*fn)(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg); + uint8_t sessType; + int expectedCallCount; + } p[] = { + {.tag = "no timeout", .fn = fn, .sessType = kDefaultSession, .expectedCallCount = 0}, + {.tag = "timeout", .fn = fn, .sessType = kProgrammingSession, .expectedCallCount = 1}, + {.tag = "no handler", .fn = NULL, .sessType = kProgrammingSession, .expectedCallCount = 0}, + }; + + for (unsigned i = 0; i < sizeof(p) / sizeof(p[0]); i++) { + ENV_SERVER_INIT(srv); + srv.fn = p[i].fn; + srv.sessionType = p[i].sessType; + ENV_RunMillis(5000); + TEST_INT_GE(call_count, p[i].expectedCallCount); + TPMockReset(); + } +} \ No newline at end of file diff --git a/test/test_tp_compliance.c b/test/test_tp_compliance.c new file mode 100644 index 0000000..36bd85a --- /dev/null +++ b/test/test_tp_compliance.c @@ -0,0 +1,107 @@ +#include "test/test.h" + +UDSTpHandle_t *srv = NULL; +UDSTpHandle_t *client = NULL; + +static bool IsISOTP() { + int tp_type = ENV_GetOpts()->tp_type; + return (ENV_TP_TYPE_ISOTP_SOCK == tp_type) || (ENV_TP_TYPE_ISOTPC == tp_type); +} + +int setup(void **state) { + srv = ENV_TpNew("server"); + client = ENV_TpNew("client"); + TPMockLogToStdout(); + return 0; +} + +int teardown(void **state) { + ENV_TpFree(srv); + ENV_TpFree(client); + return 0; +} + +void TestSendRecv(void **state) { + const uint8_t MSG[] = {0x10, 0x02}; + UDSTpSend(client, MSG, sizeof(MSG), NULL); + EXPECT_IN_APPROX_MS(UDSTpGetRecvLen(srv) == sizeof(MSG), 1); + assert_memory_equal(UDSTpGetRecvBuf(srv, NULL), MSG, sizeof(MSG)); +} + +void TestSendRecvFunctional(void **state) { + const uint8_t MSG[] = {0x10, 0x02}; + UDSSDU_t info = {0}; + uint8_t *buf = NULL; + + // When a small functional request is sent + UDSTpSend(client, MSG, sizeof(MSG), &(UDSSDU_t){.A_TA_Type = UDS_A_TA_TYPE_FUNCTIONAL}); + + // the server should receive it quickly + EXPECT_IN_APPROX_MS(UDSTpPeek(srv, &buf, &info) == sizeof(MSG), 1); + + // it should be the same message + assert_memory_equal(buf, MSG, sizeof(MSG)); + + // and the server should know it's a functional request + assert_int_equal(info.A_TA_Type, UDS_A_TA_TYPE_FUNCTIONAL); +} + +void TestISOTPSendLargestSingleFrame(void **state) { + if (!IsISOTP()) { + skip(); + } + const uint8_t MSG[] = {1, 2, 3, 4, 5, 6, 7}; + UDSSDU_t info = {0}; + uint8_t *buf = NULL; + + // When a functional request is sent + ssize_t ret = + UDSTpSend(client, MSG, sizeof(MSG), &(UDSSDU_t){.A_TA_Type = UDS_A_TA_TYPE_FUNCTIONAL}); + + // the server should receive it quickly + EXPECT_IN_APPROX_MS(UDSTpPeek(srv, &buf, &info) == sizeof(MSG), 1); + + // it should be the same message + assert_memory_equal(buf, MSG, sizeof(MSG)); + + // and the server should know it's a functional request + assert_int_equal(info.A_TA_Type, UDS_A_TA_TYPE_FUNCTIONAL); +} + +void TestISOTPSendLargerThanSingleFrameFails(void **state) { + if (!IsISOTP()) { + skip(); + } + const uint8_t MSG[] = {1, 2, 3, 4, 5, 6, 7, 8}; + UDSSDU_t info = {0}; + uint8_t *buf = NULL; + + // When a small functional request is sent + ssize_t ret = + UDSTpSend(client, MSG, sizeof(MSG), &(UDSSDU_t){.A_TA_Type = UDS_A_TA_TYPE_FUNCTIONAL}); + assert_true(ret < 0); +} + +void TestISOTPSendRecvMaxLen(void **state) { + if (!IsISOTP()) { + skip(); + } + uint8_t MSG[4095] = {0}; + MSG[0] = 0x10; + MSG[4094] = 0x02; + + UDSTpSend(client, MSG, sizeof(MSG), NULL); + EXPECT_WITHIN_MS(UDSTpGetRecvLen(srv) == sizeof(MSG), 3000); + assert_memory_equal(UDSTpGetRecvBuf(srv, NULL), MSG, sizeof(MSG)); +} + +int main() { + const struct CMUnitTest tests[] = { + // cmocka_unit_test_setup_teardown(TestSendRecv, setup, teardown), + cmocka_unit_test_setup_teardown(TestSendRecvFunctional, setup, teardown), + cmocka_unit_test_setup_teardown(TestISOTPSendLargestSingleFrame, setup, teardown), + cmocka_unit_test_setup_teardown(TestISOTPSendLargerThanSingleFrameFails, setup, teardown), + cmocka_unit_test_setup_teardown(TestISOTPSendRecvMaxLen, setup, teardown), + }; + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/test_all.sh b/test_all.sh new file mode 100755 index 0000000..5509ece --- /dev/null +++ b/test_all.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# Run all the tests that will run on the host machine +# Note: qemu must be installed, vcan must be set up +# See test/README.md for more information + +bazel test //test:all + +## +## QEMU tests +## + +# ISO-TP linux sockets fail on qemu-arm with the following error: +# ENOPROTOOPT 92 Protocol not available +# however, socketcan linux sockets work fine. +bazel test --config=arm_linux //test:all --test_tag_filters=-isotp_sock + +# It seems socketcan is not supported at all on qemu-ppc +bazel test --config=ppc //test:all --test_tag_filters=-vcan +bazel test --config=ppc64 //test:all --test_tag_filters=-vcan +bazel test --config=ppc64le //test:all --test_tag_filters=-vcan + +# Test that the ultra strict build works +bazel build -s --config=x86_64_clang //test:ultra_strict \ No newline at end of file diff --git a/test_examples.py b/test_examples.py deleted file mode 100755 index 70d7e08..0000000 --- a/test_examples.py +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env python - -import subprocess -import sys - -transports = [ - "TP=ISOTP_SOCKET", - "TP=ISOTP_C", -] - -clients = [ - "./client", - "./examples/client.py", -] - - -def make_examples(tp: str): - subprocess.check_call(["make", "clean"]) - for thing in ["server", "client"]: - subprocess.check_call(" ".join([tp, "make", thing]), shell=True) - - -for tp in transports: - make_examples(tp) - server_proc = subprocess.Popen(["./server"], stdout=sys.stdout) - assert None == server_proc.returncode - for client in clients: - subprocess.check_call([client], shell=True) - server_proc.kill() diff --git a/test_iso14229.c b/test_iso14229.c deleted file mode 100644 index e7308e2..0000000 --- a/test_iso14229.c +++ /dev/null @@ -1,785 +0,0 @@ -#define UDS_TP UDS_TP_CUSTOM -#include -#include -#include -#include -#include -#include -#include "iso14229.h" - -#define _ASSERT_INT_COND(a, b, cond) \ - { \ - int _a = a; \ - int _b = b; \ - if (!((_a)cond(_b))) { \ - printf("%s:%d (%d %s %d)\n", __FILE__, __LINE__, _a, #cond, _b); \ - fflush(stdout); \ - assert(a cond b); \ - } \ - } - -#define ASSERT_INT_LT(a, b) _ASSERT_INT_COND(a, b, <) -#define ASSERT_INT_LE(a, b) _ASSERT_INT_COND(a, b, <=) -#define ASSERT_INT_GE(a, b) _ASSERT_INT_COND(a, b, >=) -#define ASSERT_INT_EQUAL(a, b) _ASSERT_INT_COND(a, b, ==) -#define ASSERT_INT_NE(a, b) _ASSERT_INT_COND(a, b, !=) - -#define ASSERT_PTR_EQUAL(a, b) \ - { \ - const void *_a = a; \ - const void *_b = b; \ - if ((_a) != (_b)) { \ - printf("%s:%d (%p != %p)\n", __FILE__, __LINE__, _a, _b); \ - fflush(stdout); \ - assert(a == b); \ - } \ - } - -#define ASSERT_MEMORY_EQUAL(a, b, len) \ - { \ - const uint8_t *_a = (const uint8_t *)a; \ - const uint8_t *_b = (const uint8_t *)b; \ - if (memcmp(_a, _b, len)) { \ - printf("A:"); \ - for (unsigned int i = 0; i < len; i++) { \ - printf("%02x,", _a[i]); \ - } \ - printf(" (%s)\nB:", #a); \ - for (unsigned int i = 0; i < len; i++) { \ - printf("%02x,", _b[i]); \ - } \ - printf(" (%s)\n", #b); \ - fflush(stdout); \ - assert(0); \ - } \ - } - -#define ANSI_RESET "\033[0m" -#define ANSI_BOLD "\033[1m" -#define ANSI_BRIGHT_GREEN "\033[92m" -#define ANSI_BRIGHT_MAGENTA "\033[95m" - -#define _TEST_SETUP_SILENT() \ - memset(&ctx, 0, sizeof(ctx)); \ - ctx.func_name = __PRETTY_FUNCTION__; \ - ctx.server_tp = (struct MockTransport){.hdl = {.recv = mock_transport_recv, \ - .send = mock_transport_send, \ - .poll = mock_transport_poll}, \ - .tag = "server"}; \ - ctx.client_tp = (struct MockTransport){.hdl = {.recv = mock_transport_recv, \ - .send = mock_transport_send, \ - .poll = mock_transport_poll}, \ - .tag = "client"}; \ - UDSClientInit(&ctx.client, &(UDSClientConfig_t){.tp = &ctx.client_tp.hdl}); \ - UDSServerInit(&ctx.server, &(UDSServerConfig_t){.tp = &ctx.server_tp.hdl}); - -#define TEST_SETUP() \ - _TEST_SETUP_SILENT(); \ - printf("%s\n", ctx.func_name); - -struct hmm { - const char *tag; -}; - -#define TEST_SETUP_PARAMETRIZED(params_list) \ - for (size_t i = 0; i < sizeof(params_list) / sizeof(params_list[0]); i++) { \ - _TEST_SETUP_SILENT(); \ - printf("%s [p:%ld] %s\n", ctx.func_name, i, ((struct hmm *)(&(params_list[i])))->tag); - -#define TEST_TEARDOWN_PARAMETRIZED() \ - printf(ANSI_BOLD "OK [p:%ld]\n" ANSI_RESET, i); \ - } - -#define TEST_TEARDOWN() printf(ANSI_BOLD "OK\n" ANSI_RESET); - -// TODO: parameterize and fuzz this -#define DEFAULT_ISOTP_BUFSIZE (2048U) - -struct MockTransport { - UDSTpHandle_t hdl; - uint8_t recv_buf[DEFAULT_ISOTP_BUFSIZE]; - uint8_t send_buf[DEFAULT_ISOTP_BUFSIZE]; - uint16_t recv_size; - uint16_t send_size; - UDSTpAddr_t recv_ta_type; - UDSTpAddr_t send_ta_type; - UDSTpStatus_t status; - const char *tag; -}; - -static void printhex(const uint8_t *addr, int len) { - for (int i = 0; i < len; i++) { - printf("%02x,", addr[i]); - } - printf("\n"); -} - -#define SERVER_ONLY (0U) -#define CLIENT_ONLY (1U) -#define SERVER_CLIENT (2U) - -static ssize_t mock_transport_recv(UDSTpHandle_t *hdl, void *buf, size_t bufsize, - UDSTpAddr_t *ta_type) { - assert(hdl); - struct MockTransport *tp = (struct MockTransport *)hdl; - size_t size = tp->recv_size; - - if (bufsize < size) { - return -ENOBUFS; - } - - if (size) { - memmove(buf, tp->recv_buf, size); - tp->recv_size = 0; - memset(tp->recv_buf, 0, sizeof(tp->recv_buf)); - printf(ANSI_BRIGHT_MAGENTA "<-%s_tp_recv-%04d- [%02ld] ", tp->tag, UDSMillis(), size); - printhex(buf, size); - printf(ANSI_RESET); - } - return size; -} - -static ssize_t mock_transport_send(UDSTpHandle_t *hdl, const void *buf, size_t count, - UDSTpAddr_t ta_type) { - assert(hdl); - struct MockTransport *tp = (struct MockTransport *)hdl; - printf(ANSI_BRIGHT_GREEN "--%s_tp_send-%04d->[%02ld] ", tp->tag, UDSMillis(), count); - printhex(buf, count); - printf(ANSI_RESET); - assert(count); // why send zero? - memmove(tp->send_buf, buf, count); - tp->send_size = count; - return count; -} - -static UDSTpStatus_t mock_transport_poll(UDSTpHandle_t *hdl) { - assert(hdl); - struct MockTransport *tp = (struct MockTransport *)hdl; - return tp->status; -} - -typedef struct { - UDSServer_t server; - struct MockTransport server_tp; - UDSClient_t client; - struct MockTransport client_tp; - uint32_t time_ms; - uint32_t deadline; - uint32_t call_count; - const char *func_name; -} Ctx_t; - -Ctx_t ctx; - -uint32_t UDSMillis() { return ctx.time_ms; } - -static void poll_ctx(Ctx_t *ctx) { - UDSServerPoll(&ctx->server); - UDSClientPoll(&ctx->client); - ctx->time_ms++; -} - -#define SEND_TO_SERVER(d1, reqType) \ - memmove(&ctx.server_tp.recv_buf, d1, sizeof(d1)); \ - ctx.server_tp.recv_ta_type = reqType; \ - ctx.server_tp.recv_size = sizeof(d1); \ - poll_ctx(&ctx); - -#define ASSERT_CLIENT_SENT(d1, reqType) \ - ASSERT_INT_EQUAL(ctx.client_tp.send_size, sizeof(d1)); \ - ASSERT_MEMORY_EQUAL(ctx.client_tp.send_buf, d1, sizeof(d1)); \ - ASSERT_INT_EQUAL(ctx.client_tp.send_ta_type, reqType); - -// send data to the client -static void send_to_client(const uint8_t *d1, size_t len, UDSTpAddr_t reqType) { - assert(len <= sizeof(ctx.client_tp.recv_buf)); - memmove(&ctx.client_tp.recv_buf, d1, len); - ctx.client_tp.recv_ta_type = reqType; - ctx.client_tp.recv_size = len; - poll_ctx(&ctx); -} - -#define SEND_TO_CLIENT(d1, reqType) send_to_client(d1, sizeof(d1), reqType); - -// expect a server response within a timeout -#define EXPECT_RESPONSE_WITHIN_MILLIS(d1, reqType, timeout_ms) \ - { \ - uint32_t deadline = ctx.time_ms + timeout_ms; \ - while (0 == ctx.server_tp.send_size) { \ - poll_ctx(&ctx); \ - ASSERT_INT_LE(ctx.time_ms, deadline); \ - } \ - ASSERT_INT_EQUAL(ctx.server_tp.send_size, sizeof(d1)); \ - ASSERT_MEMORY_EQUAL(ctx.server_tp.send_buf, d1, sizeof(d1)); \ - ASSERT_INT_EQUAL(ctx.server_tp.send_ta_type, reqType); \ - memset(ctx.server_tp.send_buf, 0, sizeof(ctx.server_tp.send_buf)); \ - ctx.server_tp.send_size = 0; \ - } - -// expect no server response within a timeout -#define EXPECT_NO_RESPONSE_FOR_MILLIS(timeout_ms) \ - { \ - uint32_t deadline = ctx.time_ms + timeout_ms; \ - while (deadline < ctx.time_ms) { \ - poll_ctx(&ctx); \ - } \ - ASSERT_INT_EQUAL(ctx.server_tp.send_size, 0); \ - } - -void testServer0x10DiagSessCtrlIsDisabledByDefault() { - TEST_SETUP(); - const uint8_t REQ[] = {0x10, 0x02}; - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - const uint8_t RESP[] = {0x7f, 0x10, 0x11}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, kTpAddrTypePhysical, 50); - TEST_TEARDOWN(); -} - -void testServer0x10DiagSessCtrlFunctionalRequest() { - TEST_SETUP(); - // sending a diagnostic session control request functional broadcast - const uint8_t REQ[] = {0x10, 0x03}; - SEND_TO_SERVER(REQ, kTpAddrTypeFunctional); - // should receive a physical response - const uint8_t RESP[] = {0x7f, 0x10, 0x11}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, kTpAddrTypePhysical, 50); - TEST_TEARDOWN(); -} - -uint8_t fn1_callCount = 0; -uint8_t fn1(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - switch (ev) { - case UDS_SRV_EVT_EcuReset: - fn1_callCount += 1; - return kPositiveResponse; - default: - ASSERT_INT_EQUAL(UDS_SRV_EVT_DoScheduledReset, ev); - return kPositiveResponse; - } -} - -// Special-case of ECU reset service -// ISO-14229-1 2013 9.3.1: -// on the behaviour of the ECU from the time following the positive response message to the ECU -// reset request: It is recommended that during this time the ECU does not accept any request -// messages and send any response messages. -void testServer0x11DoesNotSendOrReceiveMessagesAfterECUReset() { - TEST_SETUP(); - ctx.server.fn = fn1; - - const uint8_t REQ[] = {0x11, 0x01}; - const uint8_t RESP[] = {0x51, 0x01}; - - // Sending the first ECU reset should result in a response - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, kTpAddrTypePhysical, 50); - // Sending subsequent ECU reset requests should not receive any response - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - EXPECT_NO_RESPONSE_FOR_MILLIS(5000); - - // The ECU reset handler should have been called once. - ASSERT_INT_EQUAL(fn1_callCount, 1); - TEST_TEARDOWN(); -} - -uint8_t fn2(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - ASSERT_INT_EQUAL(UDS_SRV_EVT_ReadDataByIdent, ev); - const uint8_t vin[] = {0x57, 0x30, 0x4C, 0x30, 0x30, 0x30, 0x30, 0x34, 0x33, - 0x4D, 0x42, 0x35, 0x34, 0x31, 0x33, 0x32, 0x36}; - const uint8_t data_0x010A[] = {0xA6, 0x66, 0x07, 0x50, 0x20, 0x1A, - 0x00, 0x63, 0x4A, 0x82, 0x7E}; - const uint8_t data_0x0110[] = {0x8C}; - - UDSRDBIArgs_t *r = (UDSRDBIArgs_t *)arg; - switch (r->dataId) { - case 0xF190: - return r->copy(srv, vin, sizeof(vin)); - case 0x010A: - return r->copy(srv, data_0x010A, sizeof(data_0x010A)); - case 0x0110: - return r->copy(srv, data_0x0110, sizeof(data_0x0110)); - default: - return kRequestOutOfRange; - } - return kPositiveResponse; -} - -void testServer0x22RDBI1() { - TEST_SETUP(); - ctx.server.fn = fn2; - { - uint8_t REQ[] = {0x22, 0xF1, 0x90}; - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - uint8_t RESP[] = {0x62, 0xF1, 0x90, 0x57, 0x30, 0x4C, 0x30, 0x30, 0x30, 0x30, - 0x34, 0x33, 0x4D, 0x42, 0x35, 0x34, 0x31, 0x33, 0x32, 0x36}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, kTpAddrTypePhysical, 50); - } - { - uint8_t REQ[] = {0x22, 0xF1, 0x91}; - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - uint8_t RESP[] = {0x7F, 0x22, 0x31}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, kTpAddrTypePhysical, 50); - } - TEST_TEARDOWN(); -} - -uint8_t fn10(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - ASSERT_INT_EQUAL(ev, UDS_SRV_EVT_ReadMemByAddr); - UDSReadMemByAddrArgs_t *r = (UDSReadMemByAddrArgs_t *)arg; - // 1 2 3 4 5 6 7 8 - ASSERT_PTR_EQUAL(r->memAddr, (void *)0x000055555555f0c8); - ASSERT_INT_EQUAL(r->memSize, 4); - uint8_t FakeData[4] = {0x01, 0x02, 0x03, 0x04}; - return r->copy(srv, FakeData, r->memSize); -} - -void testServer0x23ReadMemoryByAddress() { - TEST_SETUP(); - ctx.server.fn = fn10; - uint8_t REQ[] = {0x23, 0x18, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0xf0, 0xc8, 0x04}; - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - uint8_t RESP[] = {0x63, 0x01, 0x02, 0x03, 0x04}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, kTpAddrTypePhysical, 50); - TEST_TEARDOWN(); -} - -uint8_t fn4(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - switch (ev) { - case UDS_SRV_EVT_SecAccessRequestSeed: { - UDSSecAccessRequestSeedArgs_t *r = (UDSSecAccessRequestSeedArgs_t *)arg; - const uint8_t seed[] = {0x36, 0x57}; - ASSERT_INT_NE(r->level, srv->securityLevel); - return r->copySeed(srv, seed, sizeof(seed)); - break; - } - case UDS_SRV_EVT_SecAccessValidateKey: { - UDSSecAccessValidateKeyArgs_t *r = (UDSSecAccessValidateKeyArgs_t *)arg; - const uint8_t expected_key[] = {0xC9, 0xA9}; - ASSERT_INT_EQUAL(r->len, sizeof(expected_key)); - ASSERT_MEMORY_EQUAL(r->key, expected_key, sizeof(expected_key)); - break; - } - default: - assert(0); - } - return kPositiveResponse; -} - -// UDS-1 2013 9.4.5.2 -// UDS-1 2013 9.4.5.3 -void testServer0x27SecurityAccess() { - TEST_SETUP(); - ctx.server.fn = fn4; - - // the server security level after initialization should be 0 - ASSERT_INT_EQUAL(ctx.server.securityLevel, 0); - - // sending a seed request should get this response - const uint8_t SEED_REQUEST[] = {0x27, 0x01}; - const uint8_t SEED_RESPONSE[] = {0x67, 0x01, 0x36, 0x57}; - SEND_TO_SERVER(SEED_REQUEST, kTpAddrTypePhysical); - EXPECT_RESPONSE_WITHIN_MILLIS(SEED_RESPONSE, kTpAddrTypePhysical, 50); - - // subsequently sending an unlock request should get this response - const uint8_t UNLOCK_REQUEST[] = {0x27, 0x02, 0xC9, 0xA9}; - const uint8_t UNLOCK_RESPONSE[] = {0x67, 0x02}; - SEND_TO_SERVER(UNLOCK_REQUEST, kTpAddrTypePhysical); - EXPECT_RESPONSE_WITHIN_MILLIS(UNLOCK_RESPONSE, kTpAddrTypePhysical, 50); - - // sending the same seed request should now result in the "already unlocked" response - const uint8_t ALREADY_UNLOCKED_RESPONSE[] = {0x67, 0x01, 0x00, 0x00}; - SEND_TO_SERVER(SEED_REQUEST, kTpAddrTypePhysical); - EXPECT_RESPONSE_WITHIN_MILLIS(ALREADY_UNLOCKED_RESPONSE, kTpAddrTypePhysical, 50); - - // Additionally, the security level should now be 1 - ASSERT_INT_EQUAL(ctx.server.securityLevel, 1); - TEST_TEARDOWN(); -} - -static uint8_t ReturnRCRRP(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - return kRequestCorrectlyReceived_ResponsePending; -} -static uint8_t ReturnPositiveResponse(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - return kPositiveResponse; -} - -// ISO-14229-1 2013 Table A.1 Byte Value 0x78: requestCorrectlyReceived-ResponsePending -// "This NRC is in general supported by each diagnostic service". -void testServer0x31RCRRP() { - TEST_SETUP(); - // When a server handler func initially returns RRCRP - ctx.server.fn = ReturnRCRRP; - - // sending a request to the server should return RCRRP - const uint8_t REQUEST[] = {0x31, 0x01, 0x12, 0x34}; - const uint8_t RCRRP[] = {0x7F, 0x31, 0x78}; - SEND_TO_SERVER(REQUEST, kTpAddrTypePhysical); - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, kTpAddrTypePhysical, 50) - - // The server should again respond within p2_star ms, and keep responding - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, kTpAddrTypePhysical, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, kTpAddrTypePhysical, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, kTpAddrTypePhysical, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, kTpAddrTypePhysical, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, kTpAddrTypePhysical, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, kTpAddrTypePhysical, 50) - - // When the server handler func now returns a positive response - ctx.server.fn = ReturnPositiveResponse; - - // the server's next response should be a positive one - const uint8_t POSITIVE_RESPONSE[] = {0x71, 0x01, 0x12, 0x34}; - EXPECT_RESPONSE_WITHIN_MILLIS(POSITIVE_RESPONSE, kTpAddrTypePhysical, 50) - - TEST_TEARDOWN(); -} - -void testServer0x34NotEnabled() { - TEST_SETUP(); - // when no handler function is installed, sending this request to the server - const uint8_t IN[] = {0x34, 0x11, 0x33, 0x60, 0x20, 0x00, 0x00, 0xFF, 0xFF}; - SEND_TO_SERVER(IN, kTpAddrTypePhysical); - - // should return a kServiceNotSupported response - const uint8_t OUT[] = {0x7F, 0x34, 0x11}; - EXPECT_RESPONSE_WITHIN_MILLIS(OUT, kTpAddrTypePhysical, 50); - TEST_TEARDOWN(); -} - -uint8_t fn7(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - ASSERT_INT_EQUAL(ev, UDS_SRV_EVT_RequestDownload); - UDSRequestDownloadArgs_t *r = (UDSRequestDownloadArgs_t *)arg; - ASSERT_INT_EQUAL(0x11, r->dataFormatIdentifier); - ASSERT_PTR_EQUAL((void *)0x602000, r->addr); - ASSERT_INT_EQUAL(0x00FFFF, r->size); - ASSERT_INT_EQUAL(r->maxNumberOfBlockLength, UDS_SERVER_DEFAULT_XFER_DATA_MAX_BLOCKLENGTH); - r->maxNumberOfBlockLength = 0x0081; - return kPositiveResponse; -} - -void testServer0x34() { - TEST_SETUP(); - // when a handler is installed that implements UDS-1:2013 Table 415 - ctx.server.fn = fn7; - - // sending this request to the server - uint8_t REQ[] = {0x34, 0x11, 0x33, 0x60, 0x20, 0x00, 0x00, 0xFF, 0xFF}; - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - - // should receive a positive response matching UDS-1:2013 Table 415 - uint8_t RESP[] = {0x74, 0x20, 0x00, 0x81}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, kTpAddrTypePhysical, 50) - TEST_TEARDOWN(); -} - -/* UDS-1 2013 Table 72 */ -void testServer0x3ESuppressPositiveResponse() { - TEST_SETUP(); - ctx.server.fn = ReturnPositiveResponse; - // when the suppressPositiveResponse bit is set - const uint8_t REQ[] = {0x3E, 0x80}; - // there should be no response - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - EXPECT_NO_RESPONSE_FOR_MILLIS(5000); - TEST_TEARDOWN(); -} - -void testServer0x83DiagnosticSessionControl() { - TEST_SETUP(); - ctx.server.fn = ReturnPositiveResponse; - // the server sessionType after initialization should be kDefaultSession. - ASSERT_INT_EQUAL(ctx.server.sessionType, kDefaultSession); - - // When the suppressPositiveResponse bit is set, there should be no response. - const uint8_t REQ[] = {0x10, 0x83}; - SEND_TO_SERVER(REQ, kTpAddrTypePhysical); - EXPECT_NO_RESPONSE_FOR_MILLIS(5000); - // and the server sessionType should have changed - ASSERT_INT_EQUAL(ctx.server.sessionType, kExtendedDiagnostic); - TEST_TEARDOWN(); -} - -uint8_t fn9(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg) { - ASSERT_INT_EQUAL(UDS_SRV_EVT_SessionTimeout, ev); - ctx.call_count++; - return kPositiveResponse; -} - -void testServerSessionTimeout() { - struct { - const char *tag; - uint8_t (*fn)(UDSServer_t *srv, UDSServerEvent_t ev, const void *arg); - uint8_t sessType; - int expectedCallCount; - } p[] = { - {.tag = "no timeout", .fn = fn9, .sessType = kDefaultSession, .expectedCallCount = 0}, - {.tag = "timeout", .fn = fn9, .sessType = kProgrammingSession, .expectedCallCount = 1}, - {.tag = "no handler", .fn = NULL, .sessType = kProgrammingSession, .expectedCallCount = 0}, - }; - TEST_SETUP_PARAMETRIZED(p); - ctx.server.fn = p[i].fn; - ctx.server.sessionType = p[i].sessType; - while (ctx.time_ms < 5000) - poll_ctx(&ctx); - ASSERT_INT_GE(ctx.call_count, p[i].expectedCallCount); - TEST_TEARDOWN_PARAMETRIZED(); -} - -#define POLL_UNTIL_TIME_MS(abs_time_ms) \ - while (ctx.time_ms < (abs_time_ms)) \ - poll_ctx(&ctx) - -#define POLL_FOR_MS(duration_ms) \ - { \ - uint32_t start_time_ms = ctx.time_ms; \ - while (ctx.time_ms < (start_time_ms + duration_ms)) \ - poll_ctx(&ctx); \ - } - -void testClientP2TimeoutExceeded() { - TEST_SETUP(); - // when sending a request that receives no response - UDSSendECUReset(&ctx.client, kHardReset); - - // before p2 ms has elapsed, the client should have no error - POLL_UNTIL_TIME_MS(UDS_CLIENT_DEFAULT_P2_MS - 10); - ASSERT_INT_EQUAL(UDS_OK, ctx.client.err); - - // after p2 ms has elapsed, the client should have a timeout error - POLL_UNTIL_TIME_MS(UDS_CLIENT_DEFAULT_P2_MS + 10); - ASSERT_INT_EQUAL(UDS_ERR_TIMEOUT, ctx.client.err); - TEST_TEARDOWN(); -} - -void testClientP2TimeoutNotExceeded() { - TEST_SETUP(); - // a client that sends an request - UDSSendECUReset(&ctx.client, kHardReset); - - // which receives a positive response - const uint8_t POSITIVE_RESPONSE[] = {0x51, 0x01}; - SEND_TO_CLIENT(POSITIVE_RESPONSE, kTpAddrTypePhysical); - - POLL_UNTIL_TIME_MS(UDS_CLIENT_DEFAULT_P2_MS + 10); - // should return to the idle state - ASSERT_INT_EQUAL(kRequestStateIdle, ctx.client.state); - // and should have no error. - ASSERT_INT_EQUAL(UDS_OK, ctx.client.err); - TEST_TEARDOWN(); -} - -void testClientSuppressPositiveResponse() { - TEST_SETUP(); - // Setting the suppressPositiveResponse flag before sending a request - ctx.client.options |= UDS_SUPPRESS_POS_RESP; - UDSSendECUReset(&ctx.client, kHardReset); - - // and not receiving a response after approximately p2 ms - POLL_UNTIL_TIME_MS(UDS_CLIENT_DEFAULT_P2_MS + 10); - - // should not result in an error. - ASSERT_INT_EQUAL(UDS_OK, ctx.client.err); - ASSERT_INT_EQUAL(kRequestStateIdle, ctx.client.state); - TEST_TEARDOWN(); -} - -void testClientBusy() { - TEST_SETUP(); - // Sending a request should not return an error - ASSERT_INT_EQUAL(UDS_OK, UDSSendECUReset(&ctx.client, kHardReset)); - - // unless there is an existing unresolved request - ASSERT_INT_EQUAL(UDS_ERR_BUSY, UDSSendECUReset(&ctx.client, kHardReset)); - TEST_TEARDOWN(); -} - -void testClient0x11ECUReset() { - TEST_SETUP(); - const uint8_t GOOD[] = {0x51, 0x01}; - const uint8_t BAD_SID[] = {0x50, 0x01}; - const uint8_t TOO_SHORT[] = {0x51}; - const uint8_t BAD_SUBFUNC[] = {0x51, 0x02}; - const uint8_t NEG[] = {0x7F, 0x11, 0x10}; -#define CASE(d1, opt, err) \ - { \ - .tag = "resp: " #d1 ", expected_err: " #err, .resp = d1, .resp_len = sizeof(d1), \ - .options = opt, .expected_err = err \ - } - struct { - const char *tag; - const uint8_t *resp; - size_t resp_len; - uint8_t options; - UDSErr_t expected_err; - } p[] = { - CASE(GOOD, 0, UDS_OK), - CASE(BAD_SID, 0, UDS_ERR_SID_MISMATCH), - CASE(TOO_SHORT, 0, UDS_ERR_RESP_TOO_SHORT), - CASE(BAD_SUBFUNC, 0, UDS_ERR_SUBFUNCTION_MISMATCH), - CASE(NEG, 0, UDS_OK), - CASE(NEG, UDS_NEG_RESP_IS_ERR, UDS_ERR_NEG_RESP), - }; -#undef CASE - TEST_SETUP_PARAMETRIZED(p); - // sending a request with these options - ctx.client.options = p[i].options; - UDSSendECUReset(&ctx.client, kHardReset); - // that receives this response - send_to_client(p[i].resp, p[i].resp_len, kTpAddrTypePhysical); - // should return to the idle state - POLL_UNTIL_TIME_MS(UDS_CLIENT_DEFAULT_P2_MS + 10); - ASSERT_INT_EQUAL(kRequestStateIdle, ctx.client.state); - // with the expected error. - ASSERT_INT_EQUAL(p[i].expected_err, ctx.client.err); - TEST_TEARDOWN_PARAMETRIZED(); -} - -void testClient0x22RDBITxBufferTooSmall() { - TEST_SETUP(); - - // attempting to send a request payload of 6 bytes - uint16_t didList[] = {0x0001, 0x0002, 0x0003}; - - // which is larger than the underlying buffer - ctx.client.send_buf_size = 4; - - // should return an error - ASSERT_INT_EQUAL(UDS_ERR_INVALID_ARG, - UDSSendRDBI(&ctx.client, didList, sizeof(didList) / sizeof(didList[0]))) - - // and no data should be sent - ASSERT_INT_EQUAL(ctx.client.send_size, 0); - TEST_TEARDOWN(); -} - -void testClient0x22RDBIUnpackResponse() { - TEST_SETUP(); - uint8_t RESPONSE[] = {0x72, 0x12, 0x34, 0x00, 0x00, 0xAA, 0x00, 0x56, 0x78, 0xAA, 0xBB}; - UDSClient_t client; - memmove(client.recv_buf, RESPONSE, sizeof(RESPONSE)); - client.recv_size = sizeof(RESPONSE); - uint8_t buf[4]; - uint16_t offset = 0; - int err = 0; - err = UDSUnpackRDBIResponse(&client, 0x1234, buf, 4, &offset); - ASSERT_INT_EQUAL(err, UDS_OK); - uint32_t d0 = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; - ASSERT_INT_EQUAL(d0, 0x0000AA00); - err = UDSUnpackRDBIResponse(&client, 0x1234, buf, 2, &offset); - ASSERT_INT_EQUAL(err, UDS_ERR_DID_MISMATCH); - err = UDSUnpackRDBIResponse(&client, 0x5678, buf, 20, &offset); - ASSERT_INT_EQUAL(err, UDS_ERR_RESP_TOO_SHORT); - err = UDSUnpackRDBIResponse(&client, 0x5678, buf, 2, &offset); - ASSERT_INT_EQUAL(err, UDS_OK); - uint16_t d1 = (buf[0] << 8) + buf[1]; - ASSERT_INT_EQUAL(d1, 0xAABB); - err = UDSUnpackRDBIResponse(&client, 0x5678, buf, 1, &offset); - ASSERT_INT_EQUAL(err, UDS_ERR_RESP_TOO_SHORT); - ASSERT_INT_EQUAL(offset, sizeof(RESPONSE)); - TEST_TEARDOWN(); -} - -void testClient0x31RCRRP() { - TEST_SETUP(); - - { // Case 1: RCRRP Timeout - // When a request is sent - UDSSendRoutineCtrl(&ctx.client, kStartRoutine, 0x1234, NULL, 0); - - // that receives an RCRRP response - const uint8_t RCRRP[] = {0x7F, 0x31, 0x78}; // RequestCorrectly-ReceievedResponsePending - SEND_TO_CLIENT(RCRRP, kTpAddrTypePhysical); - - // that remains unresolved at a time between p2 ms and p2 star ms - POLL_FOR_MS(UDS_CLIENT_DEFAULT_P2_MS + 10); - // the client should still be pending. - ASSERT_INT_EQUAL(kRequestStateAwaitResponse, ctx.client.state) - - // after p2_star has elapsed, the client should timeout - POLL_FOR_MS(UDS_CLIENT_DEFAULT_P2_STAR_MS + 10); - ASSERT_INT_EQUAL(kRequestStateIdle, ctx.client.state) - ASSERT_INT_EQUAL(ctx.client.err, UDS_ERR_TIMEOUT); - } - - { // Case 2: Positive Response Received - // When a request is sent - UDSSendRoutineCtrl(&ctx.client, kStartRoutine, 0x1234, NULL, 0); - - // that receives an RCRRP response - const uint8_t RCRRP[] = {0x7F, 0x31, 0x78}; // RequestCorrectly-ReceievedResponsePending - SEND_TO_CLIENT(RCRRP, kTpAddrTypePhysical); - - // that remains unresolved at a time between p2 ms and p2 star ms - POLL_FOR_MS(UDS_CLIENT_DEFAULT_P2_MS + 10); - // the client should still be pending. - ASSERT_INT_EQUAL(ctx.client.err, UDS_OK); - ASSERT_INT_EQUAL(kRequestStateAwaitResponse, ctx.client.state) - - // When the client receives a positive response from the server - const uint8_t POSITIVE_RESPONSE[] = {0x71, 0x01, 0x12, 0x34}; - SEND_TO_CLIENT(POSITIVE_RESPONSE, kTpAddrTypePhysical); - - POLL_FOR_MS(5); - - // the client should return to the idle state with no error - ASSERT_INT_EQUAL(kRequestStateIdle, ctx.client.state) - ASSERT_INT_EQUAL(ctx.client.err, UDS_OK); - } - - TEST_TEARDOWN(); -} - -void testClient0x34RequestDownload() { - TEST_SETUP(); - // When RequestDownload is called with these arguments - ASSERT_INT_EQUAL(UDS_OK, UDSSendRequestDownload(&ctx.client, 0x11, 0x33, 0x602000, 0x00FFFF)); - - // the bytes sent should match UDS-1 2013 Table 415 - const uint8_t CORRECT_REQUEST[] = {0x34, 0x11, 0x33, 0x60, 0x20, 0x00, 0x00, 0xFF, 0xFF}; - ASSERT_CLIENT_SENT(CORRECT_REQUEST, kTpAddrTypePhysical); - TEST_TEARDOWN(); -} - -void testClient0x34UDSUnpackRequestDownloadResponse() { - TEST_SETUP(); - struct RequestDownloadResponse resp; - - // When the following raw bytes are received - uint8_t RESPONSE[] = {0x74, 0x20, 0x00, 0x81}; - UDSClient_t client; - memmove(client.recv_buf, RESPONSE, sizeof(RESPONSE)); - client.recv_size = sizeof(RESPONSE); - - UDSErr_t err = UDSUnpackRequestDownloadResponse(&client, &resp); - - // they should unpack without error - ASSERT_INT_EQUAL(err, UDS_OK); - ASSERT_INT_EQUAL(resp.maxNumberOfBlockLength, 0x81); - TEST_TEARDOWN(); -} - -int main() { - testServer0x10DiagSessCtrlIsDisabledByDefault(); - testServer0x10DiagSessCtrlFunctionalRequest(); - testServer0x11DoesNotSendOrReceiveMessagesAfterECUReset(); - testServer0x22RDBI1(); - testServer0x23ReadMemoryByAddress(); - testServer0x27SecurityAccess(); - testServer0x31RCRRP(); - testServer0x34NotEnabled(); - testServer0x34(); - testServer0x3ESuppressPositiveResponse(); - testServer0x83DiagnosticSessionControl(); - testServerSessionTimeout(); - - testClientP2TimeoutExceeded(); - testClientP2TimeoutNotExceeded(); - testClientSuppressPositiveResponse(); - testClientBusy(); - testClient0x11ECUReset(); - testClient0x22RDBITxBufferTooSmall(); - testClient0x22RDBIUnpackResponse(); - testClient0x31RCRRP(); - testClient0x34RequestDownload(); - testClient0x34UDSUnpackRequestDownloadResponse(); -} diff --git a/test_iso14229serverbufferedwriter.c b/test_iso14229serverbufferedwriter.c deleted file mode 100644 index 0058fc1..0000000 --- a/test_iso14229serverbufferedwriter.c +++ /dev/null @@ -1,124 +0,0 @@ -/** - * @file test_bufferedwriter.c - * @brief run with `gcc test_bufferedwriter.c && ./a.out` - */ - -#define BUFFERED_WRITER_ASSERT assert - -#include "iso14229serverbufferedwriter.h" -#include -#include -#include - -#ifndef BUFFEREDWRITER_DEBUG_PRINTF -#define BUFFEREDWRITER_DEBUG_PRINTF(fmt, ...) ((void)fmt) -#endif - -#define LOGICAL_PARTITION_SIZE (8192 * 40) - -uint8_t g_logicalPartition[LOGICAL_PARTITION_SIZE] = {0}; -uint8_t g_buffer[LOGICAL_PARTITION_SIZE] = {0}; - -uint8_t g_mockData[LOGICAL_PARTITION_SIZE] = {0}; - -int writeFunc(void *addr, const uint8_t *data, uint32_t length) { - BUFFEREDWRITER_DEBUG_PRINTF("writeFunc writing at offset %u\n", - addr - (void *)g_logicalPartition); - assert((uint8_t *)addr >= g_logicalPartition); - assert((uint8_t *)addr + length <= g_logicalPartition + sizeof(g_logicalPartition)); - memmove(addr, data, length); - return 0; -} - -static inline void print_diff(int addr) { - int min = addr - 5 < 0 ? 0 : addr - 5; - int max = addr + 5 > LOGICAL_PARTITION_SIZE ? LOGICAL_PARTITION_SIZE : addr + 5; - for (int i = min; i < max; i++) { - if (g_logicalPartition[i] != g_mockData[i]) { - printf(">"); - } else { - printf(" "); - } - printf("%d,%d,%d\n", i, g_logicalPartition[i], g_mockData[i]); - } -} - -void run_test(size_t pageBufferSize, size_t chunkSize) { - printf("pageBufferSize: %05x, chunkSize: %05x\n", pageBufferSize, chunkSize); - memset(g_logicalPartition, 0, sizeof(g_logicalPartition)); - BufferedWriter bw = {0}; - BufferedWriterConfig cfg = { - .writeFunc = writeFunc, - .logicalPartitionStartAddr = g_logicalPartition, - .logicalPartitionSize = sizeof(g_logicalPartition), - .pageBufferSize = pageBufferSize, - .pageBuffer = g_buffer, - }; - - bufferedWriterInit(&bw, &cfg); - - int n_chunks = LOGICAL_PARTITION_SIZE / chunkSize; - size_t remainder = LOGICAL_PARTITION_SIZE % chunkSize; - - // transferData - for (int chunk = 0; chunk <= n_chunks; chunk++) { - bool writePending = false; - uint32_t size = chunk == n_chunks ? remainder : chunkSize; - do { - writePending = BufferedWriterProcess(&bw, g_mockData + (chunk * chunkSize), size); - if (writePending) { - // return 0x78 and call later with the same buffer - } else { - // return 0x01, no writePending - } - } while (writePending); - } - - // onTransferExit - bool writePending = false; - do { - writePending = BufferedWriterProcess(&bw, NULL, 0); - if (writePending) { - // return 0x78 and call later with the same buffer - } else { - // return 0x01, no writePending - } - } while (writePending); - - if (0 != memcmp(g_logicalPartition, g_mockData, LOGICAL_PARTITION_SIZE)) { - printf("FAIL:\n"); - printf("idx,dst,src\n"); - for (int j = 0; j < LOGICAL_PARTITION_SIZE; j++) { - if (g_logicalPartition[j] != g_mockData[j]) { - print_diff(j); - break; - // printf("%d,%d,%d\n", j, g_logicalPartition[j], g_mockData[j]); - } - } - fflush(stdout); - assert(0); - } -} - -void setup() { - for (size_t i = 0; i < LOGICAL_PARTITION_SIZE; i++) { - // something consistent but not too repetitive - g_mockData[i] = (i & 0xff) + (i >> 8); - } -} - -int main(int ac, char **av) { - setup(); - run_test(2048, 1); - run_test(2048, 1000); - run_test(2048, 10000); - run_test(2048, 2047); - run_test(2048, 2048); - run_test(2048, 2049); - run_test(8192, 1); - run_test(8192, 8191); - run_test(8192, 8192); - run_test(8192, 8193); - run_test(8192, 10000); - printf("pass\n"); -} \ No newline at end of file diff --git a/test_qemu.py b/test_qemu.py deleted file mode 100755 index 4cd5669..0000000 --- a/test_qemu.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python - -import subprocess - -architectures = [ - "CC=arm-linux-gnueabihf-gcc RUN=qemu-arm LDFLAGS=-L/usr/arm-linux-gnueabihf/ CFLAGS=-static", - "CC=powerpc-linux-gnu-gcc RUN=qemu-ppc LDFLAGS=-L/usr/powerpc-linux-gnu/ CFLAGS=-static", - "CC=powerpc64-linux-gnu-gcc RUN=qemu-ppc64 LDFLAGS=-L/usr/powerpc64-linux-gnu/ CFLAGS=-static", -] - -for arch in architectures: - subprocess.check_call("make clean".split(" "), shell=True) - args = f"{arch} make unit_test".split(" ") - ret = subprocess.call(args, shell=True) - if ret: - exit(ret) diff --git a/toolchain/BUILD b/toolchain/BUILD new file mode 100644 index 0000000..7bd8525 --- /dev/null +++ b/toolchain/BUILD @@ -0,0 +1,64 @@ +load(":gcc_family.bzl", "gcc_toolchain") +load(":clang.bzl", "clang_toolchain") + +filegroup( + name = "empty", + srcs = [], +) + +gcc_toolchain( + name="arm_linux_gcc", + prefix="arm-linux-gnueabihf", + target_compatible_with = [ + "@platforms//cpu:arm", + "@platforms//os:linux", + ], +) + +gcc_toolchain( + name="arm_none_gcc", + prefix="arm-none-eabi", + target_compatible_with = [ + "@platforms//cpu:arm", + "@platforms//os:none", + ], + include_dirs = [ + "/usr/lib/gcc/arm-none-eabi/10.3.1/include/", + ], + linkopts = [ + "-Wl,--gc-sections", + ], + copts = [ + "-g", + "-gdwarf-2", + ] +) + +gcc_toolchain( + name="ppc_gcc", + prefix="powerpc-linux-gnu", + target_compatible_with = [ + "@platforms//cpu:ppc", + "@platforms//os:linux", + ] +) + +gcc_toolchain( + name="ppc64_gcc", + prefix="powerpc64-linux-gnu", + target_compatible_with = [ + "//platforms/cpu:ppc64", + "@platforms//os:linux", + ] +) + +gcc_toolchain( + name="ppc64le_gcc", + prefix="powerpc64le-linux-gnu", + target_compatible_with = [ + "//platforms/cpu:ppc64le", + "@platforms//os:linux", + ] +) + +clang_toolchain(name="x86_64_clang") \ No newline at end of file diff --git a/toolchain/clang.bzl b/toolchain/clang.bzl new file mode 100644 index 0000000..bfd9d3e --- /dev/null +++ b/toolchain/clang.bzl @@ -0,0 +1,147 @@ +load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") +load( + "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", + "feature", + "flag_group", + "flag_set", + "tool_path", +) + +all_link_actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, +] + +def _impl(ctx): + CLANG = "clang-15" + tool_paths = [ + tool_path( + name = "gcc", + path = "/usr/bin/" + CLANG, + ), + tool_path( + name = "ld", + path = "/usr/bin/ld", + ), + tool_path( + name = "ar", + path = "/usr/bin/ar", + ), + tool_path( + name = "cpp", + path = "/bin/false", + ), + tool_path( + name = "gcov", + path = "/bin/false", + ), + tool_path( + name = "nm", + path = "/bin/false", + ), + tool_path( + name = "objdump", + path = "/bin/false", + ), + tool_path( + name = "strip", + path = "/bin/false", + ), + ] + + features = [ + feature( + name = "default_linker_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = ([ + flag_group( + flags = [ + "-stdlib=libstdc++", + ], + ), + ]), + ), + ], + ), + # feature( + # name = "default_compiler_flags", + # enabled = True, + # flag_sets = [ + # flag_set( + # actions = [ACTION_NAMES.cpp_compile], + # flag_groups = ([ + # flag_group( + # flags = [ + # "-stdlib=libstdc++", + # ], + # ), + # ]), + # ), + # ], + # ), + ] + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + cxx_builtin_include_directories = [ + # "/usr/{}/include".format(ctx.attr.prefix), + "/usr/lib/llvm-15/lib/clang/15.0.7/include", + "/usr/lib/llvm-15/lib/clang/15.0.7/share", + "/usr/include", + "/usr/lib/x86_64-linux-gnu/", + ], + toolchain_identifier = "local", + host_system_name = "local", + target_system_name = "local", + target_cpu = "k8", + target_libc = "unknown", + compiler = CLANG, + abi_version = "unknown", + abi_libc_version = "unknown", + tool_paths = tool_paths, + ) + +clang_toolchain_config = rule( + implementation = _impl, + provides = [CcToolchainConfigInfo], +) + +def clang_toolchain(name): + CONFIG_NAME = name + "_config" + CC_TOOLCHAIN_NAME = name + "_cc_toolchain" + + clang_toolchain_config( + name = CONFIG_NAME, + ) + + native.cc_toolchain( + name = CC_TOOLCHAIN_NAME, + toolchain_identifier = "k8-toolchain", + toolchain_config = CONFIG_NAME, + all_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, + ) + + native.toolchain( + name = name, + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + target_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + toolchain = CC_TOOLCHAIN_NAME, + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", + ) diff --git a/toolchain/gcc_family.bzl b/toolchain/gcc_family.bzl new file mode 100644 index 0000000..c22ec56 --- /dev/null +++ b/toolchain/gcc_family.bzl @@ -0,0 +1,188 @@ +""" gcc toolchains """ + +load("@bazel_tools//tools/build_defs/cc:action_names.bzl", "ACTION_NAMES") +load( + "@bazel_tools//tools/cpp:cc_toolchain_config_lib.bzl", + "tool_path", + "flag_group", + "flag_set", + "feature", +) + +all_link_actions = [ + ACTION_NAMES.cpp_link_executable, + ACTION_NAMES.cpp_link_dynamic_library, + ACTION_NAMES.cpp_link_nodeps_dynamic_library, +] + +def _impl(ctx): + tool_paths = [ + tool_path( + name = "gcc", + path = "/usr/bin/{}-gcc".format(ctx.attr.prefix), + ), + tool_path( + name = "ld", + path = "/usr/bin/{}-ld".format(ctx.attr.prefix), + ), + tool_path( + name = "ar", + path = "/usr/bin/{}-ar".format(ctx.attr.prefix), + ), + tool_path( + name = "cpp", + path = "/bin/false", + ), + tool_path( + name = "gcov", + path = "/bin/false", + ), + tool_path( + name = "nm", + path = "/bin/false", + ), + tool_path( + name = "objdump", + path = "/bin/false", + ), + tool_path( + name = "strip", + path = "/bin/false", + ), + tool_path( + name = "objcopy", + path = "/usr/bin/{}-objcopy".format(ctx.attr.prefix), + ), + ] + + linker_flags = ctx.attr.linkopts + ctx.attr.arch_flags + compiler_flags = ctx.attr.copts + ctx.attr.arch_flags + + features = [] + + if len(linker_flags) > 0: + features.append( + feature( + name = "linker_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = all_link_actions, + flag_groups = [ + flag_group( + flags = linker_flags, + ), + ], + ), + ], + ) + ) + + if len(compiler_flags) > 0: + features.append( + feature( + name = "compiler_flags", + enabled = True, + flag_sets = [ + flag_set( + actions = [ + ACTION_NAMES.c_compile, + ACTION_NAMES.cpp_compile, + ], + flag_groups = [ + flag_group( + flags = compiler_flags, + ), + ], + ), + ], + ) + ) + + + return cc_common.create_cc_toolchain_config_info( + ctx = ctx, + features = features, + cxx_builtin_include_directories = [ + "/usr/{}/include".format(ctx.attr.prefix), + "/usr/lib/gcc-cross/{}/11/include".format(ctx.attr.prefix), + "/usr/include", + ] + ctx.attr.include_dirs, + toolchain_identifier = "local", + host_system_name = "local", + target_system_name = "local", + target_cpu = "k8", + target_libc = "unknown", + compiler = ctx.attr.prefix + "gcc", + abi_version = "unknown", + abi_libc_version = "unknown", + tool_paths = tool_paths, + ) + +gcc_toolchain_config = rule( + implementation = _impl, + attrs = { + "prefix": attr.string(mandatory=True, doc="Prefix of compiler suite"), + "include_dirs": attr.string_list(mandatory=False, doc="List of include dirs"), + "arch_flags": attr.string_list(mandatory=False, doc="List of arch flags"), + "linkopts": attr.string_list(mandatory=False, doc="List of additional linker options"), + "copts": attr.string_list(mandatory=False, doc="List of additional compiler options"), + }, + provides = [CcToolchainConfigInfo], +) + +def gcc_toolchain(name, prefix, target_compatible_with, include_dirs= [], linkopts = [], copts = []): + """Create a gcc toolchain + + Args: + name: Name of the toolchain + prefix: Prefix of the compiler suite + target_compatible_with: List of targets that this toolchain is compatible with + include_dirs: List of additional include directories + linkopts: List of additional linker options + copts: List of additional compiler options + """ + CONFIG_NAME = name + "_config" + CC_TOOLCHAIN_NAME = name + "_cc_toolchain" + + gcc_toolchain_config( + name = CONFIG_NAME, + prefix = prefix, + include_dirs = include_dirs, + arch_flags = select({ + "//platforms:s32k" : [ + "-mcpu=cortex-m4", + "-mthumb", + "-mfloat-abi=hard", + "-mfpu=fpv4-sp-d16", + ], + "//conditions:default" : [ + ], + }), + linkopts = linkopts, + copts = copts, + ) + + native.cc_toolchain( + name = CC_TOOLCHAIN_NAME, + toolchain_identifier = "k8-toolchain", + toolchain_config = CONFIG_NAME, + all_files = ":empty", + compiler_files = ":empty", + dwp_files = ":empty", + linker_files = ":empty", + objcopy_files = ":empty", + strip_files = ":empty", + supports_param_files = 0, + ) + + native.toolchain( + name = name, + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + target_compatible_with = target_compatible_with, + toolchain = CC_TOOLCHAIN_NAME, + toolchain_type = "@bazel_tools//tools/cpp:toolchain_type", + ) \ No newline at end of file diff --git a/toolchain/objcopy.bzl b/toolchain/objcopy.bzl new file mode 100644 index 0000000..4466110 --- /dev/null +++ b/toolchain/objcopy.bzl @@ -0,0 +1,59 @@ +""" objcopy rule """ +load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain") + +def _objcopy_impl(ctx): + cc_toolchain = find_cc_toolchain(ctx) + outfile = ctx.outputs.out + + ctx.actions.run_shell( + outputs = [outfile], + inputs = depset( + direct = [ctx.file.src], + transitive = [ + cc_toolchain.all_files, + ], + ), + command = "{objcopy} {args} {src} {dst}".format( + objcopy = cc_toolchain.objcopy_executable, + args = ctx.attr.args, + src = ctx.file.src.path, + dst = ctx.outputs.out.path, + ), + ) + + return [ + DefaultInfo( + files = depset([outfile]), + ), + ] + +_objcopy = rule( + implementation = _objcopy_impl, + attrs = { + # Source file + "src": attr.label(allow_single_file = True, mandatory = True), + # Target file + "out": attr.output(mandatory = True), + # Arguments + "args": attr.string(mandatory = True), + }, + executable = False, + toolchains = ["@bazel_tools//tools/cpp:toolchain_type"], + fragments = ["cpp"], +) + +def objcopy(src, out, args, name = ""): + """ + objcopy rule + + src: source file + out: target file + args: arguments + name: rule name + """ + _objcopy( + name = name or "objcopy" + out, + src = src, + out = out, + args = args, + ) \ No newline at end of file