GCC Code Coverage Report


Directory: ./
File: firmware/controllers/can/isotp/isotp.cpp
Date: 2025-12-07 23:45:09
Warnings: 1 unchecked decisions!
Coverage Exec Excl Total
Lines: 53.4% 140 0 262
Functions: 70.0% 7 0 10
Branches: 45.9% 67 0 146
Decisions: 39.2% 38 - 97

Line Branch Decision Exec Source
1 /**
2 *
3 * https://en.wikipedia.org/wiki/ISO_15765-2
4 */
5
6 #include "pch.h"
7 #include "isotp.h"
8 #include "can_rx.h"
9
10 #if HAL_USE_CAN || EFI_UNIT_TEST
11
12 52 int IsoTpBase::sendFrame(const IsoTpFrameHeader &header, const uint8_t *data, int num, can_sysinterval_t timeout) {
13 52 int dlc = 8; // standard 8 bytes
14
1/1
✓ Branch 2 taken 52 times.
52 CanTxMessage txmsg(CanCategory::SERIAL, txFrameId, dlc, busIndex, IS_EXT_RANGE_ID(txFrameId));
15
16 // fill the frame data according to the CAN-TP protocol (ISO 15765-2)
17
1/1
✓ Branch 1 taken 52 times.
52 txmsg[isoHeaderByteIndex] = (uint8_t)((header.frameType & 0xf) << 4);
18 int offset, maxNumBytes;
19
4/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 8 times.
✗ Branch 4 not taken.
52 switch (header.frameType) {
20
1/1
✓ Decision 'true' taken 5 times.
5 case ISO_TP_FRAME_SINGLE:
21 5 offset = isoHeaderByteIndex + 1;
22
1/1
✓ Branch 1 taken 5 times.
5 maxNumBytes = minI(header.numBytes, dlc - offset);
23
1/1
✓ Branch 1 taken 5 times.
5 txmsg[isoHeaderByteIndex] |= maxNumBytes;
24 5 break;
25
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FIRST:
26
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex] |= (header.numBytes >> 8) & 0xf;
27
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex + 1] = (uint8_t)(header.numBytes & 0xff);
28 8 offset = isoHeaderByteIndex + 2;
29
1/1
✓ Branch 1 taken 8 times.
8 maxNumBytes = minI(header.numBytes, dlc - offset);
30 8 break;
31
1/1
✓ Decision 'true' taken 31 times.
31 case ISO_TP_FRAME_CONSECUTIVE:
32
1/1
✓ Branch 1 taken 31 times.
31 txmsg[isoHeaderByteIndex] |= header.index & 0xf;
33 31 offset = isoHeaderByteIndex + 1;
34 // todo: is it correct?
35 31 maxNumBytes = dlc - offset;
36 31 break;
37
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FLOW_CONTROL:
38
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex] |= header.fcFlag & 0xf;
39
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex + 1] = (uint8_t)(header.blockSize);
40
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex + 2] = (uint8_t)(header.separationTime);
41 8 offset = isoHeaderByteIndex + 3;
42 8 maxNumBytes = 0; // no data is sent with 'flow control' frame
43 8 break;
44 default:
45 // bad frame type
46 return 0;
47 }
48
49
1/1
✓ Branch 1 taken 52 times.
52 int numBytes = minI(maxNumBytes, num);
50 // copy the contents
51
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 8 times.
2/2
✓ Decision 'true' taken 44 times.
✓ Decision 'false' taken 8 times.
52 if (data != nullptr) {
52
2/2
✓ Branch 0 taken 258 times.
✓ Branch 1 taken 44 times.
2/2
✓ Decision 'true' taken 258 times.
✓ Decision 'false' taken 44 times.
302 for (int i = 0; i < numBytes; i++) {
53
1/1
✓ Branch 1 taken 258 times.
258 txmsg[i + offset] = data[i];
54 }
55 }
56
57 // send the frame!
58
2/3
✓ Branch 1 taken 52 times.
✓ Branch 3 taken 52 times.
✗ Branch 4 not taken.
1/2
✓ Decision 'true' taken 52 times.
✗ Decision 'false' not taken.
52 if (transmit(txmsg, timeout) == CAN_MSG_OK)
59 52 return numBytes;
60 return 0;
61 52 }
62
63 // returns the number of copied bytes
64 44 int CanStreamerState::receiveFrame(const CANRxFrame &rxmsg, uint8_t *destinationBuff, int availableAtBuffer, can_sysinterval_t timeout) {
65
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 44 times.
44 if (rxmsg.DLC < 1 + isoHeaderByteIndex)
66 return 0;
67 44 engine->pauseCANdueToSerial = true;
68 44 int frameType = (rxmsg.data8[isoHeaderByteIndex] >> 4) & 0xf;
69
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 44 times.
44 if (engineConfiguration->verboseIsoTp) {
70 efiPrintf("receiveFrame frameType=%d", frameType);
71 #if EFI_PROD_CODE
72 printCANRxFrame(-1, rxmsg);
73 #endif // EFI_PROD_CODE
74 }
75 int numBytesAvailable, frameIdx;
76 const uint8_t *srcBuf;
77
3/5
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
44 switch (frameType) {
78
1/1
✓ Decision 'true' taken 5 times.
5 case ISO_TP_FRAME_SINGLE:
79 5 numBytesAvailable = rxmsg.data8[isoHeaderByteIndex] & 0xf;
80 5 this->waitingForNumBytes = numBytesAvailable;
81 5 srcBuf = rxmsg.data8 + 1 + isoHeaderByteIndex;
82 5 break;
83
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FIRST:
84 8 this->waitingForNumBytes = ((rxmsg.data8[isoHeaderByteIndex] & 0xf) << 8) | rxmsg.data8[isoHeaderByteIndex + 1];
85 8 this->waitingForFrameIndex = 1;
86 8 numBytesAvailable = minI(this->waitingForNumBytes, 6 - isoHeaderByteIndex);
87 8 srcBuf = rxmsg.data8 + 2 + isoHeaderByteIndex;
88
1/2
✓ Branch 0 taken 8 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 8 times.
✗ Decision 'false' not taken.
8 if (rxTransport) {
89 8 rxTransport->onTpFirstFrame(); // used to send flow control message
90 }
91 8 break;
92
1/1
✓ Decision 'true' taken 31 times.
31 case ISO_TP_FRAME_CONSECUTIVE:
93 31 frameIdx = rxmsg.data8[isoHeaderByteIndex] & 0xf;
94
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 31 times.
31 if (this->waitingForNumBytes < 0 || this->waitingForFrameIndex != frameIdx) {
95 // todo: that's an abnormal situation, and we probably should react?
96 return 0;
97 }
98 31 numBytesAvailable = minI(this->waitingForNumBytes, 7 - isoHeaderByteIndex);
99 31 srcBuf = rxmsg.data8 + 1 + isoHeaderByteIndex;
100 31 this->waitingForFrameIndex = (this->waitingForFrameIndex + 1) & 0xf;
101 31 break;
102 case ISO_TP_FRAME_FLOW_CONTROL:
103 // todo: currently we just ignore the FC frame
104 return 0;
105 default:
106 // bad frame type
107 return 0;
108 }
109
110 /** performance optimization specific to TS over CAN tunnelling
111 TODO: refactor into child class if we ever choose to revive this logic
112 #if defined(TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME)
113 if (frameType == ISO_TP_FRAME_SINGLE) {
114 // restore the CRC on the whole packet
115 uint32_t crc = crc32((void *) srcBuf, numBytesAvailable);
116 // we need a separate buffer for crc because srcBuf may not be word-aligned for direct copy
117 uint8_t crcBuffer[sizeof(uint32_t)];
118 *(uint32_t *) (crcBuffer) = SWAP_UINT32(crc);
119
120 // now set the packet size
121 *(uint16_t *) shortCrcPacketStagingArea = SWAP_UINT16(numBytesAvailable);
122 // copy the data
123 if (numBytesAvailable > 0)
124 memcpy(shortCrcPacketStagingArea + sizeof(uint16_t), srcBuf, numBytesAvailable);
125 // copy the crc to the end
126 memcpy(shortCrcPacketStagingArea + sizeof(uint16_t) + numBytesAvailable, crcBuffer, sizeof(crcBuffer));
127
128 // use the reconstructed tmp buffer as a source buffer
129 srcBuf = shortCrcPacketStagingArea;
130 // we added the 16-bit size & 32-bit crc bytes
131 numBytesAvailable += sizeof(uint16_t) + sizeof(crcBuffer);
132 }
133 #endif *//* TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME */
134
135 44 int numBytesToCopy = minI(availableAtBuffer, numBytesAvailable);
136
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 44 times.
✗ Decision 'false' not taken.
44 if (destinationBuff != nullptr) {
137 44 memcpy(destinationBuff, srcBuf, numBytesToCopy);
138 }
139 44 srcBuf += numBytesToCopy;
140 44 waitingForNumBytes -= numBytesAvailable;
141 44 isComplete = (waitingForNumBytes == 0);
142 44 numBytesAvailable -= numBytesToCopy;
143 // if there are some more bytes left, we save them for the next time
144
2/2
✓ Branch 0 taken 24 times.
✓ Branch 1 taken 44 times.
2/2
✓ Decision 'true' taken 24 times.
✓ Decision 'false' taken 44 times.
68 for (int i = 0; i < numBytesAvailable; i++) {
145 24 rxFifoBuf.put(srcBuf[i]);
146 }
147
148 // according to the specs, we need to acknowledge the received multi-frame start frame
149
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 36 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 36 times.
44 if (frameType == ISO_TP_FRAME_FIRST) {
150 8 IsoTpFrameHeader header;
151 8 header.frameType = ISO_TP_FRAME_FLOW_CONTROL;
152 8 header.fcFlag = 0; // = "continue to send"
153 8 header.blockSize = 0; // = the remaining "frames" to be sent without flow control or delay
154 8 header.separationTime = 0; // = wait 0 milliseconds, send immediately
155
1/1
✓ Branch 1 taken 8 times.
8 IsoTpBase::sendFrame(header, nullptr, 0, timeout);
156 }
157
158 44 return numBytesToCopy;
159 }
160
161 void CanStreamerState::reset() {
162 waitingForNumBytes = 0;
163 waitingForFrameIndex = 0;
164 isComplete = false;
165 }
166
167 13 int CanStreamerState::sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout) {
168 13 int offset = 0;
169
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 13 times.
13 if (engineConfiguration->verboseIsoTp) {
171 PRINT("*** INFO: sendDataTimeout %d" PRINT_EOL, numBytes);
172 }
173
174
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 13 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 13 times.
13 if (numBytes < 1)
175 return 0;
176
177 // 1 frame
178
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 8 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 8 times.
13 if (numBytes <= 7 - isoHeaderByteIndex) {
179 5 IsoTpFrameHeader header;
180 5 header.frameType = ISO_TP_FRAME_SINGLE;
181 5 header.numBytes = numBytes;
182
1/1
✓ Branch 1 taken 5 times.
5 return IsoTpBase::sendFrame(header, txbuf, numBytes, timeout);
183 }
184
185 // multiple frames
186
187 // send the first header frame (FF)
188 8 IsoTpFrameHeader header;
189 8 header.frameType = ISO_TP_FRAME_FIRST;
190 8 header.numBytes = numBytes;
191
1/1
✓ Branch 1 taken 8 times.
8 int numSent = IsoTpBase::sendFrame(header, txbuf + offset, numBytes, timeout);
192 8 offset += numSent;
193 8 numBytes -= numSent;
194 8 int totalNumSent = numSent;
195
196 // get a flow control (FC) frame
197 #if !EFI_UNIT_TEST // todo: add FC to unit-tests?
198 CANRxFrame rxmsg;
199 for (size_t numFcReceived = 0; ; numFcReceived++) {
200 if (rxTransport->receive(&rxmsg, timeout) != CAN_MSG_OK) {
201 #ifdef SERIAL_CAN_DEBUG
202 PRINT("*** ERROR: CAN Flow Control frame not received" PRINT_EOL);
203 #endif /* SERIAL_CAN_DEBUG */
204 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control frame not received");
205 return 0;
206 }
207 receiveFrame(rxmsg, nullptr, 0, timeout);
208 uint8_t frameType = (rxmsg.data8[isoHeaderByteIndex] >> 4) & 0xf;
209 uint8_t flowStatus = rxmsg.data8[isoHeaderByteIndex] & 0xf;
210 // if something is not ok
211 if ((frameType != ISO_TP_FRAME_FLOW_CONTROL) || (flowStatus != CAN_FLOW_STATUS_OK)) {
212 // if the receiver is not ready yet and asks to wait for the next FC frame (give it 3 attempts)
213 if ((frameType == ISO_TP_FRAME_FLOW_CONTROL) && (flowStatus == CAN_FLOW_STATUS_WAIT_MORE) && (numFcReceived < 3)) {
214 continue;
215 }
216 #ifdef SERIAL_CAN_DEBUG
217 efiPrintf("*** ERROR: CAN Flow Control mode not supported");
218 #endif /* SERIAL_CAN_DEBUG */
219 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control mode not supported");
220 return 0;
221 }
222 uint8_t blockSize = rxmsg.data8[isoHeaderByteIndex + 1];
223 uint8_t minSeparationTime = rxmsg.data8[isoHeaderByteIndex + 2];
224 if (blockSize != 0 || minSeparationTime != 0) {
225 // todo: process other Flow Control fields (see ISO 15765-2)
226 #ifdef SERIAL_CAN_DEBUG
227 efiPrintf("*** ERROR: CAN Flow Control fields not supported");
228 #endif /* SERIAL_CAN_DEBUG */
229 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control fields not supported");
230 }
231 break;
232 }
233 #endif /* EFI_UNIT_TEST */
234
235 // send the rest of the data
236 8 int idx = 1;
237
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 8 times.
2/2
✓ Decision 'true' taken 31 times.
✓ Decision 'false' taken 8 times.
39 while (numBytes > 0) {
238
1/1
✓ Branch 1 taken 31 times.
31 int len = minI(numBytes, 7 - isoHeaderByteIndex);
239 // send the consecutive frames
240 31 header.frameType = ISO_TP_FRAME_CONSECUTIVE;
241 31 header.index = ((idx++) & 0x0f);
242 31 header.numBytes = len;
243
1/1
✓ Branch 1 taken 31 times.
31 numSent = IsoTpBase::sendFrame(header, txbuf + offset, len, timeout);
244
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 31 times.
31 if (numSent < 1)
245 break;
246 31 totalNumSent += numSent;
247 31 offset += numSent;
248 31 numBytes -= numSent;
249 }
250 8 return totalNumSent;
251 }
252
253 23 int CanStreamerState::getDataFromFifo(uint8_t *rxbuf, size_t &numBytes) {
254
2/2
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 10 times.
2/2
✓ Decision 'true' taken 13 times.
✓ Decision 'false' taken 10 times.
23 if (rxFifoBuf.isEmpty())
255 13 return 0;
256 10 int numReadFromFifo = minI(numBytes, rxFifoBuf.getCount());
257 // move bytes from the FIFO buffer
258 int i;
259
6/6
✓ Branch 1 taken 29 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 24 times.
✓ Branch 4 taken 5 times.
✓ Branch 5 taken 24 times.
✓ Branch 6 taken 10 times.
0/1
? Decision couldn't be analyzed.
34 for (i = 0; !rxFifoBuf.isEmpty() && i < numReadFromFifo; i++) {
260 24 rxbuf[i] = rxFifoBuf.get();
261 24 numBytes--;
262 }
263 10 return i;
264 }
265
266 23 can_msg_t CanStreamerState::streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout) {
267 23 int numBytes = *np;
268 23 int offset = 0;
269
270
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 23 times.
23 if (engineConfiguration->verboseIsoTp) {
271 PRINT("*** INFO: streamAddToTxTimeout adding %d, in buffer %d" PRINT_EOL, numBytes, txFifoBuf.getCount());
272 }
273
274 // we send here only if the TX FIFO buffer is getting overflowed
275
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 23 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 23 times.
23 while (numBytes >= txFifoBuf.getSize() - txFifoBuf.getCount()) {
276 int numBytesToAdd = txFifoBuf.getSize() - txFifoBuf.getCount();
277 txFifoBuf.put(txbuf + offset, numBytesToAdd);
278 int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout);
279
280 if (engineConfiguration->verboseIsoTp) {
281 PRINT("*** INFO: streamAddToTxTimeout numBytesToAdd %d / numSent %d / numBytes %d" PRINT_EOL, numBytesToAdd, numSent, numBytes);
282 }
283
284 if (numSent < 1)
285 break;
286 txFifoBuf.clear();
287 offset += numBytesToAdd;
288 numBytes -= numBytesToAdd;
289 }
290
291
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 23 times.
23 if (engineConfiguration->verboseIsoTp) {
292 PRINT("*** INFO: streamAddToTxTimeout remaining goes to buffer %d" PRINT_EOL, numBytes);
293 }
294
295 // now we put the rest on hold
296 23 txFifoBuf.put(txbuf + offset, numBytes);
297
298
299
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 23 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 23 times.
23 if (engineConfiguration->verboseIsoTp) {
300 PRINT("*** INFO: in buffer %d" PRINT_EOL, txFifoBuf.getCount());
301 }
302
303 23 return CAN_MSG_OK;
304 }
305
306 13 can_msg_t CanStreamerState::streamFlushTx(can_sysinterval_t timeout) {
307 13 int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout);
308 13 if (numSent != txFifoBuf.getCount()) {
309 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN sendDataTimeout() problems");
310 }
311 13 txFifoBuf.clear();
312
313 13 return CAN_MSG_OK;
314 }
315
316 23 can_msg_t CanStreamerState::streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout) {
317 23 size_t availableBufferSpace = *np;
318
319 // first, fill the data from the stored buffer (saved from the previous CAN frame)
320
1/1
✓ Branch 1 taken 23 times.
23 int receivedSoFar = getDataFromFifo(rxbuf, availableBufferSpace);
321
322 // if even more data is needed, then we receive more CAN frames
323
2/2
✓ Branch 0 taken 44 times.
✓ Branch 1 taken 23 times.
2/2
✓ Decision 'true' taken 44 times.
✓ Decision 'false' taken 23 times.
67 while (availableBufferSpace > 0) {
324 44 CANRxFrame rxmsg;
325
2/3
✓ Branch 1 taken 44 times.
✓ Branch 3 taken 44 times.
✗ Branch 4 not taken.
1/2
✓ Decision 'true' taken 44 times.
✗ Decision 'false' not taken.
44 if (rxTransport->receive(&rxmsg, timeout) == CAN_MSG_OK) {
326
1/1
✓ Branch 1 taken 44 times.
44 int numReceived = receiveFrame(rxmsg, rxbuf + receivedSoFar, availableBufferSpace, timeout);
327
328
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 44 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 44 times.
44 if (numReceived < 1)
329 break;
330 44 availableBufferSpace -= numReceived;
331 44 receivedSoFar += numReceived;
332 } else {
333 break;
334 }
335 }
336 23 *np -= availableBufferSpace;
337
338 #ifdef SERIAL_CAN_DEBUG
339 efiPrintf("* ret: %d %d (%d)", i, *np, availableBufferSpace);
340 for (int j = 0; j < receivedSoFar; j++) {
341 efiPrintf("* [%d]: %02x", j, rxbuf[j]);
342 }
343 #endif /* SERIAL_CAN_DEBUG */
344
345 23 return CAN_MSG_OK;
346 }
347
348 int IsoTpRx::readTimeout(uint8_t *rxbuf, size_t *size, sysinterval_t timeout)
349 {
350 //is fxbuf is too small?
351 bool overflow = false;
352 bool isFirstFrame = true;
353 size_t availableAtBuffer = *size;
354 uint8_t *buf = rxbuf;
355
356 do {
357 CANRxFrame rxmsg;
358
359 // TODO: adjust timeout!
360 if (!rxFifoBuf.get(rxmsg, timeout)) {
361 // TODO: error codes
362 if (isFirstFrame) {
363 efiPrintf("IsoTp: rx timeout, nothing received");
364 } else {
365 efiPrintf("IsoTP: rx timeout, %d left to receive", waitingForNumBytes);
366 }
367 return -1;
368 }
369
370 uint8_t frameType = (rxmsg.data8[isoHeaderByteIndex] >> 4) & 0xf;
371 if (engineConfiguration->verboseIsoTp) {
372 efiPrintf("receiveFrame frameType=%d", frameType);
373 #if EFI_PROD_CODE
374 printCANRxFrame(-1, rxmsg);
375 #endif // EFI_PROD_CODE
376 }
377 size_t numBytesAvailable;
378 uint8_t frameIdx;
379 const uint8_t *srcBuf;
380 switch (frameType) {
381 case ISO_TP_FRAME_SINGLE:
382 // TODO: check that this is first packet! see isFirstFrame
383 numBytesAvailable = rxmsg.data8[isoHeaderByteIndex] & 0xf;
384 waitingForNumBytes = numBytesAvailable;
385 srcBuf = rxmsg.data8 + 1 + isoHeaderByteIndex;
386 break;
387 case ISO_TP_FRAME_FIRST:
388 // TODO: check that this is first packet! see isFirstFrame
389 waitingForNumBytes = ((rxmsg.data8[isoHeaderByteIndex] & 0xf) << 8) | rxmsg.data8[isoHeaderByteIndex + 1];
390 waitingForFrameIndex = 1;
391 numBytesAvailable = minI(waitingForNumBytes, 6 - isoHeaderByteIndex);
392 srcBuf = rxmsg.data8 + 2 + isoHeaderByteIndex;
393 // rxTransport->onTpFirstFrame(); // used to send flow control message
394 break;
395 case ISO_TP_FRAME_CONSECUTIVE:
396 frameIdx = rxmsg.data8[isoHeaderByteIndex] & 0xf;
397 if (waitingForNumBytes < 0 || waitingForFrameIndex != frameIdx) {
398 // todo: that's an abnormal situation, and we probably should react?
399 // TODO: error codes
400 return -2;
401 }
402 numBytesAvailable = minI(waitingForNumBytes, 7 - isoHeaderByteIndex);
403 srcBuf = rxmsg.data8 + 1 + isoHeaderByteIndex;
404 waitingForFrameIndex = (waitingForFrameIndex + 1) & 0xf;
405 break;
406 case ISO_TP_FRAME_FLOW_CONTROL:
407 // todo: currently we just ignore the FC frame
408 // TODO: we should not receive FC frame while receiving data
409 break;
410 default:
411 // bad frame type
412 // TODO: error codes
413 return -3;
414 }
415
416 if (isFirstFrame) {
417 if ((buf) && (waitingForNumBytes > availableAtBuffer)) {
418 efiPrintf("receive buffer is not enough %d > %d", waitingForNumBytes, availableAtBuffer);
419 }
420 isFirstFrame = false;
421 }
422
423 if (buf != nullptr) {
424 int numBytesToCopy = minI(availableAtBuffer, numBytesAvailable);
425
426 memcpy(buf, srcBuf, numBytesToCopy);
427 buf += numBytesToCopy;
428 availableAtBuffer -= numBytesToCopy;
429
430 // if there are some more bytes left, receive and drop
431 if (numBytesAvailable > numBytesToCopy) {
432 overflow = true;
433 }
434 }
435
436 // according to the specs, we need to acknowledge the received multi-frame start frame
437 if (frameType == ISO_TP_FRAME_FIRST) {
438 IsoTpFrameHeader header;
439 header.frameType = ISO_TP_FRAME_FLOW_CONTROL;
440 header.fcFlag = 0; // = "continue to send"
441 header.blockSize = 0; // = the remaining "frames" to be sent without flow control or delay
442 header.separationTime = 0; // = wait 0 milliseconds, send immediately
443 sendFrame(header, nullptr, 0, timeout);
444 }
445
446 waitingForNumBytes -= numBytesAvailable;
447 } while (waitingForNumBytes > 0);
448
449 // received size
450 *size = buf - rxbuf;
451
452 return overflow ? 1 : 0;
453 }
454
455 int IsoTpRxTx::writeTimeout(const uint8_t *txbuf, size_t size, sysinterval_t timeout) {
456 int offset = 0;
457
458 if (engineConfiguration->verboseIsoTp) {
459 PRINT("*** INFO: sendDataTimeout %d" PRINT_EOL, size);
460 }
461
462 if (size < 1)
463 return 0;
464
465 // 1 frame
466 if (size <= 7 - isoHeaderByteIndex) {
467 IsoTpFrameHeader header;
468 header.frameType = ISO_TP_FRAME_SINGLE;
469 header.numBytes = size;
470 return IsoTpBase::sendFrame(header, txbuf, size, timeout);
471 }
472
473 // multiple frames
474
475 // send the first header frame (FF)
476 IsoTpFrameHeader header;
477 header.frameType = ISO_TP_FRAME_FIRST;
478 header.numBytes = size;
479 int numSent = IsoTpBase::sendFrame(header, txbuf + offset, size, timeout);
480 offset += numSent;
481 size -= numSent;
482
483 // get a flow control (FC) frame
484 #if !EFI_UNIT_TEST // todo: add FC to unit-tests?
485 CANRxFrame rxmsg;
486 size_t numFcReceived = 0;
487 int separationTimeUs = 0;
488 while (numFcReceived < 3) {
489 // TODO: adjust timeout!
490 if (!rxFifoBuf.get(rxmsg, timeout)) {
491 efiPrintf("IsoTp: Flow Control frame not received");
492 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control frame not received");
493 return 0;
494 }
495 uint8_t frameType = (rxmsg.data8[isoHeaderByteIndex] >> 4) & 0xf;
496
497 // if something is not ok
498 if (frameType != ISO_TP_FRAME_FLOW_CONTROL) {
499 // should we expect only FC here?
500 continue;
501 }
502
503 // Ok, frame is FC
504 numFcReceived++;
505 uint8_t flowStatus = rxmsg.data8[isoHeaderByteIndex] & 0xf;
506
507 if (flowStatus == CAN_FLOW_STATUS_ABORT) {
508 efiPrintf("IsoTp: Flow Control ABORT");
509 // TODO: error codes
510 return -4;
511 }
512
513 if (flowStatus == CAN_FLOW_STATUS_WAIT_MORE) {
514 // if the receiver is not ready yet and asks to wait for the next FC frame (give it 3 attempts)
515 if (numFcReceived < 3) {
516 continue;
517 }
518 // TODO: error codes
519 return -5;
520 }
521
522 if (flowStatus != CAN_FLOW_STATUS_OK) {
523 efiPrintf("IsoTp: Flow Control unknown Status %d", flowStatus);
524 // TODO: error codes
525 return -6;
526 }
527
528 uint8_t blockSize = rxmsg.data8[isoHeaderByteIndex + 1];
529 uint8_t minSeparationTime = rxmsg.data8[isoHeaderByteIndex + 2];
530 if (blockSize != 0) {
531 // todo: process other Flow Control fields (see ISO 15765-2)
532 efiPrintf("IsoTp: Flow Control blockSize is not supported %d", blockSize);
533 // TODO: error codes
534 return -7;
535 }
536
537 if (minSeparationTime <= 0x7f) {
538 // mS units
539 separationTimeUs = minSeparationTime * 1000;
540 } else if ((minSeparationTime >= 0xf1) && (minSeparationTime <= 0xf9)) {
541 // 100 uS units
542 separationTimeUs = (minSeparationTime - 0xf0) * 100;
543 }
544
545 break;
546 }
547 #endif /* EFI_UNIT_TEST */
548
549 // send the rest of the data
550 uint8_t idx = 1;
551 while (size > 0) {
552 int len = minI(size, 7 - isoHeaderByteIndex);
553 // send the consecutive frames
554 header.frameType = ISO_TP_FRAME_CONSECUTIVE;
555 header.index = ((idx++) & 0x0f);
556 header.numBytes = len;
557 numSent = IsoTpBase::sendFrame(header, txbuf + offset, len, timeout);
558 if (numSent < 1)
559 break;
560 offset += numSent;
561 size -= numSent;
562
563 #if ! EFI_UNIT_TEST
564 if (separationTimeUs) {
565 chThdSleepMicroseconds(separationTimeUs);
566 }
567 #endif // EFI_UNIT_TEST
568 }
569 return offset;
570 }
571
572 #endif // HAL_USE_CAN || EFI_UNIT_TEST
573