From abb4dbf2f97ac2229c03bec6a8e27d09f098b3c5 Mon Sep 17 00:00:00 2001 From: Nick James Kirkby <20824939+driftregion@users.noreply.github.com> Date: Sat, 9 Dec 2023 20:11:48 -0700 Subject: [PATCH] restructure source directory --- .gitignore | 5 +- .gitmodules | 2 +- BUILD | 65 +- README.md | 58 +- README_zh.md | 146 -- examples/BUILD | 0 examples/arduino_server/README.md | 7 + examples/arduino_server/main/iso14229.c | 1 + examples/arduino_server/main/iso14229.h | 1 + examples/arduino_server/main/main.ino | 107 + examples/client_multiserver/BUILD | 15 + examples/client_multiserver/main.c | 59 + examples/client_multiserver/server1.c | 1 + examples/client_multiserver/server2.c | 0 examples/esp32_server/CMakeLists.txt | 6 + examples/esp32_server/README.md | 9 + examples/esp32_server/main/CMakeLists.txt | 6 + examples/esp32_server/main/iso14229.c | 1 + examples/esp32_server/main/iso14229.h | 1 + examples/esp32_server/main/main.c | 113 + examples/s32k144/BUILD | 5 +- examples/server_minimal/BUILD | 10 + examples/server_minimal/main.c | 70 + iso14229.c | 1956 ----------------- iso14229.h | 716 ------ run_gdb.py | 53 + src/client.c | 856 ++++++++ src/client.h | 144 ++ src/config.h | 66 + .../iso14229serverbufferedwriter.h | 0 src/server.c | 944 ++++++++ src/server.h | 169 ++ src/sys.h | 30 + src/sys_arduino.h | 16 + src/sys_esp32.h | 14 + src/sys_unix.h | 12 + src/sys_win32.h | 8 + src/tp.c | 64 + src/tp.h | 128 ++ src/tp/README.md | 66 + {tp => src/tp}/isotp-c | 0 {tp => src/tp}/isotp_c.c | 26 +- {tp => src/tp}/isotp_c.h | 19 +- {tp => src/tp}/isotp_c_socketcan.c | 34 +- {tp => src/tp}/isotp_c_socketcan.h | 7 +- {tp => src/tp}/isotp_sock.c | 35 +- {tp => src/tp}/isotp_sock.h | 4 +- {tp => src/tp}/mock.c | 26 +- {tp => src/tp}/mock.h | 14 +- src/uds.h | 223 ++ src/util.c | 30 + src/util.h | 42 + test/BUILD | 16 +- test/env.c | 3 - test/test.h | 1 - test/test_fuzz_server.c | 4 +- test_iso14229.c | 875 -------- tp/BUILD | 63 - 58 files changed, 3445 insertions(+), 3907 deletions(-) create mode 100644 examples/BUILD create mode 100644 examples/arduino_server/README.md create mode 120000 examples/arduino_server/main/iso14229.c create mode 120000 examples/arduino_server/main/iso14229.h create mode 100644 examples/arduino_server/main/main.ino create mode 100644 examples/client_multiserver/BUILD create mode 100644 examples/client_multiserver/main.c create mode 100644 examples/client_multiserver/server1.c create mode 100644 examples/client_multiserver/server2.c create mode 100644 examples/esp32_server/CMakeLists.txt create mode 100644 examples/esp32_server/README.md create mode 100644 examples/esp32_server/main/CMakeLists.txt create mode 120000 examples/esp32_server/main/iso14229.c create mode 120000 examples/esp32_server/main/iso14229.h create mode 100644 examples/esp32_server/main/main.c create mode 100644 examples/server_minimal/BUILD create mode 100644 examples/server_minimal/main.c delete mode 100644 iso14229.c delete mode 100644 iso14229.h create mode 100755 run_gdb.py create mode 100644 src/client.c create mode 100644 src/client.h create mode 100644 src/config.h rename iso14229serverbufferedwriter.h => src/iso14229serverbufferedwriter.h (100%) create mode 100644 src/server.c create mode 100644 src/server.h create mode 100644 src/sys.h create mode 100644 src/sys_arduino.h create mode 100644 src/sys_esp32.h create mode 100644 src/sys_unix.h create mode 100644 src/sys_win32.h create mode 100644 src/tp.c create mode 100644 src/tp.h create mode 100644 src/tp/README.md rename {tp => src/tp}/isotp-c (100%) rename {tp => src/tp}/isotp_c.c (91%) rename {tp => src/tp}/isotp_c.h (76%) rename {tp => src/tp}/isotp_c_socketcan.c (87%) rename {tp => src/tp}/isotp_c_socketcan.h (90%) rename {tp => src/tp}/isotp_sock.c (88%) rename {tp => src/tp}/isotp_sock.h (93%) rename {tp => src/tp}/mock.c (89%) rename {tp => src/tp}/mock.h (94%) create mode 100644 src/uds.h create mode 100644 src/util.c create mode 100644 src/util.h delete mode 100644 test_iso14229.c delete mode 100644 tp/BUILD diff --git a/.gitignore b/.gitignore index 2b53068..b0a5de7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,7 @@ corpus .gdb_history fuzzer *.o -.vscode \ No newline at end of file +.vscode +**/*.cache +**/build +**/sdkconfig* diff --git a/.gitmodules b/.gitmodules index 0a9e643..d7b4425 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "tp/isotp-c"] - path = tp/isotp-c + path = src/tp/isotp-c url = https://github.com/driftregion/isotp-c.git diff --git a/BUILD b/BUILD index d4db888..869aba6 100644 --- a/BUILD +++ b/BUILD @@ -1,37 +1,52 @@ package(default_visibility = ["//visibility:public"]) load("@hedron_compile_commands//:refresh_compile_commands.bzl", "refresh_compile_commands") +genrule( + name = "isotp_c_wrapped_c", + srcs = glob(["src/tp/isotp-c/*.c"]), + outs = ["isotp_c_wrapped.c"], + cmd = "echo '#if defined(UDS_TP) && UDS_TP==UDS_TP_ISOTP_C' >> $(OUTS) ; for f in $(SRCS); do cat $$f >> $(OUTS); done ; echo '#endif' >> $(OUTS)", +) + +genrule( + name = "isotp_c_wrapped_h", + srcs = glob(["src/tp/isotp-c/*.h"]), + outs = ["isotp_c_wrapped.h"], + cmd = "echo '#if defined(UDS_TP) && UDS_TP==UDS_TP_ISOTP_C' >> $(OUTS) ; for f in $(SRCS); do cat $$f >> $(OUTS); done ; echo '#endif' >> $(OUTS)", +) + +genrule( + name = "c_src", + srcs = glob(["src/*.c", "src/tp/*.c"]) + [ + ":isotp_c_wrapped_c", + ], + outs = ["iso14229.c"], + cmd = "(echo '#include \"iso14229.h\"'; (for f in $(SRCS); do echo; echo '#ifdef UDS_LINES'; echo \"#line 1 \\\"$$f\"\\\"; echo '#endif'; cat $$f | sed -e 's,#include \".*,,'; done)) > $(OUTS)", +) + +genrule( + name = "h_src", + srcs = glob(["src/*.h", "src/tp/*.h"]) + [ + ":isotp_c_wrapped_h", + ], + outs = ["iso14229.h"], + cmd = "echo $(SRCS); (cat ; echo '#ifndef ISO14229_H'; echo '#define ISO14229_H'; echo; echo '#ifdef __cplusplus'; echo 'extern \"C\" {'; echo '#endif'; cat 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 $(location //:isotp_c_wrapped_h) src/tp/*.h |sed -e 's,#include \".*,,' -e 's,^#pragma once,,' ; echo '#endif'; echo '#ifdef __cplusplus'; echo '}'; echo '#endif';) > $(OUTS)", +) filegroup( - name = "iso14229_srcs", + name = "srcs", srcs = [ "iso14229.c", "iso14229.h", - "iso14229serverbufferedwriter.h", ], ) -cc_test( - name="test_all", - srcs=[ - ":iso14229_srcs", - "test_iso14229.c", - ], - deps = [ - "//tp:mock", - ], - 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", ], - size = "small", ) cc_test( @@ -43,9 +58,11 @@ cc_test( size = "small", ) + cc_library( - name="iso14229", - srcs=[":iso14229_srcs"], + name="iso14229_2", + srcs=glob(["src/**/*.c", "src/**/*.h"]), + copts=['-Isrc'], ) diff --git a/README.md b/README.md index 7448ec8..187ebe5 100644 --- a/README.md +++ b/README.md @@ -9,57 +9,37 @@ 简体中文

-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. +iso14229 is an implementation of UDS (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: **stabilizing** +API status: **not yet stable**. -## quickstart: server +## Features -```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; - } - } -} +- static memory allocation. does not use `malloc`, `calloc` +- highly portable. tested on arm, x86-64, ppc, ppc64 +- easy to integrate. Download `iso14229.c` and `iso14229.h` from the releases page and copy into your source tree. +- supports: + - linux + - Windows + - esp32 + - Arduino + - NXP s32k +- cares about security + - server has fuzz test, see [test/README.md](test/README.md) + - -int main() { - UDSServer_t server; - UDSServerConfig_t cfg = { - .fn = &fn, - }; - UDSServerInit(&server, &cfg); - for (;;) { - UDSServerPoll(&server); - } -} -``` +## Quick Start -## quickstart: client - -```c -// see examples/client.c -``` +See [examples](./examples). ## Preprocessor Defines | Define | Description | Valid values | | - | - | - | -| `UDS_ARCH` | Select a porting target | `UDS_ARCH_CUSTOM`, `UDS_ARCH_UNIX` | +| `UDS_SYS` | Select a porting target | `UDS_SYS_CUSTOM`, `UDS_SYS_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 | -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 ## supported functions (server and client ) @@ -362,9 +342,9 @@ MIT ## 0.7.0 - test refactoring. theme: test invariance across different transports and processor architectures - breaking API changes: - - rename `phys_send_id`, `phys_recv_id`, `func_send_id`, and `func_recv_id` to be consistent with the standard. Now mandatory for all UDSServerConfig_t and UDSClientConfig_t regardless of transport layer implementation - overhauled transport layer implementation - simplified client and server init + - `UDS_ARCH_` renamed to `UDS_SYS_` ## 0.6.0 - breaking API changes: diff --git a/README_zh.md b/README_zh.md index 4f10b80..8a0b02b 100644 --- a/README_zh.md +++ b/README_zh.md @@ -407,149 +407,3 @@ MIT - 初次发布 --- - - -# 开发者文档 - -## 客户端请求状态机 - -```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/examples/BUILD b/examples/BUILD new file mode 100644 index 0000000..e69de29 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_multiserver/BUILD b/examples/client_multiserver/BUILD new file mode 100644 index 0000000..4e5b9b7 --- /dev/null +++ b/examples/client_multiserver/BUILD @@ -0,0 +1,15 @@ +# cc_binary( +# name = "client_multiserver", +# srcs = [ +# "main.c", +# "server1.c", +# "server2.c", +# ], +# deps = [ +# "//:iso14229", +# "//:tp_mock", +# ], +# defines = [ +# "UDS_TP=UDS_TP_CUSTOM", +# ] +# ) \ No newline at end of file diff --git a/examples/client_multiserver/main.c b/examples/client_multiserver/main.c new file mode 100644 index 0000000..f859906 --- /dev/null +++ b/examples/client_multiserver/main.c @@ -0,0 +1,59 @@ +#include "iso14229.h" +#include "tp_mock.h" + +typedef struct { + bool Done; + int NumResponses; + int Step; +} Ctx_t; + +void fn(UDSClient_t *client, UDSEvent_t evt, void *evtdata, void *fndata) { + Ctx_t *ctx = (Ctx_t *)fndata; + switch (evt) { + case UDS_EVT_IDLE: + switch (ctx->Step) { + case 0: + client->options |= UDS_FUNCTIONAL; + uint16_t did_list[] = {0x1, 0x8}; + UDSSendRDBI(client, did_list, sizeof(did_list) / sizeof(did_list[0])); + ctx->Step++; + break; + default: + break; + } + break; + case UDS_EVT_RESP_RECV: + switch (ctx->Step) { + case 1: + ctx->NumResponses++; + // int err = UDSUnpackRDBIResponse(client, evtdata); + ctx->Step++; + break; + default: + break; + } + break; + } +} + +int main() { + UDSClient_t client; + Ctx_t ctx = {0}; + UDSServer_t server1, server2, server3; + + { + UDSClientConfig_t cfg = { + .tp = TPMockNew(&(TPMockCfg_t){.phys_recv_addr = 0x1, + .phys_send_addr = 0x2, + .func_recv_addr = 0x3, + .func_send_addr = 0x4}); + } + UDSClientInit(&client, &cfg); +} + +while (!ctx.Done) { + UDSClientPoll2(&client, fn, &ctx); + UDSServerPoll(&server1); + UDSServerPoll(&server2); +} +} \ No newline at end of file diff --git a/examples/client_multiserver/server1.c b/examples/client_multiserver/server1.c new file mode 100644 index 0000000..5a56d0d --- /dev/null +++ b/examples/client_multiserver/server1.c @@ -0,0 +1 @@ +#include "iso14229.h" diff --git a/examples/client_multiserver/server2.c b/examples/client_multiserver/server2.c new file mode 100644 index 0000000..e69de29 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/s32k144/BUILD b/examples/s32k144/BUILD index 231b399..a407dd5 100644 --- a/examples/s32k144/BUILD +++ b/examples/s32k144/BUILD @@ -6,10 +6,7 @@ cc_binary( "main.c", "bsp.c", "bsp.h", - "//:iso14229_srcs", - "//tp:isotp_c_srcs", - "//tp:isotp_c.c", - "//tp:isotp_c.h", + "//:srcs", ], deps = [ ":runtime", diff --git a/examples/server_minimal/BUILD b/examples/server_minimal/BUILD new file mode 100644 index 0000000..4b2bb79 --- /dev/null +++ b/examples/server_minimal/BUILD @@ -0,0 +1,10 @@ +# cc_binary( +# name = "server_minimal", +# srcs = [ +# "main.c", +# "//:iso14229_srcs", +# ], +# deps = [ +# "//:tp_mock", +# ] +# ) \ No newline at end of file diff --git a/examples/server_minimal/main.c b/examples/server_minimal/main.c new file mode 100644 index 0000000..afc63a5 --- /dev/null +++ b/examples/server_minimal/main.c @@ -0,0 +1,70 @@ +#include "iso14229.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static UDSServer_t srv; +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 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) { + UDSServerConfig_t cfg = { + .fn = fn, +#if UDS_TP == UDS_TP_ISOTP_SOCKET + .if_name = "vcan0", + .source_addr = 0x7E0, + .target_addr = 0x7E8, + .source_addr_func = 0x7DF, +#endif + }; + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigint_handler; + sigaction(SIGINT, &sa, NULL); + + if (UDSServerInit(&srv, &cfg)) { + exit(-1); + } + + printf("server up, polling . . .\n"); + while (!done) { + UDSServerPoll(&srv); +#if UDS_TP == UDS_TP_ISOTP_C + SocketCANRecv((UDSTpISOTpC_t *)srv.tp, cfg.source_addr); +#endif + SleepMillis(1); + } + printf("server exiting\n"); + UDSServerDeInit(&srv); + return 0; +} diff --git a/iso14229.c b/iso14229.c deleted file mode 100644 index 1bc1f5c..0000000 --- a/iso14229.c +++ /dev/null @@ -1,1956 +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 -// ======================================================================== - -/** - * @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); -} - -// ======================================================================== -// Common -// ======================================================================== - -#if UDS_CUSTOM_MILLIS -#else -uint32_t UDSMillis(void) { -#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 -// ======================================================================== - -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); - long long unsigned int 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); - } - - 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) { - 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; - 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); - } - - UDSTpStatus_t tpStatus = 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: %ld\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 %ld\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->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; - } - if (r->recv_len > 0) { - uint8_t response = evaluateServiceResponse(srv, r); - srv->requestInProgress = true; - if (kRequestCorrectlyReceived_ResponsePending == response) { - srv->RCRRP = true; - } - } - } -} - -// ======================================================================== -// Client -// ======================================================================== - -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: %u, p2*: %u\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: %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: { - 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 %ld 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/iso14229.h b/iso14229.h deleted file mode 100644 index becaa79..0000000 --- a/iso14229.h +++ /dev/null @@ -1,716 +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 // bring your own transport layer -#define UDS_TP_ISOTP_C 1 // use isotp-c -#define UDS_TP_ISOTP_SOCKET 2 // use linux ISO-TP socket - -#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 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; - -// -// Convenience functions to wrap 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); - -// ======================================================================== -// 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 Get time in milliseconds - * @return current time in milliseconds - */ -uint32_t UDSMillis(void); - -// ======================================================================== -// 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; - -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); - -// ======================================================================== -// 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 * - UDS_SRV_EVT_Err, // UDSErr_t * - UDS_EVT_IDLE, - UDS_EVT_RESP_RECV, -}; - -typedef int UDSServerEvent_t; -typedef UDSServerEvent_t UDSEvent_t; - -/** - * @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 - - /** - * @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; - -// TODO: Remove -typedef struct { - uint8_t (*fn)(UDSServer_t *srv, UDSServerEvent_t event, const void *arg); - UDSTpHandle_t *tp; -} 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 *srv); -void UDSServerPoll(UDSServer_t *srv); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/run_gdb.py b/run_gdb.py new file mode 100755 index 0000000..a9056cc --- /dev/null +++ b/run_gdb.py @@ -0,0 +1,53 @@ +#!/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 + +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}"] + +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}"], + ) + + 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) + 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..1351727 --- /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..141edc6 --- /dev/null +++ b/src/client.h @@ -0,0 +1,144 @@ +#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..0f0d173 --- /dev/null +++ b/src/config.h @@ -0,0 +1,66 @@ +#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/iso14229serverbufferedwriter.h b/src/iso14229serverbufferedwriter.h similarity index 100% rename from iso14229serverbufferedwriter.h rename to src/iso14229serverbufferedwriter.h diff --git a/src/server.c b/src/server.c new file mode 100644 index 0000000..38146a7 --- /dev/null +++ b/src/server.c @@ -0,0 +1,944 @@ +#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); + long long unsigned int 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(), UDS_SERVER_0x27_BRUTE_FORCE_MITIGATION_BOOT_DELAY_MS)) { + 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; + 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); + } + + UDSTpStatus_t tpStatus = 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..a741232 --- /dev/null +++ b/src/server.h @@ -0,0 +1,169 @@ +#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 + + /** + * @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; + +// TODO: Remove +typedef struct { + uint8_t (*fn)(UDSServer_t *srv, UDSServerEvent_t event, const void *arg); + UDSTpHandle_t *tp; +} 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 *srv); +void UDSServerPoll(UDSServer_t *srv); diff --git a/src/sys.h b/src/sys.h new file mode 100644 index 0000000..417e908 --- /dev/null +++ b/src/sys.h @@ -0,0 +1,30 @@ +#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..39e6731 --- /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..6a2df9f --- /dev/null +++ b/src/sys_esp32.h @@ -0,0 +1,14 @@ +#pragma once + +#if UDS_SYS==UDS_SYS_ESP32 + +#include +#include +#include + +#define UDS_TP UDS_TP_ISOTP_C +#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..b2baf19 --- /dev/null +++ b/src/sys_unix.h @@ -0,0 +1,12 @@ +#pragma once + +#if UDS_SYS==UDS_SYS_UNIX + +#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..25d6c43 --- /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..966cab5 --- /dev/null +++ b/src/tp.h @@ -0,0 +1,128 @@ +#pragma once + +#include "sys.h" + +#if !defined(UDS_TP) +#if (UDS_SYS == UDS_SYS_UNIX) +#define UDS_TP UDS_TP_ISOTP_SOCKET +#endif +#endif + +#include +#include +#include +#include +#include + +#if (UDS_TP == UDS_TP_ISOTP_C) +#include "tp/isotp-c/isotp.h" +#include "tp/isotp-c/isotp_config.h" +#include "tp/isotp-c/isotp_defines.h" +#elif (UDS_TP == UDS_TP_ISOTP_SOCKET) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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/tp/isotp-c b/src/tp/isotp-c similarity index 100% rename from tp/isotp-c rename to src/tp/isotp-c diff --git a/tp/isotp_c.c b/src/tp/isotp_c.c similarity index 91% rename from tp/isotp_c.c rename to src/tp/isotp_c.c index a0a44f1..05f6f76 100644 --- a/tp/isotp_c.c +++ b/src/tp/isotp_c.c @@ -1,12 +1,15 @@ + +#include +#if UDS_TP == UDS_TP_ISOTP_C + +#include "util.h" #include "tp/isotp_c.h" #include "tp/isotp-c/isotp.h" -#include -#include static UDSTpStatus_t tp_poll(UDSTpHandle_t *hdl) { assert(hdl); UDSTpStatus_t status = 0; - UDSTpISOTpC_t *impl = (UDSTpISOTpC_t *)hdl; + 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; @@ -42,7 +45,7 @@ int peek_link(IsoTpLink *link, uint8_t *buf, size_t bufsize, bool functional) { static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { assert(hdl); assert(p_buf); - UDSTpISOTpC_t *tp = (UDSTpISOTpC_t *)hdl; + 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; @@ -89,7 +92,7 @@ static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { static ssize_t 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; + 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; @@ -128,17 +131,20 @@ static ssize_t tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, UDSSDU_t *i static void tp_ack_recv(UDSTpHandle_t *hdl) { assert(hdl); printf("ack recv\n"); - UDSTpISOTpC_t *tp = (UDSTpISOTpC_t *)hdl; + 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); - UDSTpISOTpC_t *tp = (UDSTpISOTpC_t *)hdl; + UDSISOTpC_t *tp = (UDSISOTpC_t *)hdl; *p_buf = tp->send_buf; return sizeof(tp->send_buf); } -UDSErr_t UDSTpISOTpCInit(UDSTpISOTpC_t *tp, UDSTpISOTpCConfig_t *cfg) { +UDSErr_t UDSISOTpCInit(UDSISOTpC_t *tp, const UDSISOTpCConfig_t *cfg) { if (cfg == NULL || tp == NULL) { return UDS_ERR; } @@ -159,4 +165,6 @@ UDSErr_t UDSTpISOTpCInit(UDSTpISOTpC_t *tp, UDSTpISOTpCConfig_t *cfg) { sizeof(tp->recv_buf), UDSMillis, cfg->isotp_user_send_can, cfg->isotp_user_debug, cfg->user_data); return UDS_OK; -} \ No newline at end of file +} + +#endif diff --git a/tp/isotp_c.h b/src/tp/isotp_c.h similarity index 76% rename from tp/isotp_c.h rename to src/tp/isotp_c.h index 84f6f98..0226576 100644 --- a/tp/isotp_c.h +++ b/src/tp/isotp_c.h @@ -1,7 +1,10 @@ -#ifndef ISOTP_C_H -#define ISOTP_C_H +#pragma once +#if UDS_TP == UDS_TP_ISOTP_C -#include "iso14229.h" +#include "sys.h" +#include "config.h" +#include "uds.h" +#include "tp.h" #include "tp/isotp-c/isotp.h" typedef struct { @@ -12,7 +15,7 @@ typedef struct { uint8_t recv_buf[UDS_ISOTP_MTU]; uint32_t phys_sa, phys_ta; uint32_t func_sa, func_ta; -} UDSTpISOTpC_t; +} UDSISOTpC_t; typedef struct { uint32_t source_addr; @@ -25,10 +28,10 @@ typedef struct { uint32_t (*isotp_user_get_ms)(void); /* get millisecond */ void (*isotp_user_debug)(const char *message, ...); /* print debug message */ void *user_data; /* user data */ -} UDSTpISOTpCConfig_t; +} UDSISOTpCConfig_t; -UDSErr_t UDSTpISOTpCInit(UDSTpISOTpC_t *tp, UDSTpISOTpCConfig_t *cfg); +UDSErr_t UDSISOTpCInit(UDSISOTpC_t *tp, const UDSISOTpCConfig_t *cfg); -void UDSTpISOTpCDeinit(UDSTpISOTpC_t *tp); +void UDSISOTpCDeinit(UDSISOTpC_t *tp); -#endif +#endif diff --git a/tp/isotp_c_socketcan.c b/src/tp/isotp_c_socketcan.c similarity index 87% rename from tp/isotp_c_socketcan.c rename to src/tp/isotp_c_socketcan.c index 5c5ade5..1977c32 100644 --- a/tp/isotp_c_socketcan.c +++ b/src/tp/isotp_c_socketcan.c @@ -1,3 +1,5 @@ +#if defined(UDS_TP_ISOTP_C_SOCKETCAN) + #include "tp/isotp_c_socketcan.h" #include "iso14229.h" #include "tp/isotp-c/isotp_defines.h" @@ -99,7 +101,7 @@ static void SocketCANRecv(UDSTpISOTpC_t *tp) { } } -static UDSTpStatus_t tp_poll(UDSTpHandle_t *hdl) { +static UDSTpStatus_t isotp_c_socketcan_tp_poll(UDSTpHandle_t *hdl) { assert(hdl); UDSTpStatus_t status = 0; UDSTpISOTpC_t *impl = (UDSTpISOTpC_t *)hdl; @@ -111,7 +113,7 @@ static UDSTpStatus_t tp_poll(UDSTpHandle_t *hdl) { return status; } -int peek_link(IsoTpLink *link, uint8_t *buf, size_t bufsize, bool functional) { +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; @@ -136,7 +138,7 @@ int peek_link(IsoTpLink *link, uint8_t *buf, size_t bufsize, bool functional) { return ret; } -static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { +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; @@ -145,7 +147,7 @@ static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { return tp->phys_link.receive_size; } int ret = -1; - ret = peek_link(&tp->phys_link, tp->recv_buf, sizeof(tp->recv_buf), false); + 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; @@ -160,7 +162,7 @@ static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { } else if (ret < 0) { goto done; } else { - ret = peek_link(&tp->func_link, tp->recv_buf, sizeof(tp->recv_buf), true); + 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; @@ -190,7 +192,7 @@ static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { return ret; } -static ssize_t tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, UDSSDU_t *info) { +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; @@ -237,13 +239,15 @@ static ssize_t tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, UDSSDU_t *i return ret; } -static void tp_ack_recv(UDSTpHandle_t *hdl) { +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 tp_get_send_buf(UDSTpHandle_t *hdl, uint8_t **p_buf) { +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; @@ -255,11 +259,11 @@ UDSErr_t UDSTpISOTpCInit(UDSTpISOTpC_t *tp, const char *ifname, uint32_t source_ uint32_t target_addr_func) { assert(tp); assert(ifname); - 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->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; @@ -279,4 +283,6 @@ void UDSTpISOTpCDeinit(UDSTpISOTpC_t *tp) { assert(tp); close(tp->fd); tp->fd = -1; -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/tp/isotp_c_socketcan.h b/src/tp/isotp_c_socketcan.h similarity index 90% rename from tp/isotp_c_socketcan.h rename to src/tp/isotp_c_socketcan.h index 16666fa..893a62a 100644 --- a/tp/isotp_c_socketcan.h +++ b/src/tp/isotp_c_socketcan.h @@ -1,9 +1,10 @@ -#ifndef ISOTP_C_SOCKETCAN_H -#define ISOTP_C_SOCKETCAN_H +#pragma once #include "iso14229.h" #include "tp/isotp-c/isotp.h" +#if defined(UDS_TP_ISOTP_C_SOCKETCAN) + typedef struct { UDSTpHandle_t hdl; IsoTpLink phys_link; @@ -21,4 +22,4 @@ UDSErr_t UDSTpISOTpCInit(UDSTpISOTpC_t *tp, const char *ifname, uint32_t source_ uint32_t target_addr_func); void UDSTpISOTpCDeinit(UDSTpISOTpC_t *tp); -#endif \ No newline at end of file +#endif diff --git a/tp/isotp_sock.c b/src/tp/isotp_sock.c similarity index 88% rename from tp/isotp_sock.c rename to src/tp/isotp_sock.c index f642367..966542b 100644 --- a/tp/isotp_sock.c +++ b/src/tp/isotp_sock.c @@ -1,3 +1,5 @@ +#if defined(UDS_TP_ISOTP_SOCK) + #include "tp/isotp_sock.h" #include "iso14229.h" #include @@ -11,7 +13,7 @@ #include #include -static UDSTpStatus_t tp_poll(UDSTpHandle_t *hdl) { return 0; } +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); @@ -28,7 +30,7 @@ static ssize_t tp_recv_once(int fd, uint8_t *buf, size_t size) { return ret; } -static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { +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; @@ -78,13 +80,13 @@ static ssize_t tp_peek(UDSTpHandle_t *hdl, uint8_t **p_buf, UDSSDU_t *info) { return ret; } -static void tp_ack_recv(UDSTpHandle_t *hdl) { +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 tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, UDSSDU_t *info) { +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; @@ -122,7 +124,7 @@ static ssize_t tp_send(UDSTpHandle_t *hdl, uint8_t *buf, size_t len, UDSSDU_t *i return ret; } -static ssize_t tp_get_send_buf(UDSTpHandle_t *hdl, uint8_t **p_buf) { +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; @@ -180,11 +182,11 @@ static int LinuxSockBind(const char *if_name, uint32_t rxid, uint32_t txid, bool UDSErr_t UDSTpIsoTpSockInitServer(UDSTpIsoTpSock_t *tp, const char *ifname, uint32_t source_addr, uint32_t target_addr, uint32_t source_addr_func) { assert(tp); - tp->hdl.peek = tp_peek; - tp->hdl.send = tp_send; - tp->hdl.poll = tp_poll; - tp->hdl.ack_recv = tp_ack_recv; - tp->hdl.get_send_buf = tp_get_send_buf; + 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; @@ -205,11 +207,11 @@ UDSErr_t UDSTpIsoTpSockInitServer(UDSTpIsoTpSock_t *tp, const char *ifname, uint UDSErr_t UDSTpIsoTpSockInitClient(UDSTpIsoTpSock_t *tp, const char *ifname, uint32_t source_addr, uint32_t target_addr, uint32_t target_addr_func) { assert(tp); - tp->hdl.peek = tp_peek; - tp->hdl.send = tp_send; - tp->hdl.poll = tp_poll; - tp->hdl.ack_recv = tp_ack_recv; - tp->hdl.get_send_buf = tp_get_send_buf; + 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; @@ -236,3 +238,6 @@ void UDSTpIsoTpSockDeinit(UDSTpIsoTpSock_t *tp) { } } } + +#endif + diff --git a/tp/isotp_sock.h b/src/tp/isotp_sock.h similarity index 93% rename from tp/isotp_sock.h rename to src/tp/isotp_sock.h index ff73688..ac189f4 100644 --- a/tp/isotp_sock.h +++ b/src/tp/isotp_sock.h @@ -1,6 +1,6 @@ -#ifndef TP_ISOTP_SOCK_H -#define TP_ISOTP_SOCK_H +#if defined(UDS_TP_ISOTP_SOCK) +#pragma once #include "iso14229.h" typedef struct { diff --git a/tp/mock.c b/src/tp/mock.c similarity index 89% rename from tp/mock.c rename to src/tp/mock.c index 52d2aed..2ce0f27 100644 --- a/tp/mock.c +++ b/src/tp/mock.c @@ -1,3 +1,5 @@ +#if defined(UDS_TP_MOCK) + #include "tp/mock.h" #include "iso14229.h" #include @@ -59,7 +61,7 @@ static void NetworkPoll() { } } -static ssize_t tp_peek(struct UDSTpHandle *hdl, uint8_t **p_buf, UDSSDU_t *info) { +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; @@ -72,7 +74,7 @@ static ssize_t tp_peek(struct UDSTpHandle *hdl, uint8_t **p_buf, UDSSDU_t *info) return tp->recv_len; } -static ssize_t tp_send(struct UDSTpHandle *hdl, uint8_t *buf, size_t len, UDSSDU_t *info) { +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) { @@ -100,13 +102,13 @@ static ssize_t tp_send(struct UDSTpHandle *hdl, uint8_t *buf, size_t len, UDSSDU return len; } -static UDSTpStatus_t tp_poll(struct UDSTpHandle *hdl) { +static UDSTpStatus_t mock_tp_poll(struct UDSTpHandle *hdl) { NetworkPoll(); // todo: make this status reflect TX time return UDS_TP_IDLE; } -static ssize_t tp_get_send_buf(struct UDSTpHandle *hdl, uint8_t **p_buf) { +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; @@ -114,7 +116,7 @@ static ssize_t tp_get_send_buf(struct UDSTpHandle *hdl, uint8_t **p_buf) { return sizeof(tp->send_buf); } -static void tp_ack_recv(struct UDSTpHandle *hdl) { +static void mock_tp_ack_recv(struct UDSTpHandle *hdl) { assert(hdl); TPMock_t *tp = (TPMock_t *)hdl; tp->recv_len = 0; @@ -127,11 +129,11 @@ static void TPMockAttach(TPMock_t *tp, TPMockArgs_t *args) { assert(args); assert(TPCount < MAX_NUM_TP); TPs[TPCount++] = tp; - tp->hdl.peek = tp_peek; - tp->hdl.send = tp_send; - tp->hdl.poll = tp_poll; - tp->hdl.get_send_buf = tp_get_send_buf; - tp->hdl.ack_recv = tp_ack_recv; + 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; @@ -204,4 +206,6 @@ void TPMockFree(UDSTpHandle_t *tp) { TPMock_t *tpm = (TPMock_t *)tp; TPMockDetach(tpm); free(tp); -} \ No newline at end of file +} + +#endif diff --git a/tp/mock.h b/src/tp/mock.h similarity index 94% rename from tp/mock.h rename to src/tp/mock.h index 53279a6..13cff58 100644 --- a/tp/mock.h +++ b/src/tp/mock.h @@ -4,17 +4,16 @@ * @date 2023-10-21 * */ +#if defined(UDS_TP_MOCK) -#include "iso14229.h" +#pragma once -#ifdef __cplusplus -extern "C" { -#endif +#include "iso14229.h" typedef struct TPMock { UDSTpHandle_t hdl; - uint8_t recv_buf[UDS_BUFSIZE]; - uint8_t send_buf[UDS_BUFSIZE]; + 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 @@ -64,6 +63,5 @@ void TPMockLogToStdout(void); */ void TPMockReset(void); -#ifdef __cplusplus -} #endif + diff --git a/src/uds.h b/src/uds.h new file mode 100644 index 0000000..455d11e --- /dev/null +++ b/src/uds.h @@ -0,0 +1,223 @@ +#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..fda75e2 --- /dev/null +++ b/src/util.h @@ -0,0 +1,42 @@ +#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/test/BUILD b/test/BUILD index 4dbb91f..7fc4cea 100644 --- a/test/BUILD +++ b/test/BUILD @@ -4,14 +4,18 @@ cc_library( "env.c", "env.h", "test.h", - "//:iso14229_srcs", - "//tp:srcs", + "//:iso14229.h", + "//:iso14229.c", ], deps = [ "@cmocka" ], defines = [ - "UDS_TP=UDS_TP_CUSTOM", + "UDS_TP_ISOTP_C_SOCKETCAN", + "UDS_TP_ISOTP_SOCK", + "UDS_TP_MOCK", "UDS_CUSTOM_MILLIS", - "UDS_DBG_PRINT=printf", + "UDS_ENABLE_DBG_PRINT", + "UDS_ENABLE_ASSERT", + "UDS_LINES", ], copts = [ "-g", ], ) @@ -110,8 +114,8 @@ sh_test( cc_library( name = "ultra_strict", srcs = [ - "//:iso14229_srcs", - "//tp:srcs", + "//:iso14229.h", + "//:iso14229.c", ], copts = [ "-Werror", diff --git a/test/env.c b/test/env.c index b104df0..f70c8d3 100644 --- a/test/env.c +++ b/test/env.c @@ -5,9 +5,6 @@ #include #include #include -#include "tp/isotp_c_socketcan.h" -#include "tp/mock.h" -#include "tp/isotp_sock.h" static UDSServer_t *registeredServer = NULL; static UDSClient_t *registeredClient = NULL; diff --git a/test/test.h b/test/test.h index a0c1445..61313d1 100644 --- a/test/test.h +++ b/test/test.h @@ -7,7 +7,6 @@ #include #include "iso14229.h" #include "test/env.h" -#include "tp/mock.h" #include #define _TEST_INT_COND(a, b, cond) \ diff --git a/test/test_fuzz_server.c b/test/test_fuzz_server.c index eeaf10b..ed6c994 100644 --- a/test/test_fuzz_server.c +++ b/test/test_fuzz_server.c @@ -18,11 +18,11 @@ typedef struct { uint16_t client_sa; uint16_t client_ta; uint8_t client_func_req; - uint8_t msg[UDS_BUFSIZE]; + uint8_t msg[UDS_TP_MTU]; } StuffToFuzz_t; static StuffToFuzz_t fuzz; -static uint8_t client_recv_buf[UDS_BUFSIZE]; +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); diff --git a/test_iso14229.c b/test_iso14229.c deleted file mode 100644 index 6cfd587..0000000 --- a/test_iso14229.c +++ /dev/null @@ -1,875 +0,0 @@ -#define UDS_TP UDS_TP_CUSTOM -#include -#include -#include -#include -#include -#include -#include "iso14229.h" -#include "tp/mock.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 CLIENT_TARGET_ADDR (0x7E0U) -#define CLIENT_TARGET_ADDR_FUNC (0x7DFU) -#define CLIENT_SOURCE_ADDR (0x7E8U) - -#define SERVER_TARGET_ADDR (0x7E8U) -#define SERVER_SOURCE_ADDR (0x7E0U) -#define SERVER_SOURCE_ADDR_FUNC (0x7DFU) - -/* - 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 SERVER_ONLY 0 -#define CLIENT_ONLY 1 - -#define _TEST_SETUP_SILENT(test_type, param_str) \ - memset(&ctx, 0, sizeof(ctx)); \ - ctx.func_name = __PRETTY_FUNCTION__; \ - if (SERVER_ONLY == test_type) { \ - UDSServerInit(&ctx.server, &(UDSServerConfig_t){ \ - .tp = TPMockNew("server"), \ - .source_addr = SERVER_SOURCE_ADDR, \ - .target_addr = SERVER_TARGET_ADDR, \ - .source_addr_func = SERVER_SOURCE_ADDR_FUNC, \ - }); \ - ctx.mock_tp = TPMockNew("mock_client"); \ - } \ - if (CLIENT_ONLY == test_type) { \ - UDSClientInit(&ctx.client, &(UDSClientConfig_t){ \ - .tp = TPMockNew("client"), \ - .target_addr = CLIENT_TARGET_ADDR, \ - .source_addr = CLIENT_SOURCE_ADDR, \ - .target_addr_func = CLIENT_TARGET_ADDR_FUNC, \ - }); \ - ctx.mock_tp = TPMockNew("mock_server"); \ - } \ - char logfilename[256] = {0}; \ - snprintf(logfilename, sizeof(logfilename), "%s%s.log", ctx.func_name, param_str); \ - TPMockLogToFile(logfilename); - -#define TEST_SETUP(test_type) \ - _TEST_SETUP_SILENT(test_type, ""); \ - printf("%s\n", ctx.func_name); - -#define TEST_SETUP_PARAMETRIZED(test_type, params_list) \ - for (size_t i = 0; i < sizeof(params_list) / sizeof(params_list[0]); i++) { \ - char _param_str[128]; \ - snprintf(_param_str, sizeof(_param_str), "%s_p_%ld_%s", ctx.func_name, i, \ - (*(char **)(&(params_list[i])))); \ - _TEST_SETUP_SILENT(test_type, _param_str); \ - printf("%s\n", _param_str); - -#define TEST_TEARDOWN_PARAMETRIZED() \ - TPMockReset(); \ - printf(ANSI_BOLD "OK [p:%ld]\n" ANSI_RESET, i); \ - } - -#define TEST_TEARDOWN() \ - { \ - TPMockReset(); \ - 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"); -} - -// static ssize_t mock_transport_recv(UDSTpHandle_t *hdl, UDSSDU_t *msg) { -// assert(hdl); -// struct MockTransport *tp = (struct MockTransport *)hdl; -// size_t size = tp->recv_size; - -// if (msg->A_DataBufSize < size) { -// return -ENOBUFS; -// } - -// if (size) { -// memmove((void *)msg->A_Data, 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(msg->A_Data, size); -// printf(ANSI_RESET); -// } -// return size; -// } - -// static ssize_t mock_transport_send(UDSTpHandle_t *hdl, UDSSDU_t *msg) { -// assert(hdl); -// struct MockTransport *tp = (struct MockTransport *)hdl; -// printf(ANSI_BRIGHT_GREEN "--%s_tp_send-%04d->[%02d] ", tp->tag, UDSMillis(), msg->A_Length); -// printhex(msg->A_Data, msg->A_Length); -// printf(ANSI_RESET); -// assert(msg->A_Length); // why send zero? -// memmove(tp->send_buf, msg->A_Data, msg->A_Length); -// tp->send_size = msg->A_Length; -// return msg->A_Length; -// } - -// 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; - UDSClient_t client; - UDSTpHandle_t *mock_tp; - uint8_t mock_recv_buf[DEFAULT_ISOTP_BUFSIZE]; - struct MockTransport client_tp; - uint32_t time_ms; - uint32_t deadline; - int 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) { - if (ctx->server.tp) { - UDSServerPoll(&ctx->server); - } - if (ctx->client.tp) { - UDSClientPoll(&ctx->client); - } - ctx->time_ms++; -} - -/* - 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 SEND_TO_SERVER(d1, reqType) \ - { \ - UDSSDU_t msg = { \ - .A_Mtype = UDS_A_MTYPE_DIAG, \ - .A_Data = d1, \ - .A_Length = sizeof(d1), \ - .A_SA = CLIENT_SOURCE_ADDR, \ - .A_TA = \ - reqType == UDS_A_TA_TYPE_PHYSICAL ? SERVER_SOURCE_ADDR : SERVER_SOURCE_ADDR_FUNC, \ - .A_TA_Type = (int)reqType, \ - }; \ - ctx.mock_tp->send(ctx.mock_tp, &msg); \ - } - -#define ASSERT_CLIENT_SENT(d1, reqType) \ - { \ - UDSSDU_t msg = { \ - .A_DataBufSize = DEFAULT_ISOTP_BUFSIZE, \ - .A_Data = ctx.mock_recv_buf, \ - }; \ - int recv_len = ctx.mock_tp->recv(ctx.mock_tp, &msg); \ - ASSERT_INT_EQUAL(recv_len, sizeof(d1)); \ - ASSERT_MEMORY_EQUAL(ctx.mock_recv_buf, d1, sizeof(d1)); \ - if (reqType == UDS_A_TA_TYPE_PHYSICAL) { \ - ASSERT_INT_EQUAL(msg.A_TA, SERVER_SOURCE_ADDR); \ - } else if (reqType == UDS_A_TA_TYPE_FUNCTIONAL) { \ - ASSERT_INT_EQUAL(msg.A_TA, SERVER_SOURCE_ADDR_FUNC); \ - } else { \ - assert(0); \ - } \ - } - -// 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)); - ctx.mock_tp->send(ctx.mock_tp, &(UDSSDU_t){ - .A_Mtype = UDS_A_MTYPE_DIAG, - .A_Data = d1, - .A_Length = len, - .A_SA = SERVER_SOURCE_ADDR, - .A_TA = SERVER_TARGET_ADDR, - .A_TA_Type = (int)reqType, - }); - // 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); -/* - while (0 == ctx.server_tp.send_size) { \ - poll_ctx(&ctx); \ - ASSERT_INT_LE(ctx.time_ms, deadline); \ - } \ - */ - -// expect a server response within a timeout -#define EXPECT_RESPONSE_WITHIN_MILLIS(d1, reqType, timeout_ms) \ - { \ - uint32_t deadline = ctx.time_ms + timeout_ms + 1; \ - UDSSDU_t msg = { \ - .A_DataBufSize = DEFAULT_ISOTP_BUFSIZE, \ - .A_Data = ctx.mock_recv_buf, \ - }; \ - while (0 == ctx.mock_tp->recv(ctx.mock_tp, &msg)) { \ - poll_ctx(&ctx); \ - printf("%d, %d, %d\n", UDSMillis(), ctx.time_ms, deadline); \ - ASSERT_INT_LE(ctx.time_ms, deadline); \ - } \ - printhex(msg.A_Data, msg.A_Length); \ - ASSERT_INT_EQUAL(msg.A_Length, sizeof(d1)); \ - ASSERT_MEMORY_EQUAL(msg.A_Data, d1, sizeof(d1)); \ - ASSERT_INT_EQUAL((int)msg.A_TA_Type, reqType); \ - } - -// expect no server response within a timeout -#define EXPECT_NO_RESPONSE_FOR_MILLIS(timeout_ms) \ - { \ - uint32_t deadline = ctx.time_ms + timeout_ms; \ - while (ctx.time_ms <= deadline) { \ - poll_ctx(&ctx); \ - UDSSDU_t msg = { \ - .A_DataBufSize = DEFAULT_ISOTP_BUFSIZE, \ - .A_Data = ctx.mock_recv_buf, \ - }; \ - int resp_len = ctx.mock_tp->recv(ctx.mock_tp, &msg); \ - ASSERT_INT_EQUAL(resp_len, 0); \ - } \ - } - -void testServer0x10DiagSessCtrlIsDisabledByDefault() { - TEST_SETUP(SERVER_ONLY); - const uint8_t REQ[] = {0x10, 0x02}; - SEND_TO_SERVER(REQ, UDS_A_TA_TYPE_PHYSICAL); - const uint8_t RESP[] = {0x7f, 0x10, 0x11}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, UDS_A_TA_TYPE_PHYSICAL, 50); - TEST_TEARDOWN(); -} - -void testServer0x10DiagSessCtrlFunctionalRequest() { - TEST_SETUP(SERVER_ONLY); - // sending a diagnostic session control request functional broadcast - const uint8_t REQ[] = {0x10, 0x03}; - SEND_TO_SERVER(REQ, UDS_A_TA_TYPE_FUNCTIONAL); - // should receive a physical response - const uint8_t RESP[] = {0x7f, 0x10, 0x11}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, UDS_A_TA_TYPE_PHYSICAL, 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(SERVER_ONLY); - 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, UDS_A_TA_TYPE_PHYSICAL); - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, UDS_A_TA_TYPE_PHYSICAL, 50); - // Sending subsequent ECU reset requests should not receive any response - SEND_TO_SERVER(REQ, UDS_A_TA_TYPE_PHYSICAL); - 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(SERVER_ONLY); - ctx.server.fn = fn2; - { - uint8_t REQ[] = {0x22, 0xF1, 0x90}; - SEND_TO_SERVER(REQ, UDS_A_TA_TYPE_PHYSICAL); - 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, UDS_A_TA_TYPE_PHYSICAL, 50); - } - { - uint8_t REQ[] = {0x22, 0xF1, 0x91}; - SEND_TO_SERVER(REQ, UDS_A_TA_TYPE_PHYSICAL); - uint8_t RESP[] = {0x7F, 0x22, 0x31}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, UDS_A_TA_TYPE_PHYSICAL, 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(SERVER_ONLY); - ctx.server.fn = fn10; - uint8_t REQ[] = {0x23, 0x18, 0x00, 0x00, 0x55, 0x55, 0x55, 0x55, 0xf0, 0xc8, 0x04}; - SEND_TO_SERVER(REQ, UDS_A_TA_TYPE_PHYSICAL); - uint8_t RESP[] = {0x63, 0x01, 0x02, 0x03, 0x04}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, UDS_A_TA_TYPE_PHYSICAL, 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(SERVER_ONLY); - 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, UDS_A_TA_TYPE_PHYSICAL); - EXPECT_RESPONSE_WITHIN_MILLIS(SEED_RESPONSE, UDS_A_TA_TYPE_PHYSICAL, 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, UDS_A_TA_TYPE_PHYSICAL); - EXPECT_RESPONSE_WITHIN_MILLIS(UNLOCK_RESPONSE, UDS_A_TA_TYPE_PHYSICAL, 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, UDS_A_TA_TYPE_PHYSICAL); - EXPECT_RESPONSE_WITHIN_MILLIS(ALREADY_UNLOCKED_RESPONSE, UDS_A_TA_TYPE_PHYSICAL, 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(SERVER_ONLY); - // 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, UDS_A_TA_TYPE_PHYSICAL); - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, UDS_A_TA_TYPE_PHYSICAL, 50) - - // The server should again respond within p2_star ms, and keep responding - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, UDS_A_TA_TYPE_PHYSICAL, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, UDS_A_TA_TYPE_PHYSICAL, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, UDS_A_TA_TYPE_PHYSICAL, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, UDS_A_TA_TYPE_PHYSICAL, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, UDS_A_TA_TYPE_PHYSICAL, 50) - EXPECT_RESPONSE_WITHIN_MILLIS(RCRRP, UDS_A_TA_TYPE_PHYSICAL, 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, UDS_A_TA_TYPE_PHYSICAL, 50) - - TEST_TEARDOWN(); -} - -void testServer0x34NotEnabled() { - TEST_SETUP(SERVER_ONLY); - // 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, UDS_A_TA_TYPE_PHYSICAL); - - // should return a kServiceNotSupported response - const uint8_t OUT[] = {0x7F, 0x34, 0x11}; - EXPECT_RESPONSE_WITHIN_MILLIS(OUT, UDS_A_TA_TYPE_PHYSICAL, 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(SERVER_ONLY); - // 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, UDS_A_TA_TYPE_PHYSICAL); - - // should receive a positive response matching UDS-1:2013 Table 415 - uint8_t RESP[] = {0x74, 0x20, 0x00, 0x81}; - EXPECT_RESPONSE_WITHIN_MILLIS(RESP, UDS_A_TA_TYPE_PHYSICAL, 50) - TEST_TEARDOWN(); -} - -/* UDS-1 2013 Table 72 */ -void testServer0x3ESuppressPositiveResponse() { - TEST_SETUP(SERVER_ONLY); - 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, UDS_A_TA_TYPE_PHYSICAL); - EXPECT_NO_RESPONSE_FOR_MILLIS(5000); - TEST_TEARDOWN(); -} - -void testServer0x83DiagnosticSessionControl() { - TEST_SETUP(SERVER_ONLY); - 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, UDS_A_TA_TYPE_PHYSICAL); - 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(SERVER_ONLY, 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(CLIENT_ONLY); - // 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(CLIENT_ONLY); - // 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, UDS_A_TA_TYPE_PHYSICAL); - - 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(CLIENT_ONLY); - // 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(CLIENT_ONLY); - // 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(CLIENT_ONLY); - 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(CLIENT_ONLY, 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, UDS_A_TA_TYPE_PHYSICAL); - // 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(CLIENT_ONLY); - - // 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(CLIENT_ONLY); - 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(CLIENT_ONLY); - - { // 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, UDS_A_TA_TYPE_PHYSICAL); - - // 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, UDS_A_TA_TYPE_PHYSICAL); - - // 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, UDS_A_TA_TYPE_PHYSICAL); - - 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(CLIENT_ONLY); - // 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, UDS_A_TA_TYPE_PHYSICAL); - TEST_TEARDOWN(); -} - -void testClient0x34UDSUnpackRequestDownloadResponse() { - TEST_SETUP(CLIENT_ONLY); - 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/tp/BUILD b/tp/BUILD deleted file mode 100644 index bf3e8bc..0000000 --- a/tp/BUILD +++ /dev/null @@ -1,63 +0,0 @@ -package(default_visibility = ["//visibility:public"]) - -cc_library( - name="mock", - srcs=[ - "mock.c", - "mock.h", - "//:iso14229.h" - ], -) - -cc_library( - name="isotp_sock", - srcs=[ - "isotp_sock.c", - "isotp_sock.h", - "//:iso14229.h" - ], -) - - -filegroup( - name="isotp_c_srcs", - srcs=[ - "isotp-c/isotp.c", - "isotp-c/isotp.h", - "isotp-c/isotp_config.h", - "isotp-c/isotp_defines.h", - ], -) - -cc_library( - name="isotp_c", - srcs=[":isotp_c_srcs"], - copts=["-Wno-unused-parameter"], -) - -cc_library( - name="isotp_c_socketcan", - srcs=[ - "isotp_c_socketcan.c", - "isotp_c_socketcan.h", - "//:iso14229.h", - ], - deps = [ - ":isotp_c", - ], -) - -filegroup( - name="srcs", - srcs = [ - "mock.c", - "mock.h", - "isotp_sock.c", - "isotp_sock.h", - "isotp_c_socketcan.c", - "isotp_c_socketcan.h", - "isotp_c.c", - "isotp_c.h", - ":isotp_c_srcs", - ] -)