File isotp.c¶
Go to the documentation of this file
/*******************************************************************************
* ISO-TP-C: ISO 15765-2 Protocol Implementation
*
* Project: ISO-TP-C - Embedded-Grade Refactoring & Optimization
* Description: Core ISO-TP protocol implementation with multi-frame support
*
* Author: Anton Vynohradov
* Email: avynohradov@systemfromscratch.com
*
* License: MIT License
*
* Copyright (c) 2026 Anton Vynohradov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* SPDX-License-Identifier: MIT
******************************************************************************/
/* ==============================================================================
* INCLUDES
* =============================================================================*/
#include <assert.h>
#include <stdint.h>
#include "isotp.h"
/* ==============================================================================
* DEFINES & MACROS
* =============================================================================*/
/* ==============================================================================
* PRIVATE TYPE DEFINITIONS
* =============================================================================*/
/* ==============================================================================
* PRIVATE VARIABLES (static)
* =============================================================================*/
/* ==============================================================================
* PRIVATE FUNCTION DECLARATIONS (static)
* =============================================================================*/
static uint8_t isotp_us_to_st_min(uint32_t us);
static uint32_t isotp_st_min_to_us(uint8_t st_min);
static int isotp_send_flow_control(const IsoTpLink* link, uint8_t flow_status, uint8_t block_size,
uint32_t st_min_us);
static int isotp_send_single_frame(const IsoTpLink* link, uint32_t id);
static int isotp_send_first_frame(IsoTpLink* link, uint32_t id);
static int isotp_send_consecutive_frame(IsoTpLink* link);
static int isotp_receive_single_frame(IsoTpLink* link, const IsoTpCanMessage* message, uint8_t len);
static int isotp_receive_first_frame(IsoTpLink* link, IsoTpCanMessage* message, uint8_t len);
static int isotp_receive_consecutive_frame(IsoTpLink* link, const IsoTpCanMessage* message,
uint8_t len);
static int isotp_receive_flow_control_frame(IsoTpLink* link, IsoTpCanMessage* message, uint8_t len);
/* ==============================================================================
* PRIVATE FUNCTION IMPLEMENTATIONS
* =============================================================================*/
/* st_min to microsecond */
static uint8_t isotp_us_to_st_min(uint32_t us)
{
// ISO 15765-2:2016 defines STmin encoding:
// 0x00..0x7F: value in milliseconds (0..127 ms)
// 0xF1..0xF9: value in 100 microsecond steps (100..900 us)
const uint32_t STMIN_MS_MAX = 127000; // 127 ms in us
const uint32_t STMIN_US_MIN = 100; // 100 us
const uint32_t STMIN_US_MAX = 900; // 900 us
const uint8_t STMIN_US_BASE = 0xF0; // base for 100us steps
if (us <= STMIN_MS_MAX)
{
if (us >= STMIN_US_MIN && us <= STMIN_US_MAX)
{
return (uint8_t) (STMIN_US_BASE + (us / 100));
}
else
{
return (uint8_t) (us / 1000u);
}
}
return 0;
}
/* st_min to usec */
static uint32_t isotp_st_min_to_us(uint8_t st_min)
{
// ISO 15765-2:2016 defines STmin encoding:
// 0x00..0x7F: value in milliseconds (0..127 ms)
// 0xF1..0xF9: value in 100 microsecond steps (100..900 us)
const uint8_t STMIN_MS_MAX = 0x7F; // 127 ms
const uint8_t STMIN_US_MIN_CODE = 0xF1; // 100 us
const uint8_t STMIN_US_MAX_CODE = 0xF9; // 900 us
const uint8_t STMIN_US_BASE = 0xF0; // base for 100us steps
const uint32_t US_PER_MS = 1000;
const uint32_t US_STEP = 100;
if (st_min <= STMIN_MS_MAX)
{
return st_min * US_PER_MS;
}
else if (st_min >= STMIN_US_MIN_CODE && st_min <= STMIN_US_MAX_CODE)
{
return (st_min - STMIN_US_BASE) * US_STEP;
}
return 0;
}
static int isotp_send_flow_control(const IsoTpLink* link, uint8_t flow_status, uint8_t block_size,
uint32_t st_min_us)
{
IsoTpCanMessage message;
(void) memset(&message, 0, sizeof(message));
int ret;
uint8_t size = 0;
/* setup message */
message.as.flow_control.type = ISOTP_PCI_TYPE_FLOW_CONTROL_FRAME;
message.as.flow_control.FS = flow_status;
message.as.flow_control.BS = block_size;
message.as.flow_control.STmin = isotp_us_to_st_min(st_min_us);
/* send message */
#ifdef ISO_TP_FRAME_PADDING
(void) memset(message.as.flow_control.reserve, ISO_TP_FRAME_PADDING_VALUE,
sizeof(message.as.flow_control.reserve));
size = sizeof(message);
#else
size = 3;
#endif
ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, size
#if defined(ISO_TP_USER_SEND_CAN_ARG)
,
link->user_send_can_arg
#endif
);
return ret;
}
static int isotp_send_single_frame(const IsoTpLink* link, uint32_t id)
{
(void) id; // Prevent unused variable warning
IsoTpCanMessage message;
int ret;
uint8_t size = 0;
/* multi frame message length must greater than 7 */
assert(link->send_size <= 7);
/* setup message */
message.as.single_frame.type = ISOTP_PCI_TYPE_SINGLE;
message.as.single_frame.SF_DL = (uint8_t) link->send_size;
(void) memcpy(message.as.single_frame.data, link->send_buffer, link->send_size);
/* send message */
#ifdef ISO_TP_FRAME_PADDING
(void) memset(message.as.single_frame.data + link->send_size, ISO_TP_FRAME_PADDING_VALUE,
sizeof(message.as.single_frame.data) - link->send_size);
size = sizeof(message);
#else
size = link->send_size + (uint8_t) 1;
#endif
ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, size
#if defined(ISO_TP_USER_SEND_CAN_ARG)
,
link->user_send_can_arg
#endif
);
return ret;
}
static int isotp_send_first_frame(IsoTpLink* link, uint32_t id)
{
IsoTpCanMessage message = {0};
int ret = 0;
/* multi frame message length must greater than 7 */
assert(link->send_size > 7);
if (link->send_size <= 4095)
{
/* setup 'short' message */
message.as.first_frame_short.type = ISOTP_PCI_TYPE_FIRST_FRAME;
message.as.first_frame_short.FF_DL_low = (uint8_t) link->send_size;
message.as.first_frame_short.FF_DL_high = (uint8_t) (0x0F & (link->send_size >> 8));
(void) memcpy(message.as.first_frame_short.data, link->send_buffer,
sizeof(message.as.first_frame_short.data));
/* send 'short' message */
ret = isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message)
#if defined(ISO_TP_USER_SEND_CAN_ARG)
,
link->user_send_can_arg
#endif
);
if (ISOTP_RET_OK == ret)
{
link->send_offset += sizeof(message.as.first_frame_short.data);
}
}
else
{ // ISO15765-2:2016
/* setup 'long' message */
message.as.first_frame_long.set_to_zero_high = 0;
message.as.first_frame_long.set_to_zero_low = 0;
message.as.first_frame_long.type = ISOTP_PCI_TYPE_FIRST_FRAME;
message.as.first_frame_long.FF_DL = LE32TOH(link->send_size);
(void) memcpy(message.as.first_frame_long.data, link->send_buffer,
sizeof(message.as.first_frame_long.data));
/* send 'long' message */
ret = isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message)
#if defined(ISO_TP_USER_SEND_CAN_ARG)
,
link->user_send_can_arg
#endif
);
if (ISOTP_RET_OK == ret)
{
link->send_offset += sizeof(message.as.first_frame_long.data);
}
}
link->send_sn = 1;
return ret;
}
static int isotp_send_consecutive_frame(IsoTpLink* link)
{
IsoTpCanMessage message;
uint32_t data_length;
int ret;
uint8_t size = 0;
/* multi frame message length must greater than 7 */
assert(link->send_size > 7);
/* setup message */
message.as.consecutive_frame.type = ISOTP_PCI_TYPE_CONSECUTIVE_FRAME;
message.as.consecutive_frame.SN = link->send_sn;
data_length = link->send_size - link->send_offset;
if (data_length > sizeof(message.as.consecutive_frame.data))
{
data_length = sizeof(message.as.consecutive_frame.data);
}
(void) memcpy(message.as.consecutive_frame.data, link->send_buffer + link->send_offset,
data_length);
/* send message */
#ifdef ISO_TP_FRAME_PADDING
(void) memset(message.as.consecutive_frame.data + data_length, ISO_TP_FRAME_PADDING_VALUE,
sizeof(message.as.consecutive_frame.data) - data_length);
size = sizeof(message);
#else
size = data_length + 1;
#endif
ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, size
#if defined(ISO_TP_USER_SEND_CAN_ARG)
,
link->user_send_can_arg
#endif
);
if (ISOTP_RET_OK == ret)
{
link->send_offset += data_length;
if (++(link->send_sn) > 0x0F)
{
link->send_sn = 0;
}
}
return ret;
}
static int isotp_receive_single_frame(IsoTpLink* link, const IsoTpCanMessage* message, uint8_t len)
{
/* check data length */
if ((0 == message->as.single_frame.SF_DL) || (message->as.single_frame.SF_DL > (len - 1)))
{
isotp_user_debug("Single-frame length too small.");
return ISOTP_RET_LENGTH;
}
/* copying data */
(void) memcpy(link->receive_buffer, message->as.single_frame.data,
message->as.single_frame.SF_DL);
link->receive_size = message->as.single_frame.SF_DL;
return ISOTP_RET_OK;
}
static int isotp_receive_first_frame(IsoTpLink* link, IsoTpCanMessage* message, uint8_t len)
{
uint8_t is_long_packet = 0;
uint32_t payload_length;
if (8 != len)
{
isotp_user_debug("First frame should be 8 bytes in length.");
return ISOTP_RET_LENGTH;
}
/* check data length */
payload_length = message->as.first_frame_short.FF_DL_high;
payload_length = (payload_length << 8) + message->as.first_frame_short.FF_DL_low;
/* if length is ZERO we get a long message > 4095bytes of payload */
if (payload_length == 0)
{
is_long_packet = 1;
payload_length = LE32TOH(message->as.first_frame_long.FF_DL);
}
/* should not use multiple frame transmition */
if (payload_length <= 7)
{
isotp_user_debug("Should not use multiple frame transmission.");
return ISOTP_RET_LENGTH;
}
if (payload_length > link->receive_buf_size)
{
isotp_user_debug("Multi-frame response too large for receiving buffer.");
return ISOTP_RET_OVERFLOW;
}
/* copying data */
if (is_long_packet)
{
(void) memcpy(link->receive_buffer, message->as.first_frame_long.data,
sizeof(message->as.first_frame_long.data));
link->receive_offset = sizeof(message->as.first_frame_long.data);
}
else
{
(void) memcpy(link->receive_buffer, message->as.first_frame_short.data,
sizeof(message->as.first_frame_short.data));
link->receive_offset = sizeof(message->as.first_frame_short.data);
}
link->receive_size = payload_length;
link->receive_sn = 1;
return ISOTP_RET_OK;
}
static int isotp_receive_consecutive_frame(IsoTpLink* link, const IsoTpCanMessage* message,
uint8_t len)
{
uint32_t remaining_bytes;
/* check sn */
if (link->receive_sn != message->as.consecutive_frame.SN)
{
return ISOTP_RET_WRONG_SN;
}
/* check data length */
remaining_bytes = link->receive_size - link->receive_offset;
if (remaining_bytes > sizeof(message->as.consecutive_frame.data))
{
remaining_bytes = sizeof(message->as.consecutive_frame.data);
}
if (remaining_bytes > (uint32_t) (len - 1))
{
isotp_user_debug("Consecutive frame too short.");
return ISOTP_RET_LENGTH;
}
/* copying data */
(void) memcpy(link->receive_buffer + link->receive_offset, message->as.consecutive_frame.data,
remaining_bytes);
link->receive_offset += remaining_bytes;
if (++(link->receive_sn) > 0x0F)
{
link->receive_sn = 0;
}
return ISOTP_RET_OK;
}
static int isotp_receive_flow_control_frame(IsoTpLink* link, IsoTpCanMessage* message, uint8_t len)
{
/* unused args */
(void) link;
(void) message;
/* check message length */
if (len < 3)
{
isotp_user_debug("Flow control frame too short.");
return ISOTP_RET_LENGTH;
}
return ISOTP_RET_OK;
}
/* ==============================================================================
* PUBLIC FUNCTION IMPLEMENTATIONS
* =============================================================================*/
int isotp_send(IsoTpLink* link, const uint8_t payload[], uint32_t size)
{
return isotp_send_with_id(link, link->send_arbitration_id, payload, size);
}
int isotp_send_with_id(IsoTpLink* link, uint32_t id, const uint8_t payload[], uint32_t size)
{
int ret;
if (link == 0x0)
{
isotp_user_debug("Link is null!");
return ISOTP_RET_ERROR;
}
if (size > link->send_buf_size)
{
isotp_user_debug("Message size too large. Increase ISO_TP_MAX_MESSAGE_SIZE to set "
"a larger buffer\n");
char message[ISOTP_MAX_ERROR_MSG_SIZE] = {0};
int32_t writtenChars = snprintf(&message[0], ISOTP_MAX_ERROR_MSG_SIZE,
"Attempted to send %u bytes; max size is %u!\n",
(unsigned int) size, (unsigned int) link->send_buf_size);
assert(writtenChars <= ISOTP_MAX_ERROR_MSG_SIZE);
(void) writtenChars;
isotp_user_debug(message);
return ISOTP_RET_OVERFLOW;
}
if (ISOTP_SEND_STATUS_INPROGRESS == link->send_status)
{
isotp_user_debug("Abort previous message, transmission in progress.\n");
return ISOTP_RET_INPROGRESS;
}
/* copy into local buffer */
link->send_size = size;
link->send_offset = 0;
(void) memcpy(link->send_buffer, payload, size);
if (link->send_size < 8)
{
/* send single frame */
ret = isotp_send_single_frame(link, id);
#ifdef ISO_TP_TRANSMIT_COMPLETE_CALLBACK
if (ret == ISOTP_RET_OK && link->tx_done_cb)
{
link->tx_done_cb(link, link->send_size, link->tx_done_cb_arg);
}
#endif
}
else
{
/* send multi-frame */
ret = isotp_send_first_frame(link, id);
/* init multi-frame control flags */
if (ISOTP_RET_OK == ret)
{
link->send_bs_remain = 0;
link->send_st_min_us = 0;
link->send_wtf_count = 0;
link->send_timer_st = isotp_user_get_us();
link->send_timer_bs = isotp_user_get_us() + link->param_n_bs_us;
link->send_protocol_result = ISOTP_PROTOCOL_RESULT_OK;
link->send_status = ISOTP_SEND_STATUS_INPROGRESS;
}
}
return ret;
}
void isotp_on_can_message(IsoTpLink* link, const uint8_t* data, uint8_t len)
{
IsoTpCanMessage message;
int ret;
if (len < 2 || len > 8)
{
return;
}
memcpy(message.as.data_array.ptr, data, len);
memset(message.as.data_array.ptr + len, 0, sizeof(message.as.data_array.ptr) - len);
switch (message.as.common.type)
{
case ISOTP_PCI_TYPE_SINGLE:
{
/* update protocol result */
if (ISOTP_RECEIVE_STATUS_INPROGRESS == link->receive_status)
{
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_UNEXP_PDU;
}
else
{
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_OK;
}
/* handle message */
ret = isotp_receive_single_frame(link, &message, len);
if (ISOTP_RET_OK == ret)
{
/* change status */
link->receive_status = ISOTP_RECEIVE_STATUS_FULL;
}
break;
}
case ISOTP_PCI_TYPE_FIRST_FRAME:
{
/* update protocol result */
if (ISOTP_RECEIVE_STATUS_INPROGRESS == link->receive_status)
{
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_UNEXP_PDU;
}
else
{
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_OK;
}
/* handle message */
ret = isotp_receive_first_frame(link, &message, len);
/* if overflow happened */
if (ISOTP_RET_OVERFLOW == ret)
{
/* update protocol result */
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_BUFFER_OVFLW;
/* change status */
link->receive_status = ISOTP_RECEIVE_STATUS_IDLE;
/* send error message */
isotp_send_flow_control(link, PCI_FLOW_STATUS_OVERFLOW, 0, 0);
break;
}
/* if receive successful */
if (ISOTP_RET_OK == ret)
{
/* change status */
link->receive_status = ISOTP_RECEIVE_STATUS_INPROGRESS;
/* send fc frame */
link->receive_bs_count = link->param_block_size;
isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE, link->receive_bs_count,
link->param_st_min_us);
/* refresh timer cs */
link->receive_timer_cr = isotp_user_get_us() + link->param_n_cr_us;
}
break;
}
case ISOTP_PCI_TYPE_CONSECUTIVE_FRAME:
{
/* check if in receiving status */
if (ISOTP_RECEIVE_STATUS_INPROGRESS != link->receive_status)
{
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_UNEXP_PDU;
break;
}
/* handle message */
ret = isotp_receive_consecutive_frame(link, &message, len);
/* if wrong sn */
if (ISOTP_RET_WRONG_SN == ret)
{
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_WRONG_SN;
link->receive_status = ISOTP_RECEIVE_STATUS_IDLE;
break;
}
/* if success */
if (ISOTP_RET_OK == ret)
{
/* refresh timer cs */
link->receive_timer_cr = isotp_user_get_us() + link->param_n_cr_us;
/* receive finished */
if (link->receive_offset >= link->receive_size)
{
link->receive_status = ISOTP_RECEIVE_STATUS_FULL;
}
else
{
/* send fc when bs reaches limit */
if (link->receive_bs_count > 0 && 0 == --link->receive_bs_count)
{
link->receive_bs_count = link->param_block_size;
isotp_send_flow_control(link, PCI_FLOW_STATUS_CONTINUE,
link->receive_bs_count, link->param_st_min_us);
}
}
}
break;
}
case ISOTP_PCI_TYPE_FLOW_CONTROL_FRAME:
/* handle fc frame only when sending in progress */
if (ISOTP_SEND_STATUS_INPROGRESS != link->send_status)
{
break;
}
/* handle message */
ret = isotp_receive_flow_control_frame(link, &message, len);
if (ISOTP_RET_OK == ret)
{
/* refresh bs timer */
link->send_timer_bs = isotp_user_get_us() + link->param_n_bs_us;
/* overflow */
if (PCI_FLOW_STATUS_OVERFLOW == message.as.flow_control.FS)
{
link->send_protocol_result = ISOTP_PROTOCOL_RESULT_BUFFER_OVFLW;
link->send_status = ISOTP_SEND_STATUS_ERROR;
}
/* wait */
else if (PCI_FLOW_STATUS_WAIT == message.as.flow_control.FS)
{
link->send_wtf_count += 1;
/* wait exceed allowed count */
if (link->send_wtf_count > ISO_TP_MAX_WFT_NUMBER)
{
link->send_protocol_result = ISOTP_PROTOCOL_RESULT_WFT_OVRN;
link->send_status = ISOTP_SEND_STATUS_ERROR;
}
}
/* permit send */
else if (PCI_FLOW_STATUS_CONTINUE == message.as.flow_control.FS)
{
if (0 == message.as.flow_control.BS)
{
link->send_bs_remain = ISOTP_INVALID_BS;
}
else
{
link->send_bs_remain = message.as.flow_control.BS;
}
uint32_t message_st_min_us = isotp_st_min_to_us(message.as.flow_control.STmin);
link->send_st_min_us =
message_st_min_us > ISO_TP_DEFAULT_ST_MIN_US ?
message_st_min_us :
ISO_TP_DEFAULT_ST_MIN_US; // prefer as much st_min as possible
// for stability?
link->send_wtf_count = 0;
}
}
break;
default:
break;
};
#ifdef ISO_TP_RECEIVE_COMPLETE_CALLBACK
/* Notify user via callback if registered */
if (link->receive_status == ISOTP_RECEIVE_STATUS_FULL && link->rx_done_cb != NULL)
{
link->rx_done_cb(link, link->receive_buffer, link->receive_size, link->rx_done_cb_arg);
link->receive_status = ISOTP_RECEIVE_STATUS_IDLE;
}
#endif
return;
}
int isotp_receive(IsoTpLink* link, uint8_t* payload, const uint32_t payload_size,
uint32_t* out_size)
{
uint32_t copylen;
#ifdef ISO_TP_RECEIVE_COMPLETE_CALLBACK
/* If callback is registered, isotp_receive should not be used */
if (link->rx_done_cb != NULL)
{
return ISOTP_RET_ERROR; /* Callback mode active, use callback instead */
}
#endif
if (ISOTP_RECEIVE_STATUS_FULL != link->receive_status)
{
return ISOTP_RET_NO_DATA;
}
copylen = link->receive_size;
if (copylen > payload_size)
{
copylen = payload_size;
}
memcpy(payload, link->receive_buffer, copylen);
*out_size = copylen;
link->receive_status = ISOTP_RECEIVE_STATUS_IDLE;
return ISOTP_RET_OK;
}
void isotp_init_link(IsoTpLink* link, uint32_t sendid, uint8_t* sendbuf, uint32_t sendbufsize,
uint8_t* recvbuf, uint32_t recvbufsize)
{
memset(link, 0, sizeof(*link));
link->receive_status = ISOTP_RECEIVE_STATUS_IDLE;
link->send_status = ISOTP_SEND_STATUS_IDLE;
link->send_arbitration_id = sendid;
link->send_buffer = sendbuf;
link->send_buf_size = sendbufsize;
link->receive_buffer = recvbuf;
link->receive_buf_size = recvbufsize;
link->param_n_bs_us = ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US;
link->param_n_cr_us = ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US;
link->param_st_min_us = ISO_TP_DEFAULT_ST_MIN_US;
link->param_block_size = ISO_TP_DEFAULT_BLOCK_SIZE;
#ifdef ISO_TP_TRANSMIT_COMPLETE_CALLBACK
link->tx_done_cb = NULL;
link->tx_done_cb_arg = NULL;
#endif
#ifdef ISO_TP_RECEIVE_COMPLETE_CALLBACK
link->rx_done_cb = NULL;
link->rx_done_cb_arg = NULL;
#endif
return;
}
void isotp_destroy_link(IsoTpLink* link)
{
if (link == NULL)
{
return;
}
// Clear callbacks
#ifdef ISO_TP_TRANSMIT_COMPLETE_CALLBACK
link->tx_done_cb = NULL;
link->tx_done_cb_arg = NULL;
#endif
#ifdef ISO_TP_RECEIVE_COMPLETE_CALLBACK
link->rx_done_cb = NULL;
link->rx_done_cb_arg = NULL;
#endif
// Reset link state (optional, but good practice)
memset(link, 0, sizeof(IsoTpLink));
}
void isotp_set_timeouts(IsoTpLink* link, uint32_t n_bs_us, uint32_t n_cr_us)
{
if (link != NULL)
{
link->param_n_bs_us = n_bs_us;
link->param_n_cr_us = n_cr_us;
}
}
void isotp_set_fc_params(IsoTpLink* link, uint8_t block_size, uint32_t st_min_us)
{
if (link != NULL)
{
link->param_block_size = block_size;
link->param_st_min_us = st_min_us;
}
}
void isotp_poll(IsoTpLink* link)
{
int ret = 0;
/* only polling when operation in progress */
if (ISOTP_SEND_STATUS_INPROGRESS == link->send_status)
{
/* continue send data */
if (/* send data if bs_remain is invalid or bs_remain large than zero */
(ISOTP_INVALID_BS == link->send_bs_remain || link->send_bs_remain > 0) &&
/* and if st_min is zero or go beyond interval time */
(0 == link->send_st_min_us || IsoTpTimeAfter(isotp_user_get_us(), link->send_timer_st)))
{
ret = isotp_send_consecutive_frame(link);
if (ISOTP_RET_OK == ret)
{
if (ISOTP_INVALID_BS != link->send_bs_remain)
{
link->send_bs_remain -= 1;
}
link->send_timer_bs = isotp_user_get_us() + link->param_n_bs_us;
link->send_timer_st = isotp_user_get_us() + link->send_st_min_us;
/* check if send finish */
if (link->send_offset >= link->send_size)
{
link->send_status = ISOTP_SEND_STATUS_IDLE;
#ifdef ISO_TP_TRANSMIT_COMPLETE_CALLBACK
if (link->tx_done_cb != NULL)
{
link->tx_done_cb(link, link->send_size, link->tx_done_cb_arg);
}
#endif
}
}
else if (ISOTP_RET_NOSPACE == ret)
{
/* shim reported that it isn't able to send a frame at present, retry on
* next call
*/
}
else
{
link->send_status = ISOTP_SEND_STATUS_ERROR;
}
}
/* check timeout */
if (IsoTpTimeAfter(isotp_user_get_us(), link->send_timer_bs))
{
link->send_protocol_result = ISOTP_PROTOCOL_RESULT_TIMEOUT_BS;
link->send_status = ISOTP_SEND_STATUS_ERROR;
}
}
/* only polling when operation in progress */
if (ISOTP_RECEIVE_STATUS_INPROGRESS == link->receive_status)
{
/* check timeout */
if ((link->receive_timer_cr > 0)
&& IsoTpTimeAfter(isotp_user_get_us(), link->receive_timer_cr))
{
link->receive_protocol_result = ISOTP_PROTOCOL_RESULT_TIMEOUT_CR;
link->receive_status = ISOTP_RECEIVE_STATUS_IDLE;
}
}
return;
}
#ifdef ISO_TP_TRANSMIT_COMPLETE_CALLBACK
void isotp_set_tx_done_cb(IsoTpLink* link, isotp_tx_done_cb cb, void* arg)
{
if (link != NULL)
{
link->tx_done_cb = cb;
link->tx_done_cb_arg = arg;
}
}
#endif
#ifdef ISO_TP_RECEIVE_COMPLETE_CALLBACK
void isotp_set_rx_done_cb(IsoTpLink* link, isotp_rx_done_cb cb, void* arg)
{
if (link != NULL)
{
link->rx_done_cb = cb;
link->rx_done_cb_arg = arg;
}
}
#endif