GCC Code Coverage Report


Directory: ./
File: firmware/console/binary/serial_can.cpp
Date: 2025-10-03 00:57:22
Warnings: 1 unchecked decisions!
Coverage Exec Excl Total
Lines: 80.1% 137 0 171
Functions: 87.5% 7 0 8
Branches: 68.8% 66 0 96
Decisions: 58.1% 36 - 62

Line Branch Decision Exec Source
1 /**
2 * @file serial_can.cpp
3 *
4 * This code is a bridge between a serial streaming used by TS and a packet-frame CAN-bus, using the ISO-TP protocol.
5 * ISO 15765-2, or ISO-TP (Transport Layer), which is an international standard for sending data packets over a CAN-Bus.
6 * https://en.wikipedia.org/wiki/ISO_15765-2
7 *
8 * @date Aug 1, 2020
9 * @author andreika <prometheus.pcb@gmail.com>
10 * @author Andrey Belomutskiy, (c) 2012-2020
11 */
12
13 #include "pch.h"
14
15
16 #if EFI_UNIT_TEST
17 #define PRINT printf
18 #define PRINT_EOL "\n"
19 #else
20 #define PRINT efiPrintf
21 #define PRINT_EOL ""
22 #endif
23
24 // todo: this file is asking to improve conditional compilation. unit_tests and cypress/kinetis are both special cases
25 #if HAL_USE_CAN || EFI_UNIT_TEST
26 #include "serial_can.h"
27 #include "can.h"
28 #include "can_msg_tx.h"
29 #endif // HAL_USE_CAN || EFI_UNIT_TEST
30
31
32 #if HAL_USE_CAN
33 static CanStreamer streamer;
34 static CanStreamerState state(&streamer);
35 static CanTsListener listener;
36 #endif // HAL_USE_CAN
37
38 #if HAL_USE_CAN || EFI_UNIT_TEST
39
40 52 int CanStreamerState::sendFrame(const IsoTpFrameHeader & header, const uint8_t *data, int num, can_sysinterval_t timeout) {
41 52 int dlc = 8; // standard 8 bytes
42
1/1
✓ Branch 2 taken 52 times.
52 CanTxMessage txmsg(CanCategory::SERIAL, CAN_ECU_SERIAL_TX_ID, dlc, /*bus*/0, IS_EXT_RANGE_ID(CAN_ECU_SERIAL_TX_ID));
43
44 // fill the frame data according to the CAN-TP protocol (ISO 15765-2)
45
1/1
✓ Branch 1 taken 52 times.
52 txmsg[0] = (uint8_t)((header.frameType & 0xf) << 4);
46 int offset, maxNumBytes;
47
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) {
48
1/1
✓ Decision 'true' taken 5 times.
5 case ISO_TP_FRAME_SINGLE:
49 5 offset = 1;
50
1/1
✓ Branch 1 taken 5 times.
5 maxNumBytes = minI(header.numBytes, dlc - offset);
51
1/1
✓ Branch 1 taken 5 times.
5 txmsg[0] |= maxNumBytes;
52 5 break;
53
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FIRST:
54
1/1
✓ Branch 1 taken 8 times.
8 txmsg[0] |= (header.numBytes >> 8) & 0xf;
55
1/1
✓ Branch 1 taken 8 times.
8 txmsg[1] = (uint8_t)(header.numBytes & 0xff);
56 8 offset = 2;
57
1/1
✓ Branch 1 taken 8 times.
8 maxNumBytes = minI(header.numBytes, dlc - offset);
58 8 break;
59
1/1
✓ Decision 'true' taken 31 times.
31 case ISO_TP_FRAME_CONSECUTIVE:
60
1/1
✓ Branch 1 taken 31 times.
31 txmsg[0] |= header.index & 0xf;
61 31 offset = 1;
62 // todo: is it correct?
63 31 maxNumBytes = dlc - offset;
64 31 break;
65
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FLOW_CONTROL:
66
1/1
✓ Branch 1 taken 8 times.
8 txmsg[0] |= header.fcFlag & 0xf;
67
1/1
✓ Branch 1 taken 8 times.
8 txmsg[1] = (uint8_t)(header.blockSize);
68
1/1
✓ Branch 1 taken 8 times.
8 txmsg[2] = (uint8_t)(header.separationTime);
69 8 offset = 3;
70 8 maxNumBytes = 0; // no data is sent with 'flow control' frame
71 8 break;
72 default:
73 // bad frame type
74 return 0;
75 }
76
77
1/1
✓ Branch 1 taken 52 times.
52 int numBytes = minI(maxNumBytes, num);
78 // copy the contents
79
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) {
80
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++) {
81
1/1
✓ Branch 1 taken 258 times.
258 txmsg[i + offset] = data[i];
82 }
83 }
84
85 // send the frame!
86
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 (streamer->transmit(CAN_ANY_MAILBOX, &txmsg, timeout) == CAN_MSG_OK)
87 52 return numBytes;
88 return 0;
89 52 }
90
91 // returns the number of copied bytes
92 44 int CanStreamerState::receiveFrame(CANRxFrame *rxmsg, uint8_t *buf, int num, can_sysinterval_t timeout) {
93
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)
94 return 0;
95 44 engine->pauseCANdueToSerial = true;
96 44 int frameType = (rxmsg->data8[0] >> 4) & 0xf;
97 int numBytesAvailable, frameIdx;
98 44 uint8_t *srcBuf = rxmsg->data8;
99
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) {
100
1/1
✓ Decision 'true' taken 5 times.
5 case ISO_TP_FRAME_SINGLE:
101 5 numBytesAvailable = rxmsg->data8[0] & 0xf;
102 5 srcBuf = rxmsg->data8 + 1;
103 5 this->waitingForNumBytes = -1;
104 5 break;
105
1/1
✓ Decision 'true' taken 8 times.
8 case ISO_TP_FRAME_FIRST:
106 8 this->waitingForNumBytes = ((rxmsg->data8[0] & 0xf) << 8) | rxmsg->data8[1];
107 8 this->waitingForFrameIndex = 1;
108 8 numBytesAvailable = minI(this->waitingForNumBytes, 6);
109 8 srcBuf = rxmsg->data8 + 2;
110 8 break;
111
1/1
✓ Decision 'true' taken 31 times.
31 case ISO_TP_FRAME_CONSECUTIVE:
112 31 frameIdx = rxmsg->data8[0] & 0xf;
113
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) {
114 // todo: that's an abnormal situation, and we probably should react?
115 return 0;
116 }
117 31 numBytesAvailable = minI(this->waitingForNumBytes, 7);
118 31 srcBuf = rxmsg->data8 + 1;
119 31 this->waitingForFrameIndex = (this->waitingForFrameIndex + 1) & 0xf;
120 31 break;
121 case ISO_TP_FRAME_FLOW_CONTROL:
122 // todo: currently we just ignore the FC frame
123 return 0;
124 default:
125 // bad frame type
126 return 0;
127 }
128
129 #if defined(TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME)
130 if (frameType == ISO_TP_FRAME_SINGLE) {
131 // restore the CRC on the whole packet
132 uint32_t crc = crc32((void *) srcBuf, numBytesAvailable);
133 // we need a separate buffer for crc because srcBuf may not be word-aligned for direct copy
134 uint8_t crcBuffer[sizeof(uint32_t)];
135 *(uint32_t *) (crcBuffer) = SWAP_UINT32(crc);
136
137 // now set the packet size
138 *(uint16_t *) tmpRxBuf = SWAP_UINT16(numBytesAvailable);
139 // copy the data
140 if (numBytesAvailable > 0)
141 memcpy(tmpRxBuf + sizeof(uint16_t), srcBuf, numBytesAvailable);
142 // copy the crc to the end
143 memcpy(tmpRxBuf + sizeof(uint16_t) + numBytesAvailable, crcBuffer, sizeof(crcBuffer));
144
145 // use the reconstructed tmp buffer as a source buffer
146 srcBuf = tmpRxBuf;
147 // we added the 16-bit size & 32-bit crc bytes
148 numBytesAvailable += sizeof(uint16_t) + sizeof(crcBuffer);
149 }
150 #endif /* TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME */
151
152 44 int numBytesToCopy = minI(num, numBytesAvailable);
153
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) {
154 44 memcpy(buf, srcBuf, numBytesToCopy);
155 }
156 44 srcBuf += numBytesToCopy;
157 44 waitingForNumBytes -= numBytesAvailable;
158 44 numBytesAvailable -= numBytesToCopy;
159 // if there are some more bytes left, we save them for the next time
160
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++) {
161 24 rxFifoBuf.put(srcBuf[i]);
162 }
163
164 // according to the specs, we need to acknowledge the received multi-frame start frame
165
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) {
166 8 IsoTpFrameHeader header;
167 8 header.frameType = ISO_TP_FRAME_FLOW_CONTROL;
168 8 header.fcFlag = 0; // = "continue to send"
169 8 header.blockSize = 0; // = the remaining "frames" to be sent without flow control or delay
170 8 header.separationTime = 0; // = wait 0 milliseconds, send immediately
171
1/1
✓ Branch 1 taken 8 times.
8 sendFrame(header, nullptr, 0, timeout);
172 }
173
174 44 return numBytesToCopy;
175 }
176
177 13 int CanStreamerState::sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout) {
178 13 int offset = 0;
179
180
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) {
181 PRINT("*** INFO: sendDataTimeout %d" PRINT_EOL, numBytes);
182 }
183
184
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)
185 return 0;
186
187 // 1 frame
188
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) {
189 5 IsoTpFrameHeader header;
190 5 header.frameType = ISO_TP_FRAME_SINGLE;
191 5 header.numBytes = numBytes;
192
1/1
✓ Branch 1 taken 5 times.
5 return sendFrame(header, txbuf, numBytes, timeout);
193 }
194
195 // multiple frames
196
197 // send the first header frame (FF)
198 8 IsoTpFrameHeader header;
199 8 header.frameType = ISO_TP_FRAME_FIRST;
200 8 header.numBytes = numBytes;
201
1/1
✓ Branch 1 taken 8 times.
8 int numSent = sendFrame(header, txbuf + offset, numBytes, timeout);
202 8 offset += numSent;
203 8 numBytes -= numSent;
204 8 int totalNumSent = numSent;
205
206 // get a flow control (FC) frame
207 #if !EFI_UNIT_TEST // todo: add FC to unit-tests?
208 CANRxFrame rxmsg;
209 for (int numFcReceived = 0; ; numFcReceived++) {
210 if (streamer->receive(CAN_ANY_MAILBOX, &rxmsg, timeout) != CAN_MSG_OK) {
211 #ifdef SERIAL_CAN_DEBUG
212 PRINT("*** ERROR: CAN Flow Control frame not received" PRINT_EOL);
213 #endif /* SERIAL_CAN_DEBUG */
214 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control frame not received");
215 return 0;
216 }
217 receiveFrame(&rxmsg, nullptr, 0, timeout);
218 int flowStatus = rxmsg.data8[0] & 0xf;
219 // if something is not ok
220 if (flowStatus != CAN_FLOW_STATUS_OK) {
221 // if the receiver is not ready yet and asks to wait for the next FC frame (give it 3 attempts)
222 if (flowStatus == CAN_FLOW_STATUS_WAIT_MORE && numFcReceived < 3) {
223 continue;
224 }
225 #ifdef SERIAL_CAN_DEBUG
226 efiPrintf("*** ERROR: CAN Flow Control mode not supported");
227 #endif /* SERIAL_CAN_DEBUG */
228 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control mode not supported");
229 return 0;
230 }
231 int blockSize = rxmsg.data8[1];
232 int minSeparationTime = rxmsg.data8[2];
233 if (blockSize != 0 || minSeparationTime != 0) {
234 // todo: process other Flow Control fields (see ISO 15765-2)
235 #ifdef SERIAL_CAN_DEBUG
236 efiPrintf("*** ERROR: CAN Flow Control fields not supported");
237 #endif /* SERIAL_CAN_DEBUG */
238 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control fields not supported");
239 }
240 break;
241 }
242 #endif /* EFI_UNIT_TEST */
243
244 // send the rest of the data
245 8 int idx = 1;
246
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) {
247
1/1
✓ Branch 1 taken 31 times.
31 int len = minI(numBytes, 7);
248 // send the consecutive frames
249 31 header.frameType = ISO_TP_FRAME_CONSECUTIVE;
250 31 header.index = ((idx++) & 0x0f);
251 31 header.numBytes = len;
252
1/1
✓ Branch 1 taken 31 times.
31 numSent = sendFrame(header, txbuf + offset, len, timeout);
253
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)
254 break;
255 31 totalNumSent += numSent;
256 31 offset += numSent;
257 31 numBytes -= numSent;
258 }
259 8 return totalNumSent;
260 }
261
262 23 int CanStreamerState::getDataFromFifo(uint8_t *rxbuf, size_t &numBytes) {
263
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())
264 13 return 0;
265 10 int numReadFromFifo = minI(numBytes, rxFifoBuf.getCount());
266 // move bytes from the FIFO buffer
267 int i;
268
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++) {
269 24 rxbuf[i] = rxFifoBuf.get();
270 24 numBytes--;
271 }
272 10 return i;
273 }
274
275 23 can_msg_t CanStreamerState::streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout) {
276 23 int numBytes = *np;
277 23 int offset = 0;
278
279
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) {
280 PRINT("*** INFO: streamAddToTxTimeout adding %d, in buffer %d" PRINT_EOL, numBytes, txFifoBuf.getCount());
281 }
282
283 // we send here only if the TX FIFO buffer is getting overflowed
284
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()) {
285 int numBytesToAdd = txFifoBuf.getSize() - txFifoBuf.getCount();
286 txFifoBuf.put(txbuf + offset, numBytesToAdd);
287 int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout);
288
289 if (engineConfiguration->verboseIsoTp) {
290 PRINT("*** INFO: streamAddToTxTimeout numBytesToAdd %d / numSent %d / numBytes %d" PRINT_EOL, numBytesToAdd, numSent, numBytes);
291 }
292
293 if (numSent < 1)
294 break;
295 txFifoBuf.clear();
296 offset += numBytesToAdd;
297 numBytes -= numBytesToAdd;
298 }
299
300
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) {
301 PRINT("*** INFO: streamAddToTxTimeout remaining goes to buffer %d" PRINT_EOL, numBytes);
302 }
303
304 // now we put the rest on hold
305 23 txFifoBuf.put(txbuf + offset, numBytes);
306
307
308
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) {
309 PRINT("*** INFO: in buffer %d" PRINT_EOL, txFifoBuf.getCount());
310 }
311
312 23 return CAN_MSG_OK;
313 }
314
315 13 can_msg_t CanStreamerState::streamFlushTx(can_sysinterval_t timeout) {
316 13 int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout);
317 13 if (numSent != txFifoBuf.getCount()) {
318 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN sendDataTimeout() problems");
319 }
320 13 txFifoBuf.clear();
321
322 13 return CAN_MSG_OK;
323 }
324
325 23 can_msg_t CanStreamerState::streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout) {
326 23 size_t availableBufferSpace = *np;
327
328 // first, fill the data from the stored buffer (saved from the previous CAN frame)
329
1/1
✓ Branch 1 taken 23 times.
23 int receivedSoFar = getDataFromFifo(rxbuf, availableBufferSpace);
330
331 // if even more data is needed, then we receive more CAN frames
332
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) {
333 44 CANRxFrame rxmsg;
334
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 (streamer->receive(CAN_ANY_MAILBOX, &rxmsg, timeout) == CAN_MSG_OK) {
335
1/1
✓ Branch 1 taken 44 times.
44 int numReceived = receiveFrame(&rxmsg, rxbuf + receivedSoFar, availableBufferSpace, timeout);
336
337
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)
338 break;
339 44 availableBufferSpace -= numReceived;
340 44 receivedSoFar += numReceived;
341 } else {
342 break;
343 }
344 }
345 23 *np -= availableBufferSpace;
346
347 #ifdef SERIAL_CAN_DEBUG
348 efiPrintf("* ret: %d %d (%d)", i, *np, availableBufferSpace);
349 for (int j = 0; j < receivedSoFar; j++) {
350 efiPrintf("* [%d]: %02x", j, rxbuf[j]);
351 }
352 #endif /* SERIAL_CAN_DEBUG */
353
354 23 return CAN_MSG_OK;
355 }
356 static int isoTpPacketCounter = 0;
357
358 /**
359 * incoming data main entry point
360 */
361 void CanTsListener::decodeFrame(const CANRxFrame& frame, efitick_t /*nowNt*/) {
362 // CAN ID filtering happens in base class, by the time we are here we know it's the CAN_ECU_SERIAL_RX_ID packet
363 // todo: what if the FIFO is full?
364 CanRxMessage msg(frame);
365 if (engineConfiguration->verboseIsoTp) {
366 PRINT("*** INFO: CanTsListener decodeFrame %d" PRINT_EOL, isoTpPacketCounter++);
367 }
368 if (!rxFifo.put(msg)) {
369 warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN sendDataTimeout() problems");
370 }
371 }
372
373 #if HAL_USE_CAN
374
375 void CanStreamer::init() {
376 registerCanListener(listener);
377 }
378
379 can_msg_t CanStreamer::transmit(canmbx_t /*mailbox*/, const CanTxMessage */*ctfp*/, can_sysinterval_t /*timeout*/) {
380 // we do nothing here - see CanTxMessage::~CanTxMessage()
381 return CAN_MSG_OK;
382 }
383
384 can_msg_t CanStreamer::receive(canmbx_t /*mailbox*/, CANRxFrame *crfp, can_sysinterval_t timeout) {
385 // see CanTsListener and processCanRxMessage()
386 CanRxMessage msg;
387 if (listener.get(msg, timeout)) {
388 *crfp = msg.frame;
389 return CAN_MSG_OK;
390 }
391 return CAN_MSG_TIMEOUT;
392 }
393
394 void canStreamInit(void) {
395 streamer.init();
396 }
397
398 msg_t canStreamAddToTxTimeout(size_t *np, const uint8_t *txbuf, sysinterval_t timeout) {
399 return state.streamAddToTxTimeout(np, txbuf, timeout);
400 }
401
402 msg_t canStreamFlushTx(sysinterval_t timeout) {
403 return state.streamFlushTx(timeout);
404 }
405
406 // np uses in/out parameter approach. Yes ChibiOS does same but still evil!
407 // in entry: number of data frames to receive
408 // on exit the number of frames actually received
409 msg_t canStreamReceiveTimeout(size_t *np, uint8_t *rxbuf, sysinterval_t timeout) {
410 return state.streamReceiveTimeout(np, rxbuf, timeout);
411 }
412
413 #endif /* HAL_USE_CAN */
414
415
416
417 #endif // HAL_USE_CAN || EFI_UNIT_TEST
418