-
Notifications
You must be signed in to change notification settings - Fork 1
/
isotp.c
169 lines (131 loc) · 3.64 KB
/
isotp.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include <string.h>
#include <stdint.h>
#include "log.h"
#include "isotp.h"
#define _HOOK(x, ...) if (s->x ## _hook) ret = s->x ## _hook(s, __VA_ARGS__)
static void
_isotp_buf_append(isotp_state_t *s, uint8_t *src, size_t to_copy) {
memcpy(s->buf + s->bufpos, src, to_copy);
s->bufpos += to_copy;
s->sn = ((s->sn + 1) % 0xF);
}
static int
_isotp_reset(isotp_state_t *s)
{
s->dl = 0;
s->bufpos = 0;
s->sn = 0;
s->state = STATE_IDLE;
}
static int
_isotp_send_fc(isotp_state_t *s)
{
// XXX - For now, ask for all frames, with no further FC, and no delays between blocks.
int ret = 0;
uint8_t fc[] = {(FRAME_FLOW_CONTROL << 4 | 0x00), 0, 0};
size_t to_send = sizeof(fc) / sizeof(fc[0]);
LOG_DEBUG("isotp %s sending flow control...", s->name);
_HOOK(xmit, 1, s->dest_can_id, fc, to_send);
if (ret < 0) {
LOG_ERROR("isotp %s failed to send flow control frame", s->name);
_isotp_reset(s);
}
return ret;
}
int
isotp_init(isotp_state_t *s) {
memset(s, 0, sizeof(isotp_state_t));
s->name = "<unknown>";
s->pci_pos = 0;
_isotp_reset(s);
return 0;
}
int
isotp_ingest_frame(isotp_state_t *s, uint8_t *buf, uint8_t len)
{
#define _HOOK(x, ...) if (s->x ## _hook) ret = s->x ## _hook(s, __VA_ARGS__)
#define _ERROR(x) do { ret = x; goto out; } while (0)
#define _MIN_LEN(x) if (len < (s->pci_pos + x)) _ERROR(-1)
int ret = 0;
// XXX - We need to handle extended and mixed addressing.
size_t data_start = s->pci_pos + 1;
if (len < data_start) {
LOG_WARN("isotp %s received short CAN frame", s->name);
ret = -1;
goto out;
}
uint8_t pci = buf[s->pci_pos];
uint8_t frame_type = (pci >> 4) & 0xF;
LOG_DEBUG("isotp %s received frame type %d", s->name, frame_type);
switch (frame_type) {
case FRAME_SINGLE:
if (s->state != STATE_IDLE) {
// Signal an unexpected receive, and clear state.
_isotp_reset(s);
}
s->state = STATE_RECV;
s->dl = pci & 0xF;
if (s->dl > (8 - data_start)) {
LOG_ERROR("isotp %s received a single frame with too large (%d) of a length", s->name, s->dl);
ret = -1;
goto out;
}
_HOOK(recv, buf + data_start, s->dl);
_isotp_reset(s);
goto out;
break;
case FRAME_FIRST:
if (s->state != STATE_IDLE) {
// Signal an unexpected receive, and clear state.
LOG_ERROR("isotp %s received a first frame while not idle. resetting reception.", s->name);
_isotp_reset(s);
}
s->state = STATE_RECV;
_MIN_LEN(2);
data_start++;
s->dl = ((buf[s->pci_pos] & 0xF) << 8) | buf[s->pci_pos + 1];
size_t to_copy = 8 - data_start;
if ((s->dl - s->bufpos) < to_copy) {
LOG_ERROR("isotp %s received a short first frame (%d) which is invalid", s->name, s->dl);
_isotp_reset(s);
goto out;
}
_isotp_buf_append(s, buf + data_start, to_copy);
_isotp_send_fc(s);
break;
case FRAME_CONSECUTIVE:
{
if (s->state != STATE_RECV) {
// Signal that we got a consecutive frame, when we shouldn't have.
LOG_WARN("isotp %s received a consecutive frame with no first frame", s->name);
goto out;
}
uint8_t new_sn = pci & 0xF;
if (new_sn != s->sn) {
LOG_ERROR("isotp %s missed a packet expected %d got %d", s->sn, new_sn);
_isotp_reset(s);
goto out;
}
size_t to_copy = s->dl - s->bufpos;
if (to_copy > (8 - data_start)) {
to_copy = 8 - data_start;
}
_isotp_buf_append(s, buf + data_start, to_copy);
break;
}
default:
// Ignore anything else, but mention it:
LOG_WARN("isotp %s ignoring frame %d", s->name, frame_type);
break;
}
// We have a complete buffer, send it up the stack.
if (s->dl != 0 && s->bufpos == s->dl) {
_HOOK(recv, s->buf, s->dl);
_isotp_reset(s);
}
out:
return ret;
#undef _MIN_LEN
#undef _ERROR
}
#undef _HOOK