rusEFI
The most advanced open source ECU
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 
40 int 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;
53  case ISO_TP_FRAME_FIRST:
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
92 int 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) {
100  case ISO_TP_FRAME_SINGLE:
101  numBytesAvailable = rxmsg->data8[0] & 0xf;
102  srcBuf = rxmsg->data8 + 1;
103  this->waitingForNumBytes = -1;
104  break;
105  case ISO_TP_FRAME_FIRST:
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 
177 int 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;
199  header.frameType = ISO_TP_FRAME_FIRST;
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 
262 int 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 
275 can_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 }
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);
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 
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 
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
void registerCanListener(CanListener &listener)
Definition: can_rx.cpp:89
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
Definition: serial_can.cpp:384
virtual can_msg_t transmit(canmbx_t mailbox, const CanTxMessage *ctfp, can_sysinterval_t timeout) override
Definition: serial_can.cpp:379
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)
Definition: serial_can.cpp:92
can_msg_t streamFlushTx(can_sysinterval_t timeout)
Definition: serial_can.cpp:315
int sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout)
Definition: serial_can.cpp:177
int sendFrame(const IsoTpFrameHeader &header, const uint8_t *data, int num, can_sysinterval_t timeout)
Definition: serial_can.cpp:40
can_msg_t streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout)
Definition: serial_can.cpp:325
int waitingForNumBytes
Definition: serial_can.h:75
uint8_t tmpRxBuf[13]
Definition: serial_can.h:71
int waitingForFrameIndex
Definition: serial_can.h:76
int getDataFromFifo(uint8_t *rxbuf, size_t &numBytes)
Definition: serial_can.cpp:262
can_msg_t streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout)
Definition: serial_can.cpp:275
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)
Definition: serial_can.cpp:361
bool pauseCANdueToSerial
Definition: engine.h:111
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
Engine * engine
bool warning(ObdCode code, const char *fmt,...)
@ CUSTOM_ERR_CAN_COMMUNICATION
engine_configuration_s * engineConfiguration
static ScState state
msg_t canStreamReceiveTimeout(size_t *np, uint8_t *rxbuf, sysinterval_t timeout)
Definition: serial_can.cpp:409
static int isoTpPacketCounter
Definition: serial_can.cpp:356
static CanStreamer streamer
Definition: serial_can.cpp:33
static CanTsListener listener
Definition: serial_can.cpp:35
msg_t canStreamAddToTxTimeout(size_t *np, const uint8_t *txbuf, sysinterval_t timeout)
Definition: serial_can.cpp:398
msg_t canStreamFlushTx(sysinterval_t timeout)
Definition: serial_can.cpp:402
void canStreamInit(void)
Definition: serial_can.cpp:394
@ 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