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
-
-
-
-
-
-
-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