GCC Code Coverage Report


Directory: ./
File: firmware/controllers/can/isotp/isotp.cpp
Date: 2025-10-24 14:26:41
Warnings: 1 unchecked decisions!
Coverage Exec Excl Total
Lines: 83.5% 137 0 164
Functions: 100.0% 7 0 7
Branches: 74.2% 66 0 89
Decisions: 62.1% 36 - 58

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
9 #if HAL_USE_CAN || EFI_UNIT_TEST
10
11 52 int CanStreamerState::sendFrame(const IsoTpFrameHeader & header, const uint8_t *data, int num, can_sysinterval_t timeout) {
12 52 int dlc = 8; // standard 8 bytes
13
1/1
✓ Branch 2 taken 52 times.
52 CanTxMessage txmsg(CanCategory::SERIAL, txFrameId, dlc, busIndex, IS_EXT_RANGE_ID(txFrameId));
14
15 // fill the frame data according to the CAN-TP protocol (ISO 15765-2)
16
1/1
✓ Branch 1 taken 52 times.
52 txmsg[isoHeaderByteIndex] = (uint8_t)((header.frameType & 0xf) << 4);
17 int offset, maxNumBytes;
18
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) {
19
1/1
✓ Decision 'true' taken 5 times.
5 case ISO_TP_FRAME_SINGLE:
20 5 offset = 1;
21
1/1
✓ Branch 1 taken 5 times.
5 maxNumBytes = minI(header.numBytes, dlc - offset);
22
1/1
✓ Branch 1 taken 5 times.
5 txmsg[isoHeaderByteIndex] |= maxNumBytes;
23 5 break;
24
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FIRST:
25
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex] |= (header.numBytes >> 8) & 0xf;
26
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex + 1] = (uint8_t)(header.numBytes & 0xff);
27 8 offset = isoHeaderByteIndex + 2;
28
1/1
✓ Branch 1 taken 8 times.
8 maxNumBytes = minI(header.numBytes, dlc - offset);
29 8 break;
30
1/1
✓ Decision 'true' taken 31 times.
31 case ISO_TP_FRAME_CONSECUTIVE:
31
1/1
✓ Branch 1 taken 31 times.
31 txmsg[isoHeaderByteIndex] |= header.index & 0xf;
32 31 offset = isoHeaderByteIndex + 1;
33 // todo: is it correct?
34 31 maxNumBytes = dlc - offset;
35 31 break;
36
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FLOW_CONTROL:
37
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex] |= header.fcFlag & 0xf;
38
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex + 1] = (uint8_t)(header.blockSize);
39
1/1
✓ Branch 1 taken 8 times.
8 txmsg[isoHeaderByteIndex + 2] = (uint8_t)(header.separationTime);
40 8 offset = isoHeaderByteIndex + 3;
41 8 maxNumBytes = 0; // no data is sent with 'flow control' frame
42 8 break;
43 default:
44 // bad frame type
45 return 0;
46 }
47
48
1/1
✓ Branch 1 taken 52 times.
52 int numBytes = minI(maxNumBytes, num);
49 // copy the contents
50
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) {
51
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++) {
52
1/1
✓ Branch 1 taken 258 times.
258 txmsg[i + offset] = data[i];
53 }
54 }
55
56 // send the frame!
57
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 (transport->transmit(&txmsg, timeout) == CAN_MSG_OK)
58 52 return numBytes;
59 return 0;
60 52 }
61
62 // returns the number of copied bytes
63 44 int CanStreamerState::receiveFrame(CANRxFrame *rxmsg, uint8_t *buf, int num, can_sysinterval_t timeout) {
64
2/4
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 44 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 44 times.
44 if (rxmsg == nullptr || rxmsg->DLC < 1)
65 return 0;
66 44 engine->pauseCANdueToSerial = true;
67 44 int frameType = (rxmsg->data8[isoHeaderByteIndex] >> 4) & 0xf;
68 int numBytesAvailable, frameIdx;
69 44 uint8_t *srcBuf = rxmsg->data8;
70
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) {
71
1/1
✓ Decision 'true' taken 5 times.
5 case ISO_TP_FRAME_SINGLE:
72 5 numBytesAvailable = rxmsg->data8[isoHeaderByteIndex] & 0xf;
73 5 srcBuf = rxmsg->data8 + 1;
74 5 this->waitingForNumBytes = -1;
75 5 break;
76
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FIRST:
77 8 this->waitingForNumBytes = ((rxmsg->data8[isoHeaderByteIndex] & 0xf) << 8) | rxmsg->data8[isoHeaderByteIndex + 1];
78 8 this->waitingForFrameIndex = 1;
79 8 numBytesAvailable = minI(this->waitingForNumBytes, 6 - isoHeaderByteIndex);
80 8 srcBuf = rxmsg->data8 + 2 + isoHeaderByteIndex;
81 8 break;
82
1/1
✓ Decision 'true' taken 31 times.
31 case ISO_TP_FRAME_CONSECUTIVE:
83 31 frameIdx = rxmsg->data8[isoHeaderByteIndex] & 0xf;
84
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) {
85 // todo: that's an abnormal situation, and we probably should react?
86 return 0;
87 }
88 31 numBytesAvailable = minI(this->waitingForNumBytes, 7 - isoHeaderByteIndex);
89 31 srcBuf = rxmsg->data8 + 1 + isoHeaderByteIndex;
90 31 this->waitingForFrameIndex = (this->waitingForFrameIndex + 1) & 0xf;
91 31 break;
92 case ISO_TP_FRAME_FLOW_CONTROL:
93 // todo: currently we just ignore the FC frame
94 return 0;
95 default:
96 // bad frame type
97 return 0;
98 }
99
100 /** performance optimization specific to TS over CAN tunnelling
101 TODO: refactor into child class if we ever choose to revive this logic
102 #if defined(TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME)
103 if (frameType == ISO_TP_FRAME_SINGLE) {
104 // restore the CRC on the whole packet
105 uint32_t crc = crc32((void *) srcBuf, numBytesAvailable);
106 // we need a separate buffer for crc because srcBuf may not be word-aligned for direct copy
107 uint8_t crcBuffer[sizeof(uint32_t)];
108 *(uint32_t *) (crcBuffer) = SWAP_UINT32(crc);
109
110 // now set the packet size
111 *(uint16_t *) shortCrcPacketStagingArea = SWAP_UINT16(numBytesAvailable);
112 // copy the data
113 if (numBytesAvailable > 0)
114 memcpy(shortCrcPacketStagingArea + sizeof(uint16_t), srcBuf, numBytesAvailable);
115 // copy the crc to the end
116 memcpy(shortCrcPacketStagingArea + sizeof(uint16_t) + numBytesAvailable, crcBuffer, sizeof(crcBuffer));
117
118 // use the reconstructed tmp buffer as a source buffer
119 srcBuf = shortCrcPacketStagingArea;
120 // we added the 16-bit size & 32-bit crc bytes
121 numBytesAvailable += sizeof(uint16_t) + sizeof(crcBuffer);
122 }
123 #endif *//* TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME */
124
125 44 int numBytesToCopy = minI(num, numBytesAvailable);
126
1/2
✓ Branch 0 taken 44 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 44 times.
✗ Decision 'false' not taken.
44 if (buf != nullptr) {
127 44 memcpy(buf, srcBuf, numBytesToCopy);
128 }
129 44 srcBuf += numBytesToCopy;
130 44 waitingForNumBytes -= numBytesAvailable;
131 44 numBytesAvailable -= numBytesToCopy;
132 // if there are some more bytes left, we save them for the next time
133
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++) {
134 24 rxFifoBuf.put(srcBuf[i]);
135 }
136
137 // according to the specs, we need to acknowledge the received multi-frame start frame
138
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) {
139 8 IsoTpFrameHeader header;
140 8 header.frameType = ISO_TP_FRAME_FLOW_CONTROL;
141 8 header.fcFlag = 0; // = "continue to send"
142 8 header.blockSize = 0; // = the remaining "frames" to be sent without flow control or delay
143 8 header.separationTime = 0; // = wait 0 milliseconds, send immediately
144
1/1
✓ Branch 1 taken 8 times.
8 sendFrame(header, nullptr, 0, timeout);
145 }
146
147 44 return numBytesToCopy;
148 }
149
150 13 int CanStreamerState::sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout) {
151 13 int offset = 0;
152
153
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) {
154 PRINT("*** INFO: sendDataTimeout %d" PRINT_EOL, numBytes);
155 }
156
157
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)
158 return 0;
159
160 // 1 frame
161
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) {
162 5 IsoTpFrameHeader header;
163 5 header.frameType = ISO_TP_FRAME_SINGLE;
164 5 header.numBytes = numBytes;
165
1/1
✓ Branch 1 taken 5 times.
5 return sendFrame(header, txbuf, numBytes, timeout);
166 }
167
168 // multiple frames
169
170 // send the first header frame (FF)
171 8 IsoTpFrameHeader header;
172 8 header.frameType = ISO_TP_FRAME_FIRST;
173 8 header.numBytes = numBytes;
174
1/1
✓ Branch 1 taken 8 times.
8 int numSent = sendFrame(header, txbuf + offset, numBytes, timeout);
175 8 offset += numSent;
176 8 numBytes -= numSent;
177 8 int totalNumSent = numSent;
178
179 // get a flow control (FC) frame
180 #if !EFI_UNIT_TEST // todo: add FC to unit-tests?
181 CANRxFrame rxmsg;
182 for (int numFcReceived = 0; ; numFcReceived++) {
183 if (transport->receive(&rxmsg, timeout) != CAN_MSG_OK) {
184 #ifdef SERIAL_CAN_DEBUG
185 PRINT("*** ERROR: CAN Flow Control frame not received" PRINT_EOL);
186 #endif /* SERIAL_CAN_DEBUG */
187 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control frame not received");
188 return 0;
189 }
190 receiveFrame(&rxmsg, nullptr, 0, timeout);
191 int flowStatus = rxmsg.data8[0] & 0xf;
192 // if something is not ok
193 if (flowStatus != CAN_FLOW_STATUS_OK) {
194 // if the receiver is not ready yet and asks to wait for the next FC frame (give it 3 attempts)
195 if (flowStatus == CAN_FLOW_STATUS_WAIT_MORE && numFcReceived < 3) {
196 continue;
197 }
198 #ifdef SERIAL_CAN_DEBUG
199 efiPrintf("*** ERROR: CAN Flow Control mode not supported");
200 #endif /* SERIAL_CAN_DEBUG */
201 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control mode not supported");
202 return 0;
203 }
204 int blockSize = rxmsg.data8[1];
205 int minSeparationTime = rxmsg.data8[2];
206 if (blockSize != 0 || minSeparationTime != 0) {
207 // todo: process other Flow Control fields (see ISO 15765-2)
208 #ifdef SERIAL_CAN_DEBUG
209 efiPrintf("*** ERROR: CAN Flow Control fields not supported");
210 #endif /* SERIAL_CAN_DEBUG */
211 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control fields not supported");
212 }
213 break;
214 }
215 #endif /* EFI_UNIT_TEST */
216
217 // send the rest of the data
218 8 int idx = 1;
219
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) {
220
1/1
✓ Branch 1 taken 31 times.
31 int len = minI(numBytes, 7);
221 // send the consecutive frames
222 31 header.frameType = ISO_TP_FRAME_CONSECUTIVE;
223 31 header.index = ((idx++) & 0x0f);
224 31 header.numBytes = len;
225
1/1
✓ Branch 1 taken 31 times.
31 numSent = sendFrame(header, txbuf + offset, len, timeout);
226
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)
227 break;
228 31 totalNumSent += numSent;
229 31 offset += numSent;
230 31 numBytes -= numSent;
231 }
232 8 return totalNumSent;
233 }
234
235 23 int CanStreamerState::getDataFromFifo(uint8_t *rxbuf, size_t &numBytes) {
236
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())
237 13 return 0;
238 10 int numReadFromFifo = minI(numBytes, rxFifoBuf.getCount());
239 // move bytes from the FIFO buffer
240 int i;
241
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++) {
242 24 rxbuf[i] = rxFifoBuf.get();
243 24 numBytes--;
244 }
245 10 return i;
246 }
247
248 23 can_msg_t CanStreamerState::streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout) {
249 23 int numBytes = *np;
250 23 int offset = 0;
251
252
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) {
253 PRINT("*** INFO: streamAddToTxTimeout adding %d, in buffer %d" PRINT_EOL, numBytes, txFifoBuf.getCount());
254 }
255
256 // we send here only if the TX FIFO buffer is getting overflowed
257
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()) {
258 int numBytesToAdd = txFifoBuf.getSize() - txFifoBuf.getCount();
259 txFifoBuf.put(txbuf + offset, numBytesToAdd);
260 int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout);
261
262 if (engineConfiguration->verboseIsoTp) {
263 PRINT("*** INFO: streamAddToTxTimeout numBytesToAdd %d / numSent %d / numBytes %d" PRINT_EOL, numBytesToAdd, numSent, numBytes);
264 }
265
266 if (numSent < 1)
267 break;
268 txFifoBuf.clear();
269 offset += numBytesToAdd;
270 numBytes -= numBytesToAdd;
271 }
272
273
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) {
274 PRINT("*** INFO: streamAddToTxTimeout remaining goes to buffer %d" PRINT_EOL, numBytes);
275 }
276
277 // now we put the rest on hold
278 23 txFifoBuf.put(txbuf + offset, numBytes);
279
280
281
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) {
282 PRINT("*** INFO: in buffer %d" PRINT_EOL, txFifoBuf.getCount());
283 }
284
285 23 return CAN_MSG_OK;
286 }
287
288 13 can_msg_t CanStreamerState::streamFlushTx(can_sysinterval_t timeout) {
289 13 int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout);
290 13 if (numSent != txFifoBuf.getCount()) {
291 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN sendDataTimeout() problems");
292 }
293 13 txFifoBuf.clear();
294
295 13 return CAN_MSG_OK;
296 }
297
298 23 can_msg_t CanStreamerState::streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout) {
299 23 size_t availableBufferSpace = *np;
300
301 // first, fill the data from the stored buffer (saved from the previous CAN frame)
302
1/1
✓ Branch 1 taken 23 times.
23 int receivedSoFar = getDataFromFifo(rxbuf, availableBufferSpace);
303
304 // if even more data is needed, then we receive more CAN frames
305
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) {
306 44 CANRxFrame rxmsg;
307
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 (transport->receive(&rxmsg, timeout) == CAN_MSG_OK) {
308
1/1
✓ Branch 1 taken 44 times.
44 int numReceived = receiveFrame(&rxmsg, rxbuf + receivedSoFar, availableBufferSpace, timeout);
309
310
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)
311 break;
312 44 availableBufferSpace -= numReceived;
313 44 receivedSoFar += numReceived;
314 } else {
315 break;
316 }
317 }
318 23 *np -= availableBufferSpace;
319
320 #ifdef SERIAL_CAN_DEBUG
321 efiPrintf("* ret: %d %d (%d)", i, *np, availableBufferSpace);
322 for (int j = 0; j < receivedSoFar; j++) {
323 efiPrintf("* [%d]: %02x", j, rxbuf[j]);
324 }
325 #endif /* SERIAL_CAN_DEBUG */
326
327 23 return CAN_MSG_OK;
328 }
329
330 #endif // HAL_USE_CAN || EFI_UNIT_TEST
331