File syn_kwp2000.c¶
File List > proto > syn_kwp2000.c
Go to the documentation of this file
#include "syn_kwp2000.h"
#include <string.h>
static void make_nrc(uint8_t sid, uint8_t nrc, uint8_t *resp_buf, uint16_t *resp_len)
{
if (resp_buf != NULL && resp_len != NULL) {
resp_buf[0] = SYN_KWP2000_SID_NEGATIVE_RESPONSE;
resp_buf[1] = sid;
resp_buf[2] = nrc;
*resp_len = 3U;
}
}
SYN_Status syn_kwp2000_init(SYN_KWP2000_Server *server)
{
if (server == NULL)
return SYN_INVALID_PARAM;
memset(server, 0, sizeof(*server));
server->current_session = SYN_KWP2000_SESSION_DEFAULT;
return SYN_OK;
}
SYN_Status syn_kwp2000_register_lid(SYN_KWP2000_Server *server, uint8_t lid, const void *data,
uint16_t len, bool writable)
{
if (server == NULL || data == NULL || len == 0U)
return SYN_INVALID_PARAM;
for (uint16_t i = 0U; i < server->lid_count; i++) {
if (server->lids[i].lid == lid) {
server->lids[i].data = data;
server->lids[i].len = len;
server->lids[i].writable = writable;
return SYN_OK;
}
}
if (server->lid_count >= SYN_KWP2000_MAX_LIDS)
return SYN_ERROR;
server->lids[server->lid_count].lid = lid;
server->lids[server->lid_count].data = data;
server->lids[server->lid_count].len = len;
server->lids[server->lid_count].writable = writable;
server->lid_count++;
return SYN_OK;
}
SYN_Status syn_kwp2000_register_cid(SYN_KWP2000_Server *server, uint16_t cid, const void *data,
uint16_t len, bool writable)
{
if (server == NULL || data == NULL || len == 0U)
return SYN_INVALID_PARAM;
for (uint16_t i = 0U; i < server->cid_count; i++) {
if (server->cids[i].cid == cid) {
server->cids[i].data = data;
server->cids[i].len = len;
server->cids[i].writable = writable;
return SYN_OK;
}
}
if (server->cid_count >= SYN_KWP2000_MAX_CIDS)
return SYN_ERROR;
server->cids[server->cid_count].cid = cid;
server->cids[server->cid_count].data = data;
server->cids[server->cid_count].len = len;
server->cids[server->cid_count].writable = writable;
server->cid_count++;
return SYN_OK;
}
SYN_Status syn_kwp2000_set_session_handler(SYN_KWP2000_Server *server,
SYN_KWP2000_SessionHandler cb, void *user_ctx)
{
if (server == NULL)
return SYN_INVALID_PARAM;
server->session_cb = cb;
server->session_ctx = user_ctx;
return SYN_OK;
}
SYN_Status syn_kwp2000_set_reset_handler(SYN_KWP2000_Server *server, SYN_KWP2000_ResetHandler cb,
void *user_ctx)
{
if (server == NULL)
return SYN_INVALID_PARAM;
server->reset_cb = cb;
server->reset_ctx = user_ctx;
return SYN_OK;
}
SYN_Status syn_kwp2000_set_security_handler(SYN_KWP2000_Server *server,
SYN_KWP2000_SeedHandler seed_cb,
SYN_KWP2000_KeyHandler key_cb, void *user_ctx)
{
if (server == NULL)
return SYN_INVALID_PARAM;
server->seed_cb = seed_cb;
server->key_cb = key_cb;
server->security_ctx = user_ctx;
return SYN_OK;
}
SYN_Status syn_kwp2000_set_routine_handler(SYN_KWP2000_Server *server,
SYN_KWP2000_RoutineHandler cb, void *user_ctx)
{
if (server == NULL)
return SYN_INVALID_PARAM;
server->routine_cb = cb;
server->routine_ctx = user_ctx;
return SYN_OK;
}
SYN_Status syn_kwp2000_set_memory_handler(SYN_KWP2000_Server *server, SYN_KWP2000_MemoryHandler cb,
void *user_ctx)
{
if (server == NULL)
return SYN_INVALID_PARAM;
server->memory_cb = cb;
server->memory_ctx = user_ctx;
return SYN_OK;
}
/* ── Service Processing Implementation ─────────────────────────────── */
SYN_Status syn_kwp2000_process_request(SYN_KWP2000_Server *server, const uint8_t *req_buf,
uint16_t req_len, uint8_t *resp_buf, uint16_t max_resp_len,
uint16_t *resp_len)
{
if (server == NULL || req_buf == NULL || resp_buf == NULL || resp_len == NULL ||
max_resp_len < 3U) {
return SYN_INVALID_PARAM;
}
if (req_len < 1U) {
make_nrc(0x00U, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint8_t sid = req_buf[0];
switch (sid) {
/* 0x10 StartDiagnosticSession */
case SYN_KWP2000_SID_START_DIAGNOSTIC_SESSION: {
if (req_len < 2U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint8_t target_session = req_buf[1];
if (target_session != SYN_KWP2000_SESSION_DEFAULT &&
target_session != SYN_KWP2000_SESSION_PROGRAMMING &&
target_session != SYN_KWP2000_SESSION_EXTENDED) {
make_nrc(sid, SYN_KWP2000_NRC_SUBFUNCTION_NOT_SUPPORTED, resp_buf, resp_len);
return SYN_OK;
}
if (server->session_cb != NULL) {
if (!server->session_cb(server->current_session, target_session, server->session_ctx)) {
make_nrc(sid, SYN_KWP2000_NRC_CONDITIONS_NOT_CORRECT, resp_buf, resp_len);
return SYN_OK;
}
}
server->current_session = target_session;
server->s3_timer_ms = 0U;
resp_buf[0] = sid + 0x40U;
resp_buf[1] = target_session;
*resp_len = 2U;
return SYN_OK;
}
/* 0x11 ECUReset */
case SYN_KWP2000_SID_ECU_RESET: {
if (req_len < 2U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint8_t reset_type = req_buf[1];
if (reset_type != 0x01U && reset_type != 0x02U) {
make_nrc(sid, SYN_KWP2000_NRC_SUBFUNCTION_NOT_SUPPORTED, resp_buf, resp_len);
return SYN_OK;
}
server->pending_reset_type = reset_type;
resp_buf[0] = sid + 0x40U;
resp_buf[1] = reset_type;
*resp_len = 2U;
return SYN_OK;
}
/* 0x21 ReadDataByLocalIdentifier (LID) */
case SYN_KWP2000_SID_READ_DATA_BY_LOCAL_IDENTIFIER: {
if (req_len < 2U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint8_t target_lid = req_buf[1];
const SYN_KWP2000_LID *found_lid = NULL;
for (uint16_t i = 0U; i < server->lid_count; i++) {
if (server->lids[i].lid == target_lid) {
found_lid = &server->lids[i];
break;
}
}
if (found_lid == NULL) {
make_nrc(sid, SYN_KWP2000_NRC_REQUEST_OUT_OF_RANGE, resp_buf, resp_len);
return SYN_OK;
}
if (2U + found_lid->len > max_resp_len) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
resp_buf[0] = sid + 0x40U;
resp_buf[1] = target_lid;
memcpy(&resp_buf[2], found_lid->data, found_lid->len);
*resp_len = 2U + found_lid->len;
return SYN_OK;
}
/* 0x22 ReadDataByCommonIdentifier (CID) */
case SYN_KWP2000_SID_READ_DATA_BY_COMMON_IDENTIFIER: {
if (req_len < 3U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint16_t target_cid = ((uint16_t)req_buf[1] << 8) | req_buf[2];
const SYN_KWP2000_CID *found_cid = NULL;
for (uint16_t i = 0U; i < server->cid_count; i++) {
if (server->cids[i].cid == target_cid) {
found_cid = &server->cids[i];
break;
}
}
if (found_cid == NULL) {
make_nrc(sid, SYN_KWP2000_NRC_REQUEST_OUT_OF_RANGE, resp_buf, resp_len);
return SYN_OK;
}
if (3U + found_cid->len > max_resp_len) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
resp_buf[0] = sid + 0x40U;
resp_buf[1] = (uint8_t)(target_cid >> 8);
resp_buf[2] = (uint8_t)(target_cid & 0xFFU);
memcpy(&resp_buf[3], found_cid->data, found_cid->len);
*resp_len = 3U + found_cid->len;
return SYN_OK;
}
/* 0x27 SecurityAccess */
case SYN_KWP2000_SID_SECURITY_ACCESS: {
if (req_len < 2U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint8_t sub_func = req_buf[1];
/* Request Seed (Odd subfunctions e.g. 0x01) */
if ((sub_func % 2U) != 0U) {
uint8_t seed_buf[16];
uint16_t seed_len = sizeof(seed_buf);
if (server->seed_cb != NULL) {
if (!server->seed_cb(sub_func, seed_buf, &seed_len, server->security_ctx)) {
make_nrc(sid, SYN_KWP2000_NRC_CONDITIONS_NOT_CORRECT, resp_buf, resp_len);
return SYN_OK;
}
} else {
/* Default seed */
seed_buf[0] = 0x36U;
seed_buf[1] = 0x37U;
seed_len = 2U;
}
resp_buf[0] = sid + 0x40U;
resp_buf[1] = sub_func;
if (2U + seed_len > max_resp_len) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
memcpy(&resp_buf[2], seed_buf, seed_len);
*resp_len = 2U + seed_len;
return SYN_OK;
}
/* Send Key (Even subfunctions e.g. 0x02) */
else {
if (req_len < 3U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
const uint8_t *key = &req_buf[2];
uint16_t key_len = req_len - 2U;
bool key_ok = false;
if (server->key_cb != NULL) {
key_ok = server->key_cb(sub_func, key, key_len, server->security_ctx);
} else {
key_ok = (key_len >= 2U && key[0] == 0xC5U && key[1] == 0xC9U);
}
if (!key_ok) {
make_nrc(sid, SYN_KWP2000_NRC_INVALID_KEY, resp_buf, resp_len);
return SYN_OK;
}
server->security_unlocked = true;
server->security_level = sub_func;
resp_buf[0] = sid + 0x40U;
resp_buf[1] = sub_func;
*resp_len = 2U;
return SYN_OK;
}
}
/* 0x31 StartRoutineByLocalIdentifier */
case SYN_KWP2000_SID_START_ROUTINE_BY_LOCAL_IDENTIFIER: {
if (req_len < 2U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint8_t routine_id = req_buf[1];
const uint8_t *in_data = (req_len > 2U) ? &req_buf[2] : NULL;
uint16_t in_len = (req_len > 2U) ? (req_len - 2U) : 0U;
uint8_t routine_out[64];
uint16_t out_len = 0U;
if (server->routine_cb != NULL) {
if (!server->routine_cb(routine_id, in_data, in_len, routine_out, sizeof(routine_out),
&out_len, server->routine_ctx)) {
make_nrc(sid, SYN_KWP2000_NRC_CONDITIONS_NOT_CORRECT, resp_buf, resp_len);
return SYN_OK;
}
}
resp_buf[0] = sid + 0x40U;
resp_buf[1] = routine_id;
if (out_len > 0U) {
if (2U + out_len > max_resp_len) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
memcpy(&resp_buf[2], routine_out, out_len);
}
*resp_len = 2U + out_len;
return SYN_OK;
}
/* 0x34 RequestDownload */
case SYN_KWP2000_SID_REQUEST_DOWNLOAD: {
if (req_len < 5U) {
make_nrc(sid, SYN_KWP2000_NRC_INCORRECT_MESSAGE_LENGTH, resp_buf, resp_len);
return SYN_OK;
}
uint32_t addr = ((uint32_t)req_buf[1] << 24) | ((uint32_t)req_buf[2] << 16) |
((uint32_t)req_buf[3] << 8) | req_buf[4];
uint32_t size = (req_len >= 9U)
? (((uint32_t)req_buf[5] << 24) | ((uint32_t)req_buf[6] << 16) |
((uint32_t)req_buf[7] << 8) | req_buf[8])
: 0U;
server->is_downloading = true;
server->download_addr = addr;
server->download_size = size;
server->download_received = 0U;
resp_buf[0] = sid + 0x40U;
resp_buf[1] = 0x20U; /* Max block length format identifier */
*resp_len = 2U;
return SYN_OK;
}
/* 0x36 TransferData */
case SYN_KWP2000_SID_TRANSFER_DATA: {
if (!server->is_downloading || req_len < 2U) {
make_nrc(sid, SYN_KWP2000_NRC_CONDITIONS_NOT_CORRECT, resp_buf, resp_len);
return SYN_OK;
}
uint8_t block_num = req_buf[1];
const uint8_t *data = &req_buf[2];
uint16_t data_len = req_len - 2U;
uint32_t target_addr = server->download_addr + server->download_received;
if (server->memory_cb != NULL) {
if (!server->memory_cb(true, target_addr, data_len, (uint8_t *)data,
server->memory_ctx)) {
make_nrc(sid, SYN_KWP2000_NRC_CONDITIONS_NOT_CORRECT, resp_buf, resp_len);
return SYN_OK;
}
}
server->download_received += data_len;
resp_buf[0] = sid + 0x40U;
resp_buf[1] = block_num;
*resp_len = 2U;
return SYN_OK;
}
/* 0x37 RequestTransferExit */
case SYN_KWP2000_SID_REQUEST_TRANSFER_EXIT: {
server->is_downloading = false;
resp_buf[0] = sid + 0x40U;
*resp_len = 1U;
return SYN_OK;
}
/* 0x3E TesterPresent */
case SYN_KWP2000_SID_TESTER_PRESENT: {
server->s3_timer_ms = 0U;
resp_buf[0] = sid + 0x40U;
*resp_len = 1U;
return SYN_OK;
}
default:
make_nrc(sid, SYN_KWP2000_NRC_SERVICE_NOT_SUPPORTED, resp_buf, resp_len);
return SYN_OK;
}
}
void syn_kwp2000_tick(SYN_KWP2000_Server *server, uint32_t dt_ms)
{
if (server == NULL)
return;
server->s3_timer_ms += dt_ms;
/* S3 session inactivity timeout (ISO 14230-3 §5.3) */
if (server->current_session != SYN_KWP2000_SESSION_DEFAULT) {
if (server->s3_timer_ms >= SYN_KWP2000_S3_TIMEOUT_MS) {
server->current_session = SYN_KWP2000_SESSION_DEFAULT;
server->security_unlocked = false;
server->s3_timer_ms = 0U;
}
} else {
server->s3_timer_ms = 0U;
}
if (server->pending_reset_type != 0U) {
if (server->reset_wait_ms > dt_ms) {
server->reset_wait_ms -= dt_ms;
} else {
uint8_t rtype = server->pending_reset_type;
server->pending_reset_type = 0U;
if (server->reset_cb != NULL) {
server->reset_cb(rtype, server->reset_ctx);
}
}
}
}