rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
serial_can.cpp
Go to the documentation of this file.
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
36#endif // HAL_USE_CAN
37
38#if HAL_USE_CAN || EFI_UNIT_TEST
39
40int CanStreamerState::sendFrame(const IsoTpFrameHeader & header, const uint8_t *data, int num, can_sysinterval_t timeout) {
41 int dlc = 8; // standard 8 bytes
42 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 txmsg[0] = (uint8_t)((header.frameType & 0xf) << 4);
46 int offset, maxNumBytes;
47 switch (header.frameType) {
49 offset = 1;
50 maxNumBytes = minI(header.numBytes, dlc - offset);
51 txmsg[0] |= maxNumBytes;
52 break;
54 txmsg[0] |= (header.numBytes >> 8) & 0xf;
55 txmsg[1] = (uint8_t)(header.numBytes & 0xff);
56 offset = 2;
57 maxNumBytes = minI(header.numBytes, dlc - offset);
58 break;
60 txmsg[0] |= header.index & 0xf;
61 offset = 1;
62 // todo: is it correct?
63 maxNumBytes = dlc - offset;
64 break;
66 txmsg[0] |= header.fcFlag & 0xf;
67 txmsg[1] = (uint8_t)(header.blockSize);
68 txmsg[2] = (uint8_t)(header.separationTime);
69 offset = 3;
70 maxNumBytes = 0; // no data is sent with 'flow control' frame
71 break;
72 default:
73 // bad frame type
74 return 0;
75 }
76
77 int numBytes = minI(maxNumBytes, num);
78 // copy the contents
79 if (data != nullptr) {
80 for (int i = 0; i < numBytes; i++) {
81 txmsg[i + offset] = data[i];
82 }
83 }
84
85 // send the frame!
86 if (streamer->transmit(CAN_ANY_MAILBOX, &txmsg, timeout) == CAN_MSG_OK)
87 return numBytes;
88 return 0;
89}
90
91// returns the number of copied bytes
92int CanStreamerState::receiveFrame(CANRxFrame *rxmsg, uint8_t *buf, int num, can_sysinterval_t timeout) {
93 if (rxmsg == nullptr || rxmsg->DLC < 1)
94 return 0;
96 int frameType = (rxmsg->data8[0] >> 4) & 0xf;
97 int numBytesAvailable, frameIdx;
98 uint8_t *srcBuf = rxmsg->data8;
99 switch (frameType) {
101 numBytesAvailable = rxmsg->data8[0] & 0xf;
102 srcBuf = rxmsg->data8 + 1;
103 this->waitingForNumBytes = -1;
104 break;
106 this->waitingForNumBytes = ((rxmsg->data8[0] & 0xf) << 8) | rxmsg->data8[1];
107 this->waitingForFrameIndex = 1;
108 numBytesAvailable = minI(this->waitingForNumBytes, 6);
109 srcBuf = rxmsg->data8 + 2;
110 break;
112 frameIdx = rxmsg->data8[0] & 0xf;
113 if (this->waitingForNumBytes < 0 || this->waitingForFrameIndex != frameIdx) {
114 // todo: that's an abnormal situation, and we probably should react?
115 return 0;
116 }
117 numBytesAvailable = minI(this->waitingForNumBytes, 7);
118 srcBuf = rxmsg->data8 + 1;
119 this->waitingForFrameIndex = (this->waitingForFrameIndex + 1) & 0xf;
120 break;
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 int numBytesToCopy = minI(num, numBytesAvailable);
153 if (buf != nullptr) {
154 memcpy(buf, srcBuf, numBytesToCopy);
155 }
156 srcBuf += numBytesToCopy;
157 waitingForNumBytes -= numBytesAvailable;
158 numBytesAvailable -= numBytesToCopy;
159 // if there are some more bytes left, we save them for the next time
160 for (int i = 0; i < numBytesAvailable; i++) {
161 rxFifoBuf.put(srcBuf[i]);
162 }
163
164 // according to the specs, we need to acknowledge the received multi-frame start frame
165 if (frameType == ISO_TP_FRAME_FIRST) {
166 IsoTpFrameHeader header;
168 header.fcFlag = 0; // = "continue to send"
169 header.blockSize = 0; // = the remaining "frames" to be sent without flow control or delay
170 header.separationTime = 0; // = wait 0 milliseconds, send immediately
171 sendFrame(header, nullptr, 0, timeout);
172 }
173
174 return numBytesToCopy;
175}
176
177int CanStreamerState::sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout) {
178 int offset = 0;
179
181 PRINT("*** INFO: sendDataTimeout %d" PRINT_EOL, numBytes);
182 }
183
184 if (numBytes < 1)
185 return 0;
186
187 // 1 frame
188 if (numBytes <= 7) {
189 IsoTpFrameHeader header;
191 header.numBytes = numBytes;
192 return sendFrame(header, txbuf, numBytes, timeout);
193 }
194
195 // multiple frames
196
197 // send the first header frame (FF)
198 IsoTpFrameHeader header;
200 header.numBytes = numBytes;
201 int numSent = sendFrame(header, txbuf + offset, numBytes, timeout);
202 offset += numSent;
203 numBytes -= numSent;
204 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 int idx = 1;
246 while (numBytes > 0) {
247 int len = minI(numBytes, 7);
248 // send the consecutive frames
250 header.index = ((idx++) & 0x0f);
251 header.numBytes = len;
252 numSent = sendFrame(header, txbuf + offset, len, timeout);
253 if (numSent < 1)
254 break;
255 totalNumSent += numSent;
256 offset += numSent;
257 numBytes -= numSent;
258 }
259 return totalNumSent;
260}
261
262int CanStreamerState::getDataFromFifo(uint8_t *rxbuf, size_t &numBytes) {
263 if (rxFifoBuf.isEmpty())
264 return 0;
265 int numReadFromFifo = minI(numBytes, rxFifoBuf.getCount());
266 // move bytes from the FIFO buffer
267 int i;
268 for (i = 0; !rxFifoBuf.isEmpty() && i < numReadFromFifo; i++) {
269 rxbuf[i] = rxFifoBuf.get();
270 numBytes--;
271 }
272 return i;
273}
274
275can_msg_t CanStreamerState::streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout) {
276 int numBytes = *np;
277 int offset = 0;
278
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 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
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
301 PRINT("*** INFO: streamAddToTxTimeout remaining goes to buffer %d" PRINT_EOL, numBytes);
302 }
303
304 // now we put the rest on hold
305 txFifoBuf.put(txbuf + offset, numBytes);
306
307
309 PRINT("*** INFO: in buffer %d" PRINT_EOL, txFifoBuf.getCount());
310 }
311
312 return CAN_MSG_OK;
313}
314
316 int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout);
317 if (numSent != txFifoBuf.getCount()) {
318 //warning(ObdCode::CUSTOM_ERR_CAN_COMMUNICATION, "CAN sendDataTimeout() problems");
319 }
320 txFifoBuf.clear();
321
322 return CAN_MSG_OK;
323}
324
326 size_t availableBufferSpace = *np;
327
328 // first, fill the data from the stored buffer (saved from the previous CAN frame)
329 int receivedSoFar = getDataFromFifo(rxbuf, availableBufferSpace);
330
331 // if even more data is needed, then we receive more CAN frames
332 while (availableBufferSpace > 0) {
333 CANRxFrame rxmsg;
334 if (streamer->receive(CAN_ANY_MAILBOX, &rxmsg, timeout) == CAN_MSG_OK) {
335 int numReceived = receiveFrame(&rxmsg, rxbuf + receivedSoFar, availableBufferSpace, timeout);
336
337 if (numReceived < 1)
338 break;
339 availableBufferSpace -= numReceived;
340 receivedSoFar += numReceived;
341 } else {
342 break;
343 }
344 }
345 *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 return CAN_MSG_OK;
355}
356static int isoTpPacketCounter = 0;
357
358/**
359 * incoming data main entry point
360 */
361void 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);
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
378
379can_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
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
394void canStreamInit(void) {
395 streamer.init();
396}
397
398msg_t canStreamAddToTxTimeout(size_t *np, const uint8_t *txbuf, sysinterval_t timeout) {
399 return state.streamAddToTxTimeout(np, txbuf, timeout);
400}
401
402msg_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
409msg_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
void registerCanListener(CanListener &listener)
Definition can_rx.cpp:84
int32_t can_msg_t
Definition can_mocks.h:6
uint32_t canmbx_t
Definition can_mocks.h:5
int32_t can_sysinterval_t
Definition can_mocks.h:7
CANRxFrame frame
Definition serial_can.h:108
virtual can_msg_t receive(canmbx_t mailbox, CANRxFrame *crfp, can_sysinterval_t timeout) override
virtual can_msg_t transmit(canmbx_t mailbox, const CanTxMessage *ctfp, can_sysinterval_t timeout) override
fifo_buffer< uint8_t, CAN_FIFO_BUF_SIZE > txFifoBuf
Definition serial_can.h:67
ICanStreamer * streamer
Definition serial_can.h:78
fifo_buffer< uint8_t, CAN_FIFO_BUF_SIZE > rxFifoBuf
Definition serial_can.h:66
int receiveFrame(CANRxFrame *rxmsg, uint8_t *buf, int num, can_sysinterval_t timeout)
can_msg_t streamFlushTx(can_sysinterval_t timeout)
int sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout)
int sendFrame(const IsoTpFrameHeader &header, const uint8_t *data, int num, can_sysinterval_t timeout)
can_msg_t streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout)
uint8_t tmpRxBuf[13]
Definition serial_can.h:71
int getDataFromFifo(uint8_t *rxbuf, size_t &numBytes)
can_msg_t streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout)
bool get(CanRxMessage &item, int timeout)
Definition serial_can.h:120
fifo_buffer_sync< CanRxMessage, CAN_FIFO_FRAME_SIZE > rxFifo
Definition serial_can.h:125
virtual void decodeFrame(const CANRxFrame &frame, efitick_t nowNt)
bool pauseCANdueToSerial
Definition engine.h:123
virtual can_msg_t transmit(canmbx_t mailbox, const CanTxMessage *ctfp, can_sysinterval_t timeout)=0
virtual can_msg_t receive(canmbx_t mailbox, CANRxFrame *crfp, can_sysinterval_t timeout)=0
IsoTpFrameType frameType
Definition serial_can.h:45
uint32_t SWAP_UINT32(uint32_t x)
Definition efilib.h:27
uint16_t SWAP_UINT16(uint16_t x)
Definition efilib.h:22
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
bool warning(ObdCode code, const char *fmt,...)
@ CUSTOM_ERR_CAN_COMMUNICATION
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1871, 1.0, -1.0, -1.0, "")
msg_t canStreamReceiveTimeout(size_t *np, uint8_t *rxbuf, sysinterval_t timeout)
static int isoTpPacketCounter
static CanStreamer streamer
static CanTsListener listener
msg_t canStreamAddToTxTimeout(size_t *np, const uint8_t *txbuf, sysinterval_t timeout)
msg_t canStreamFlushTx(sysinterval_t timeout)
void canStreamInit(void)
@ ISO_TP_FRAME_CONSECUTIVE
Definition serial_can.h:39
@ ISO_TP_FRAME_FIRST
Definition serial_can.h:38
@ ISO_TP_FRAME_FLOW_CONTROL
Definition serial_can.h:40
@ ISO_TP_FRAME_SINGLE
Definition serial_can.h:37
uint8_t data8[8]
Frame data.
Definition can_mocks.h:55
uint8_t DLC
Data length.
Definition can_mocks.h:42
uint16_t offset
Definition tunerstudio.h:0