| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | #pragma once | |||
| 2 | ||||
| 3 | #include "fifo_buffer.h" | |||
| 4 | #include "can.h" | |||
| 5 | #include "can_msg_tx.h" | |||
| 6 | #include "can_listener.h" | |||
| 7 | ||||
| 8 | #if EFI_UNIT_TEST | |||
| 9 | #define PRINT printf | |||
| 10 | #define PRINT_EOL "\n" | |||
| 11 | #else | |||
| 12 | #define PRINT efiPrintf | |||
| 13 | #define PRINT_EOL "" | |||
| 14 | #endif | |||
| 15 | ||||
| 16 | #if EFI_PROD_CODE | EFI_SIMULATOR | |||
| 17 | #define can_msg_t msg_t | |||
| 18 | #define can_sysinterval_t sysinterval_t | |||
| 19 | #define CAN_MSG_OK MSG_OK | |||
| 20 | #define CAN_MSG_TIMEOUT MSG_TIMEOUT | |||
| 21 | #else | |||
| 22 | #include "can_mocks.h" | |||
| 23 | #endif /* EFI_UNIT_TEST */ | |||
| 24 | ||||
| 25 | #define CAN_FLOW_STATUS_OK 0 | |||
| 26 | #define CAN_FLOW_STATUS_WAIT_MORE 1 | |||
| 27 | #define CAN_FLOW_STATUS_ABORT 2 | |||
| 28 | ||||
| 29 | enum IsoTpFrameType { | |||
| 30 | ISO_TP_FRAME_SINGLE = 0, | |||
| 31 | ISO_TP_FRAME_FIRST = 1, | |||
| 32 | ISO_TP_FRAME_CONSECUTIVE = 2, | |||
| 33 | ISO_TP_FRAME_FLOW_CONTROL = 3, | |||
| 34 | }; | |||
| 35 | ||||
| 36 | class IsoTpFrameHeader { | |||
| 37 | public: | |||
| 38 | IsoTpFrameType frameType; | |||
| 39 | ||||
| 40 | // used for 'single' or 'first' frames | |||
| 41 | int numBytes; | |||
| 42 | // used for 'consecutive' frames | |||
| 43 | int index; | |||
| 44 | // used for 'flow control' frames | |||
| 45 | int fcFlag; | |||
| 46 | int blockSize; | |||
| 47 | int separationTime; | |||
| 48 | }; | |||
| 49 | ||||
| 50 | // todo: what's the point of this wrapper/holder class anyway? | |||
| 51 | class CanRxMessage { | |||
| 52 | public: | |||
| 53 | CanRxMessage() {} | |||
| 54 | ||||
| 55 | ✗ | CanRxMessage(const CANRxFrame &f) { | ||
| 56 | ✗ | frame = f; | ||
| 57 | ✗ | } | ||
| 58 | ||||
| 59 | ✗ | CanRxMessage(const CanRxMessage& msg) : frame(msg.frame) {} | ||
| 60 | ||||
| 61 | ✗ | CanRxMessage& operator=(const CanRxMessage& msg) { | ||
| 62 | // full content copy | |||
| 63 | ✗ | frame = msg.frame; | ||
| 64 | ✗ | return *this; | ||
| 65 | } | |||
| 66 | ||||
| 67 | public: | |||
| 68 | CANRxFrame frame; | |||
| 69 | }; | |||
| 70 | ||||
| 71 | class CanRxMessageSource { | |||
| 72 | public: | |||
| 73 | virtual bool get(CanRxMessage &item, int timeout) = 0; | |||
| 74 | }; | |||
| 75 | ||||
| 76 | class ICanTransmitter { | |||
| 77 | public: | |||
| 78 | virtual can_msg_t transmit(CanTxMessage &ctfp, can_sysinterval_t timeout) = 0; | |||
| 79 | }; | |||
| 80 | ||||
| 81 | class ICanReceiver { | |||
| 82 | public: | |||
| 83 | virtual can_msg_t receive(CANRxFrame *crfp, can_sysinterval_t timeout) = 0; | |||
| 84 | virtual void onTpFirstFrame() = 0; | |||
| 85 | }; | |||
| 86 | ||||
| 87 | class IsoTpBase { | |||
| 88 | public: | |||
| 89 | 13 | IsoTpBase(ICanTransmitter *p_txTransport, size_t p_busIndex, uint32_t p_rxFrameId, uint32_t p_txFrameId) | ||
| 90 | 13 | : | ||
| 91 | 13 | txTransport(p_txTransport), | ||
| 92 | 13 | busIndex(p_busIndex), | ||
| 93 | 13 | rxFrameId(p_rxFrameId), | ||
| 94 | 13 | txFrameId(p_txFrameId) | ||
| 95 | 13 | {} | ||
| 96 | ||||
| 97 | int sendFrame(const IsoTpFrameHeader & header, const uint8_t *data, int num, can_sysinterval_t timeout); | |||
| 98 | ||||
| 99 | 52 | can_msg_t transmit(CanTxMessage &ctfp, can_sysinterval_t timeout) { | ||
| 100 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 52 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 52 times.
|
52 | if (isoHeaderByteIndex) { |
| 101 | // yes that would be truncated to byte, that's expected | |||
| 102 | ✗ | ctfp[0] = rxFrameId & 0xff; | ||
| 103 | } | |||
| 104 |
1/2✓ Branch 0 taken 52 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 52 times.
✗ Decision 'false' not taken.
|
52 | if (txTransport) { |
| 105 | 52 | return txTransport->transmit(ctfp, timeout); | ||
| 106 | } | |||
| 107 | ✗ | return CAN_MSG_OK; | ||
| 108 | } | |||
| 109 | ||||
| 110 | // Offset of first ISO-TP byte, usually 0 | |||
| 111 | // but some vendors add some specific data in first CAN byte | |||
| 112 | size_t isoHeaderByteIndex = 0; | |||
| 113 | ||||
| 114 | ICanTransmitter *txTransport; | |||
| 115 | ||||
| 116 | size_t busIndex; | |||
| 117 | uint32_t rxFrameId; | |||
| 118 | uint32_t txFrameId; | |||
| 119 | }; | |||
| 120 | ||||
| 121 | // We need an abstraction layer for unit-testing | |||
| 122 | // todo: no reason for composite entity to exist, keep splitting CanStreamerState into RX and TX! | |||
| 123 | class ICanTransport : public ICanTransmitter, public ICanReceiver { | |||
| 124 | }; | |||
| 125 | ||||
| 126 | // most efficient sizes are 6 + x * 7 that way whole buffer is transmitted as (x+1) full packets | |||
| 127 | #ifndef CAN_FIFO_BUF_SIZE | |||
| 128 | #define CAN_FIFO_BUF_SIZE 76 | |||
| 129 | #endif // CAN_FIFO_BUF_SIZE | |||
| 130 | ||||
| 131 | #define CAN_FIFO_FRAME_SIZE 8 | |||
| 132 | ||||
| 133 | class CanStreamerState : public IsoTpBase { | |||
| 134 | public: | |||
| 135 | // serial_can uses fifo_buffer_sync, unify? | |||
| 136 | fifo_buffer<uint8_t, CAN_FIFO_BUF_SIZE> rxFifoBuf; | |||
| 137 | fifo_buffer<uint8_t, CAN_FIFO_BUF_SIZE> txFifoBuf; | |||
| 138 | ||||
| 139 | /* | |||
| 140 | #if defined(TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME) | |||
| 141 | // used to restore the original packet with CRC | |||
| 142 | uint8_t shortCrcPacketStagingArea[13]; | |||
| 143 | #endif | |||
| 144 | */ | |||
| 145 | // used for multi-frame ISO-TP packets | |||
| 146 | int waitingForNumBytes = 0; | |||
| 147 | int waitingForFrameIndex = 0; | |||
| 148 | ||||
| 149 | ICanReceiver *rxTransport; | |||
| 150 | ||||
| 151 | public: | |||
| 152 | 13 | CanStreamerState(ICanTransmitter *p_txTransport, ICanReceiver *p_rxTransport, size_t p_busIndex, uint32_t p_rxFrameId, uint32_t p_txFrameId) | ||
| 153 | 13 | : | ||
| 154 | IsoTpBase(p_txTransport, p_busIndex, p_rxFrameId, p_txFrameId), | |||
| 155 | 13 | rxTransport(p_rxTransport) | ||
| 156 | 13 | {} | ||
| 157 | ||||
| 158 | bool isComplete{}; | |||
| 159 | ||||
| 160 | void reset(); | |||
| 161 | ||||
| 162 | int sendFrame(const IsoTpFrameHeader & header, const uint8_t *data, int num, can_sysinterval_t timeout); | |||
| 163 | int receiveFrame(const CANRxFrame &rxmsg, uint8_t *buf, int num, can_sysinterval_t timeout); | |||
| 164 | int getDataFromFifo(uint8_t *rxbuf, size_t &numBytes); | |||
| 165 | // returns the number of bytes sent | |||
| 166 | int sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout); | |||
| 167 | ||||
| 168 | // streaming support for TS I/O (see tunerstudio_io.cpp) | |||
| 169 | can_msg_t streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout); | |||
| 170 | can_msg_t streamFlushTx(can_sysinterval_t timeout); | |||
| 171 | can_msg_t streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout); | |||
| 172 | }; | |||
| 173 | ||||
| 174 | #define ISOTP_RX_QUEUE_LEN 4 | |||
| 175 | ||||
| 176 | class IsoTpRx : public CanListener, public IsoTpBase { | |||
| 177 | public: | |||
| 178 | IsoTpRx(size_t p_busIndex, uint32_t p_rxFrameId, uint32_t p_txFrameId) | |||
| 179 | : | |||
| 180 | CanListener(p_rxFrameId), | |||
| 181 | IsoTpBase(nullptr, p_busIndex, p_rxFrameId, p_txFrameId) | |||
| 182 | { | |||
| 183 | rxFifoBuf.clear(); | |||
| 184 | registerCanListener(*this); | |||
| 185 | } | |||
| 186 | ||||
| 187 | ~IsoTpRx() { | |||
| 188 | unregisterCanListener(*this); | |||
| 189 | } | |||
| 190 | ||||
| 191 | void reset() { | |||
| 192 | rxFifoBuf.clear(); | |||
| 193 | waitingForNumBytes = 0; | |||
| 194 | waitingForFrameIndex = 0; | |||
| 195 | } | |||
| 196 | ||||
| 197 | /* CAN messages entry point */ | |||
| 198 | virtual void decodeFrame(const CANRxFrame& frame, efitick_t nowNt) | |||
| 199 | { | |||
| 200 | if (frame.DLC < 1 + isoHeaderByteIndex) { | |||
| 201 | // invalid++; | |||
| 202 | return; | |||
| 203 | } | |||
| 204 | ||||
| 205 | if (isoHeaderByteIndex) { | |||
| 206 | if (frame.data8[0] != (txFrameId & 0xff)) { | |||
| 207 | return; | |||
| 208 | } | |||
| 209 | } | |||
| 210 | ||||
| 211 | if (!rxFifoBuf.put(frame)) { | |||
| 212 | // overruns++; | |||
| 213 | } | |||
| 214 | } | |||
| 215 | ||||
| 216 | /* User app entry point */ | |||
| 217 | int readTimeout(uint8_t *rxbuf, size_t *size, sysinterval_t timeout); | |||
| 218 | ||||
| 219 | private: | |||
| 220 | // used for multi-frame ISO-TP packets | |||
| 221 | int waitingForNumBytes = 0; | |||
| 222 | uint8_t waitingForFrameIndex = 0; | |||
| 223 | ||||
| 224 | protected: | |||
| 225 | fifo_buffer_sync<CANRxFrame, ISOTP_RX_QUEUE_LEN> rxFifoBuf; | |||
| 226 | }; | |||
| 227 | ||||
| 228 | class IsoTpRxTx : public IsoTpRx { | |||
| 229 | public: | |||
| 230 | IsoTpRxTx(size_t p_busIndex, uint32_t p_rxFrameId, uint32_t p_txFrameId) | |||
| 231 | : | |||
| 232 | IsoTpRx(p_busIndex, p_rxFrameId, p_txFrameId) | |||
| 233 | {} | |||
| 234 | ||||
| 235 | int writeTimeout(const uint8_t *txbuf, size_t size, sysinterval_t timeout); | |||
| 236 | }; | |||
| 237 |