ISOTP-C
A platform-agnostic ISOTP implementation in C.
isotp.c
Go to the documentation of this file.
1#include <stdint.h>
2#include "assert.h"
3#include "isotp.h"
4
5///////////////////////////////////////////////////////
6/// STATIC FUNCTIONS ///
7///////////////////////////////////////////////////////
8
9/* st_min to microsecond */
10static uint8_t isotp_us_to_st_min(uint32_t us) {
11 if (us <= 127000) {
12 return us / 1000;
13 } else if (us >= 100 && us <= 900) {
14 return 0xF0 + (us / 100);
15 }
16 return 0;
17}
18
19/* st_min to usec */
20static uint32_t isotp_st_min_to_us(uint8_t st_min) {
21 if (st_min <= 0x7F) {
22 return st_min * 1000;
23 } else if (st_min >= 0xF1 && st_min <= 0xF9) {
24 return (st_min - 0xF0) * 100;
25 }
26 return 0;
27}
28
29static int isotp_send_flow_control(IsoTpLink* link, uint8_t flow_status, uint8_t block_size, uint32_t st_min_us) {
30
31 IsoTpCanMessage message;
32 int ret;
33
34 /* setup message */
36 message.as.flow_control.FS = flow_status;
37 message.as.flow_control.BS = block_size;
38 message.as.flow_control.STmin = isotp_us_to_st_min(st_min_us);
39
40 /* send message */
41#ifdef ISO_TP_FRAME_PADDING
42 (void) memset(message.as.flow_control.reserve, ISO_TP_FRAME_PADDING_VALUE, sizeof(message.as.flow_control.reserve));
43 ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, sizeof(message));
44#else
46 message.as.data_array.ptr,
47 3);
48#endif
49
50 return ret;
51}
52
53static int isotp_send_single_frame(IsoTpLink* link, uint32_t id) {
54
55 IsoTpCanMessage message;
56 int ret;
57
58 /* multi frame message length must greater than 7 */
59 assert(link->send_size <= 7);
60
61 /* setup message */
62 message.as.single_frame.type = ISOTP_PCI_TYPE_SINGLE;
63 message.as.single_frame.SF_DL = (uint8_t) link->send_size;
64 (void) memcpy(message.as.single_frame.data, link->send_buffer, link->send_size);
65
66 /* send message */
67#ifdef ISO_TP_FRAME_PADDING
68 (void) memset(message.as.single_frame.data + link->send_size, ISO_TP_FRAME_PADDING_VALUE, sizeof(message.as.single_frame.data) - link->send_size);
69 ret = isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message));
70#else
72 message.as.data_array.ptr,
73 link->send_size + 1);
74#endif
75
76 return ret;
77}
78
79static int isotp_send_first_frame(IsoTpLink* link, uint32_t id) {
80
81 IsoTpCanMessage message;
82 int ret;
83
84 /* multi frame message length must greater than 7 */
85 assert(link->send_size > 7);
86
87 /* setup message */
89 message.as.first_frame.FF_DL_low = (uint8_t) link->send_size;
90 message.as.first_frame.FF_DL_high = (uint8_t) (0x0F & (link->send_size >> 8));
91 (void) memcpy(message.as.first_frame.data, link->send_buffer, sizeof(message.as.first_frame.data));
92
93 /* send message */
94 ret = isotp_user_send_can(id, message.as.data_array.ptr, sizeof(message));
95 if (ISOTP_RET_OK == ret) {
96 link->send_offset += sizeof(message.as.first_frame.data);
97 link->send_sn = 1;
98 }
99
100 return ret;
101}
102
103static int isotp_send_consecutive_frame(IsoTpLink* link) {
104
105 IsoTpCanMessage message;
106 uint16_t data_length;
107 int ret;
108
109 /* multi frame message length must greater than 7 */
110 assert(link->send_size > 7);
111
112 /* setup message */
114 message.as.consecutive_frame.SN = link->send_sn;
115 data_length = link->send_size - link->send_offset;
116 if (data_length > sizeof(message.as.consecutive_frame.data)) {
117 data_length = sizeof(message.as.consecutive_frame.data);
118 }
119 (void) memcpy(message.as.consecutive_frame.data, link->send_buffer + link->send_offset, data_length);
120
121 /* send message */
122#ifdef ISO_TP_FRAME_PADDING
123 (void) memset(message.as.consecutive_frame.data + data_length, ISO_TP_FRAME_PADDING_VALUE, sizeof(message.as.consecutive_frame.data) - data_length);
124 ret = isotp_user_send_can(link->send_arbitration_id, message.as.data_array.ptr, sizeof(message));
125#else
127 message.as.data_array.ptr,
128 data_length + 1);
129#endif
130 if (ISOTP_RET_OK == ret) {
131 link->send_offset += data_length;
132 if (++(link->send_sn) > 0x0F) {
133 link->send_sn = 0;
134 }
135 }
136
137 return ret;
138}
139
140static int isotp_receive_single_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) {
141 /* check data length */
142 if ((0 == message->as.single_frame.SF_DL) || (message->as.single_frame.SF_DL > (len - 1))) {
143 isotp_user_debug("Single-frame length too small.");
144 return ISOTP_RET_LENGTH;
145 }
146
147 /* copying data */
148 (void) memcpy(link->receive_buffer, message->as.single_frame.data, message->as.single_frame.SF_DL);
149 link->receive_size = message->as.single_frame.SF_DL;
150
151 return ISOTP_RET_OK;
152}
153
154static int isotp_receive_first_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) {
155 uint16_t payload_length;
156
157 if (8 != len) {
158 isotp_user_debug("First frame should be 8 bytes in length.");
159 return ISOTP_RET_LENGTH;
160 }
161
162 /* check data length */
163 payload_length = message->as.first_frame.FF_DL_high;
164 payload_length = (payload_length << 8) + message->as.first_frame.FF_DL_low;
165
166 /* should not use multiple frame transmition */
167 if (payload_length <= 7) {
168 isotp_user_debug("Should not use multiple frame transmission.");
169 return ISOTP_RET_LENGTH;
170 }
171
172 if (payload_length > link->receive_buf_size) {
173 isotp_user_debug("Multi-frame response too large for receiving buffer.");
174 return ISOTP_RET_OVERFLOW;
175 }
176
177 /* copying data */
178 (void) memcpy(link->receive_buffer, message->as.first_frame.data, sizeof(message->as.first_frame.data));
179 link->receive_size = payload_length;
180 link->receive_offset = sizeof(message->as.first_frame.data);
181 link->receive_sn = 1;
182
183 return ISOTP_RET_OK;
184}
185
186static int isotp_receive_consecutive_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) {
187 uint16_t remaining_bytes;
188
189 /* check sn */
190 if (link->receive_sn != message->as.consecutive_frame.SN) {
191 return ISOTP_RET_WRONG_SN;
192 }
193
194 /* check data length */
195 remaining_bytes = link->receive_size - link->receive_offset;
196 if (remaining_bytes > sizeof(message->as.consecutive_frame.data)) {
197 remaining_bytes = sizeof(message->as.consecutive_frame.data);
198 }
199 if (remaining_bytes > len - 1) {
200 isotp_user_debug("Consecutive frame too short.");
201 return ISOTP_RET_LENGTH;
202 }
203
204 /* copying data */
205 (void) memcpy(link->receive_buffer + link->receive_offset, message->as.consecutive_frame.data, remaining_bytes);
206
207 link->receive_offset += remaining_bytes;
208 if (++(link->receive_sn) > 0x0F) {
209 link->receive_sn = 0;
210 }
211
212 return ISOTP_RET_OK;
213}
214
215static int isotp_receive_flow_control_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len) {
216 /* check message length */
217 if (len < 3) {
218 isotp_user_debug("Flow control frame too short.");
219 return ISOTP_RET_LENGTH;
220 }
221
222 return ISOTP_RET_OK;
223}
224
225///////////////////////////////////////////////////////
226/// PUBLIC FUNCTIONS ///
227///////////////////////////////////////////////////////
228
229int isotp_send(IsoTpLink *link, const uint8_t payload[], uint16_t size) {
230 return isotp_send_with_id(link, link->send_arbitration_id, payload, size);
231}
232
233int isotp_send_with_id(IsoTpLink *link, uint32_t id, const uint8_t payload[], uint16_t size) {
234 int ret;
235
236 if (link == 0x0) {
237 isotp_user_debug("Link is null!");
238 return ISOTP_RET_ERROR;
239 }
240
241 if (size > link->send_buf_size) {
242 isotp_user_debug("Message size too large. Increase ISO_TP_MAX_MESSAGE_SIZE to set a larger buffer\n");
243 char message[128];
244 sprintf(&message[0], "Attempted to send %d bytes; max size is %d!\n", size, link->send_buf_size);
245 isotp_user_debug(message);
246 return ISOTP_RET_OVERFLOW;
247 }
248
250 isotp_user_debug("Abort previous message, transmission in progress.\n");
252 }
253
254 /* copy into local buffer */
255 link->send_size = size;
256 link->send_offset = 0;
257 (void) memcpy(link->send_buffer, payload, size);
258
259 if (link->send_size < 8) {
260 /* send single frame */
261 ret = isotp_send_single_frame(link, id);
262 } else {
263 /* send multi-frame */
264 ret = isotp_send_first_frame(link, id);
265
266 /* init multi-frame control flags */
267 if (ISOTP_RET_OK == ret) {
268 link->send_bs_remain = 0;
269 link->send_st_min_us = 0;
270 link->send_wtf_count = 0;
275 }
276 }
277
278 return ret;
279}
280
281void isotp_on_can_message(IsoTpLink *link, uint8_t *data, uint8_t len) {
282 IsoTpCanMessage message;
283 int ret;
284
285 if (len < 2 || len > 8) {
286 return;
287 }
288
289 memcpy(message.as.data_array.ptr, data, len);
290 memset(message.as.data_array.ptr + len, 0, sizeof(message.as.data_array.ptr) - len);
291
292 switch (message.as.common.type) {
294 /* update protocol result */
297 } else {
299 }
300
301 /* handle message */
302 ret = isotp_receive_single_frame(link, &message, len);
303
304 if (ISOTP_RET_OK == ret) {
305 /* change status */
307 }
308 break;
309 }
311 /* update protocol result */
314 } else {
316 }
317
318 /* handle message */
319 ret = isotp_receive_first_frame(link, &message, len);
320
321 /* if overflow happened */
322 if (ISOTP_RET_OVERFLOW == ret) {
323 /* update protocol result */
325 /* change status */
327 /* send error message */
329 break;
330 }
331
332 /* if receive successful */
333 if (ISOTP_RET_OK == ret) {
334 /* change status */
336 /* send fc frame */
339 /* refresh timer cs */
341 }
342
343 break;
344 }
346 /* check if in receiving status */
349 break;
350 }
351
352 /* handle message */
353 ret = isotp_receive_consecutive_frame(link, &message, len);
354
355 /* if wrong sn */
356 if (ISOTP_RET_WRONG_SN == ret) {
359 break;
360 }
361
362 /* if success */
363 if (ISOTP_RET_OK == ret) {
364 /* refresh timer cs */
366
367 /* receive finished */
368 if (link->receive_offset >= link->receive_size) {
370 } else {
371 /* send fc when bs reaches limit */
372 if (0 == --link->receive_bs_count) {
375 }
376 }
377 }
378
379 break;
380 }
382 /* handle fc frame only when sending in progress */
384 break;
385 }
386
387 /* handle message */
388 ret = isotp_receive_flow_control_frame(link, &message, len);
389
390 if (ISOTP_RET_OK == ret) {
391 /* refresh bs timer */
393
394 /* overflow */
395 if (PCI_FLOW_STATUS_OVERFLOW == message.as.flow_control.FS) {
398 }
399
400 /* wait */
401 else if (PCI_FLOW_STATUS_WAIT == message.as.flow_control.FS) {
402 link->send_wtf_count += 1;
403 /* wait exceed allowed count */
407 }
408 }
409
410 /* permit send */
411 else if (PCI_FLOW_STATUS_CONTINUE == message.as.flow_control.FS) {
412 if (0 == message.as.flow_control.BS) {
414 } else {
415 link->send_bs_remain = message.as.flow_control.BS;
416 }
417 uint32_t message_st_min_us = isotp_st_min_to_us(message.as.flow_control.STmin);
418 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?
419 link->send_wtf_count = 0;
420 }
421 }
422 break;
423 default:
424 break;
425 };
426
427 return;
428}
429
430int isotp_receive(IsoTpLink *link, uint8_t *payload, const uint16_t payload_size, uint16_t *out_size) {
431 uint16_t copylen;
432
434 return ISOTP_RET_NO_DATA;
435 }
436
437 copylen = link->receive_size;
438 if (copylen > payload_size) {
439 copylen = payload_size;
440 }
441
442 memcpy(payload, link->receive_buffer, copylen);
443 *out_size = copylen;
444
446
447 return ISOTP_RET_OK;
448}
449
450void isotp_init_link(IsoTpLink *link, uint32_t sendid, uint8_t *sendbuf, uint16_t sendbufsize, uint8_t *recvbuf, uint16_t recvbufsize) {
451 memset(link, 0, sizeof(*link));
454 link->send_arbitration_id = sendid;
455 link->send_buffer = sendbuf;
456 link->send_buf_size = sendbufsize;
457 link->receive_buffer = recvbuf;
458 link->receive_buf_size = recvbufsize;
459
460 return;
461}
462
463void isotp_poll(IsoTpLink *link) {
464 int ret;
465
466 /* only polling when operation in progress */
468
469 /* continue send data */
470 if (/* send data if bs_remain is invalid or bs_remain large than zero */
472 /* and if st_min is zero or go beyond interval time */
474
476 if (ISOTP_RET_OK == ret) {
478 link->send_bs_remain -= 1;
479 }
482
483 /* check if send finish */
484 if (link->send_offset >= link->send_size) {
486 }
487 } else if (ISOTP_RET_NOSPACE == ret) {
488 /* shim reported that it isn't able to send a frame at present, retry on next call */
489 } else {
491 }
492 }
493
494 /* check timeout */
498 }
499 }
500
501 /* only polling when operation in progress */
503
504 /* check timeout */
508 }
509 }
510
511 return;
512}
void isotp_on_can_message(IsoTpLink *link, uint8_t *data, uint8_t len)
Handles incoming CAN messages. Determines whether an incoming message is a valid ISO-TP frame or not ...
Definition: isotp.c:281
static int isotp_send_single_frame(IsoTpLink *link, uint32_t id)
Definition: isotp.c:53
static int isotp_send_consecutive_frame(IsoTpLink *link)
Definition: isotp.c:103
static int isotp_receive_first_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len)
Definition: isotp.c:154
int isotp_receive(IsoTpLink *link, uint8_t *payload, const uint16_t payload_size, uint16_t *out_size)
Receives and parses the received data and copies the parsed data in to the internal buffer.
Definition: isotp.c:430
static int isotp_receive_consecutive_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len)
Definition: isotp.c:186
static int isotp_receive_flow_control_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len)
Definition: isotp.c:215
static int isotp_send_flow_control(IsoTpLink *link, uint8_t flow_status, uint8_t block_size, uint32_t st_min_us)
Definition: isotp.c:29
void isotp_init_link(IsoTpLink *link, uint32_t sendid, uint8_t *sendbuf, uint16_t sendbufsize, uint8_t *recvbuf, uint16_t recvbufsize)
Initialises the ISO-TP library.
Definition: isotp.c:450
void isotp_poll(IsoTpLink *link)
Polling function; call this function periodically to handle timeouts, send consecutive frames,...
Definition: isotp.c:463
static uint8_t isotp_us_to_st_min(uint32_t us)
STATIC FUNCTIONS ///.
Definition: isotp.c:10
int isotp_send_with_id(IsoTpLink *link, uint32_t id, const uint8_t payload[], uint16_t size)
See isotp_send, with the exception that this function is used only for functional addressing.
Definition: isotp.c:233
static uint32_t isotp_st_min_to_us(uint8_t st_min)
Definition: isotp.c:20
static int isotp_send_first_frame(IsoTpLink *link, uint32_t id)
Definition: isotp.c:79
int isotp_send(IsoTpLink *link, const uint8_t payload[], uint16_t size)
PUBLIC FUNCTIONS ///.
Definition: isotp.c:229
static int isotp_receive_single_frame(IsoTpLink *link, IsoTpCanMessage *message, uint8_t len)
Definition: isotp.c:140
#define ISO_TP_MAX_WFT_NUMBER
Definition: isotp_config.h:17
#define ISO_TP_DEFAULT_ST_MIN_US
Definition: isotp_config.h:12
#define ISO_TP_DEFAULT_RESPONSE_TIMEOUT_US
Definition: isotp_config.h:22
#define ISO_TP_DEFAULT_BLOCK_SIZE
Definition: isotp_config.h:7
@ ISOTP_RECEIVE_STATUS_FULL
Definition: isotp_defines.h:64
@ ISOTP_RECEIVE_STATUS_IDLE
Definition: isotp_defines.h:62
@ ISOTP_RECEIVE_STATUS_INPROGRESS
Definition: isotp_defines.h:63
#define ISOTP_RET_NOSPACE
Definition: isotp_defines.h:45
#define ISOTP_PROTOCOL_RESULT_BUFFER_OVFLW
#define ISOTP_PROTOCOL_RESULT_UNEXP_PDU
#define ISOTP_INVALID_BS
Definition: isotp_defines.h:51
#define ISOTP_RET_NO_DATA
Definition: isotp_defines.h:42
#define ISOTP_PROTOCOL_RESULT_OK
#define ISOTP_RET_OVERFLOW
Definition: isotp_defines.h:40
#define ISOTP_PROTOCOL_RESULT_TIMEOUT_CR
@ ISOTP_SEND_STATUS_ERROR
Definition: isotp_defines.h:57
@ ISOTP_SEND_STATUS_IDLE
Definition: isotp_defines.h:55
@ ISOTP_SEND_STATUS_INPROGRESS
Definition: isotp_defines.h:56
#define ISOTP_PROTOCOL_RESULT_TIMEOUT_BS
#define ISOTP_RET_WRONG_SN
Definition: isotp_defines.h:41
#define ISOTP_PROTOCOL_RESULT_WFT_OVRN
#define ISOTP_RET_LENGTH
Definition: isotp_defines.h:44
#define ISOTP_RET_INPROGRESS
Definition: isotp_defines.h:39
@ PCI_FLOW_STATUS_OVERFLOW
@ PCI_FLOW_STATUS_WAIT
@ PCI_FLOW_STATUS_CONTINUE
#define ISOTP_RET_ERROR
Definition: isotp_defines.h:38
#define ISOTP_RET_OK
Definition: isotp_defines.h:37
@ TSOTP_PCI_TYPE_CONSECUTIVE_FRAME
@ ISOTP_PCI_TYPE_SINGLE
@ ISOTP_PCI_TYPE_FIRST_FRAME
@ ISOTP_PCI_TYPE_FLOW_CONTROL_FRAME
#define IsoTpTimeAfter(a, b)
Definition: isotp_defines.h:48
#define ISOTP_PROTOCOL_RESULT_WRONG_SN
void isotp_user_debug(const char *message,...)
user implemented, print debug message
int isotp_user_send_can(const uint32_t arbitration_id, const uint8_t *data, const uint8_t size)
user implemented, send can message. should return ISOTP_RET_OK when success.
uint32_t isotp_user_get_us(void)
user implemented, gets the amount of time passed since the last call in microseconds
IsoTpDataArray data_array
IsoTpFirstFrame first_frame
IsoTpPciType common
IsoTpConsecutiveFrame consecutive_frame
IsoTpSingleFrame single_frame
IsoTpFlowControl flow_control
uint8_t ptr[8]