diff --git a/firmware/console/binary/serial_can.cpp b/firmware/console/binary/serial_can.cpp index 5535afe263..d7117b5dc9 100644 --- a/firmware/console/binary/serial_can.cpp +++ b/firmware/console/binary/serial_can.cpp @@ -12,44 +12,47 @@ #include "pch.h" #include "os_access.h" +#include "crc.h" + +#if HAL_USE_CAN #include "serial_can.h" +#include "can.h" +#include "can_msg_tx.h" +static CanStreamer streamer; +static CanStreamerState state(&streamer); +static CanTsListener listener; -static CanStreamerState state; - -int CanStreamerState::sendFrame(CANDriver *canp, const IsoTpFrameHeader & header, const uint8_t *data, int num) { - CANTxFrame txmsg; - memset(&txmsg, 0, sizeof(txmsg)); - txmsg.IDE = CAN_IDE_STD; - txmsg.EID = CAN_TX_ID; - txmsg.RTR = CAN_RTR_DATA; - txmsg.DLC = 8; // 8 bytes +int CanStreamerState::sendFrame(const IsoTpFrameHeader & header, const uint8_t *data, int num, can_sysinterval_t timeout) { + int dlc = 8; // standard 8 bytes + CanTxMessage txmsg(CAN_SERIAL_TX_ID, dlc, false); // fill the frame data according to the CAN-TP protocol (ISO 15765-2) - txmsg.data8[0] = (uint8_t)((header.frameType & 0xf) << 4); + txmsg[0] = (uint8_t)((header.frameType & 0xf) << 4); int offset, maxNumBytes; switch (header.frameType) { case ISO_TP_FRAME_SINGLE: offset = 1; - maxNumBytes = minI(header.numBytes, txmsg.DLC - offset); - txmsg.data8[0] |= maxNumBytes; + maxNumBytes = minI(header.numBytes, dlc - offset); + txmsg[0] |= maxNumBytes; break; case ISO_TP_FRAME_FIRST: - txmsg.data8[0] |= (header.numBytes >> 8) & 0xf; - txmsg.data8[1] = (uint8_t)(header.numBytes & 0xff); + txmsg[0] |= (header.numBytes >> 8) & 0xf; + txmsg[1] = (uint8_t)(header.numBytes & 0xff); offset = 2; - maxNumBytes = minI(header.numBytes, txmsg.DLC - offset); + maxNumBytes = minI(header.numBytes, dlc - offset); break; case ISO_TP_FRAME_CONSECUTIVE: - txmsg.data8[0] |= header.index & 0xf; + txmsg[0] |= header.index & 0xf; offset = 1; - maxNumBytes = txmsg.DLC - offset; + // todo: is it correct? + maxNumBytes = dlc - offset; break; case ISO_TP_FRAME_FLOW_CONTROL: - txmsg.data8[0] |= header.fcFlag & 0xf; - txmsg.data8[1] = (uint8_t)(header.blockSize); - txmsg.data8[2] = (uint8_t)(header.separationTime); + txmsg[0] |= header.fcFlag & 0xf; + txmsg[1] = (uint8_t)(header.blockSize); + txmsg[2] = (uint8_t)(header.separationTime); offset = 3; maxNumBytes = 0; // no data is sent with 'flow control' frame break; @@ -59,18 +62,18 @@ int CanStreamerState::sendFrame(CANDriver *canp, const IsoTpFrameHeader & header // copy the contents if (data != nullptr) { for (int i = 0; i < numBytes; i++) { - txmsg.data8[i + offset] = data[i]; + txmsg[i + offset] = data[i]; } } - + // send the frame! - if (canTransmit(&CAND1, CAN_ANY_MAILBOX, &txmsg, TIME_MS2I(100)) == MSG_OK) + if (streamer->transmit(CAN_ANY_MAILBOX, &txmsg, timeout) == CAN_MSG_OK) return numBytes; return 0; } // returns the number of copied bytes -int CanStreamerState::receiveFrame(CANDriver *canp, CANRxFrame *rxmsg, uint8_t *buf, int num) { +int CanStreamerState::receiveFrame(CANRxFrame *rxmsg, uint8_t *buf, int num, can_sysinterval_t timeout) { if (rxmsg == nullptr || rxmsg->DLC < 1) return 0; int frameType = (rxmsg->data8[0] >> 4) & 0xf; @@ -105,21 +108,24 @@ int CanStreamerState::receiveFrame(CANDriver *canp, CANRxFrame *rxmsg, uint8_t * #if defined(TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME) if (frameType == ISO_TP_FRAME_SINGLE) { - srcBuf = state.tmpRxBuf; // restore the CRC on the whole packet - uint32_t crc = crc32((void *) (rxmsg->data + 1), numBytesAvailable); + uint32_t crc = crc32((void *) srcBuf, numBytesAvailable); // we need a separate buffer for crc because srcBuf may not be word-aligned for direct copy uint8_t crcBuffer[sizeof(uint32_t)]; *(uint32_t *) (crcBuffer) = SWAP_UINT32(crc); - // now set the packet size (including the command byte) - *(uint16_t *) srcBuf = SWAP_UINT16(numBytesAvailable); + // now set the packet size + *(uint16_t *) tmpRxBuf = SWAP_UINT16(numBytesAvailable); // copy the data if (numBytesAvailable > 0) - memcpy(srcBuf + 2, rxmsg->data8 + 1, numBytesAvailable); - // copy the crc - memcpy(srcBuf + 2 + numBytesAvailable, crcBuffer, sizeof(crcBuffer)); - numBytesAvailable += 1 + sizeof(crcBuffer); // added command & crc bytes + memcpy(tmpRxBuf + sizeof(uint16_t), srcBuf, numBytesAvailable); + // copy the crc to the end + memcpy(tmpRxBuf + sizeof(uint16_t) + numBytesAvailable, crcBuffer, sizeof(crcBuffer)); + + // use the reconstructed tmp buffer as a source buffer + srcBuf = tmpRxBuf; + // we added the 16-bit size & 32-bit crc bytes + numBytesAvailable += sizeof(uint16_t) + sizeof(crcBuffer); } #endif /* TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME */ @@ -142,39 +148,71 @@ int CanStreamerState::receiveFrame(CANDriver *canp, CANRxFrame *rxmsg, uint8_t * header.fcFlag = 0; // = "continue to send" header.blockSize = 0; // = the remaining "frames" to be sent without flow control or delay header.separationTime = 0; // = wait 0 milliseconds, send immediately - sendFrame(canp, header, nullptr, 0); + sendFrame(header, nullptr, 0, timeout); } return numBytesToCopy; } -int CanStreamerState::sendDataTimeout(CANDriver *canp, const uint8_t *txbuf, int numBytes, sysinterval_t timeout) { - +int CanStreamerState::sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout) { int offset = 0; - msg_t ret; + + if (numBytes < 1) + return 0; + // 1 frame if (numBytes <= 7) { IsoTpFrameHeader header; header.frameType = ISO_TP_FRAME_SINGLE; header.numBytes = numBytes; - return state.sendFrame(canp, header, txbuf, numBytes); + return sendFrame(header, txbuf, numBytes, timeout); } // multiple frames - // send the first header frame + // send the first header frame (FF) IsoTpFrameHeader header; header.frameType = ISO_TP_FRAME_FIRST; header.numBytes = numBytes; - int numSent = state.sendFrame(canp, header, txbuf + offset, numBytes); + int numSent = sendFrame(header, txbuf + offset, numBytes, timeout); offset += numSent; numBytes -= numSent; int totalNumSent = numSent; - // get a flow control frame + // get a flow control (FC) frame CANRxFrame rxmsg; - if (canReceive(&CAND1, CAN_ANY_MAILBOX, &rxmsg, timeout) == MSG_OK) { - state.receiveFrame(canp, &rxmsg, nullptr, 0); + for (int numFcReceived = 0; ; numFcReceived++) { + if (streamer->receive(CAN_ANY_MAILBOX, &rxmsg, timeout) != CAN_MSG_OK) { +#ifdef SERIAL_CAN_DEBUG + efiPrintf("*** ERROR: CAN Flow Control frame not received"); +#endif /* SERIAL_CAN_DEBUG */ + //warning(CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control frame not received"); + return 0; + } + receiveFrame(&rxmsg, nullptr, 0, timeout); + int flowStatus = rxmsg.data8[0] & 0xf; + // if something is not ok + if (flowStatus != CAN_FLOW_STATUS_OK) { + // if the receiver is not ready yet and asks to wait for the next FC frame (give it 3 attempts) + if (flowStatus == CAN_FLOW_STATUS_WAIT_MORE && numFcReceived < 3) { + continue; + } +#ifdef SERIAL_CAN_DEBUG + efiPrintf("*** ERROR: CAN Flow Control mode not supported"); +#endif /* SERIAL_CAN_DEBUG */ + //warning(CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control mode not supported"); + return 0; + } + int blockSize = rxmsg.data8[1]; + int minSeparationTime = rxmsg.data8[2]; + if (blockSize != 0 || minSeparationTime != 0) { + // todo: process other Flow Control fields (see ISO 15765-2) +#ifdef SERIAL_CAN_DEBUG + efiPrintf("*** ERROR: CAN Flow Control fields not supported"); +#endif /* SERIAL_CAN_DEBUG */ + //warning(CUSTOM_ERR_CAN_COMMUNICATION, "CAN Flow Control fields not supported"); + } + break; } // send the rest of the data @@ -185,8 +223,8 @@ int CanStreamerState::sendDataTimeout(CANDriver *canp, const uint8_t *txbuf, int IsoTpFrameHeader header; header.frameType = ISO_TP_FRAME_CONSECUTIVE; header.index = ((idx++) & 0x0f); - header.numBytes = numBytes; - int numSent = state.sendFrame(canp, header, txbuf + offset, numBytes); + header.numBytes = len; + int numSent = sendFrame(header, txbuf + offset, len, timeout); if (numSent < 1) break; totalNumSent += numSent; @@ -209,60 +247,111 @@ int CanStreamerState::getDataFromFifo(uint8_t *rxbuf, size_t &numBytes) { return i; } -void canInit(CANDriver *canp) { - chEvtRegister(&CAND1.rxfull_event, &state.el, 0); -} - -msg_t canAddToTxStreamTimeout(CANDriver *canp, size_t *np, - const uint8_t *txbuf, sysinterval_t timeout) { +can_msg_t CanStreamerState::streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout) { int numBytes = *np; int offset = 0; - int minNumBytesRequiredToSend = 7 - state.txFifoBuf.getCount(); + int minNumBytesRequiredToSend = 7 - txFifoBuf.getCount(); while (numBytes >= minNumBytesRequiredToSend) { - state.txFifoBuf.put(txbuf + offset, minNumBytesRequiredToSend); - int numSent = state.sendDataTimeout(canp, (const uint8_t *)state.txFifoBuf.elements, state.txFifoBuf.getCount(), timeout); + txFifoBuf.put(txbuf + offset, numBytes); + int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout); if (numSent < 1) break; - state.txFifoBuf.clear(); + txFifoBuf.clear(); offset += numSent; numBytes -= numSent; minNumBytesRequiredToSend = 7; } // now we put the rest on hold - state.txFifoBuf.put(txbuf + offset, numBytes); + txFifoBuf.put(txbuf + offset, numBytes); - return MSG_OK; + return CAN_MSG_OK; } -msg_t canFlushTxStream(CANDriver *canp, sysinterval_t timeout) { - int numSent = state.sendDataTimeout(canp, (const uint8_t *)state.txFifoBuf.elements, state.txFifoBuf.getCount(), timeout); - state.txFifoBuf.clear(); +can_msg_t CanStreamerState::streamFlushTx(can_sysinterval_t timeout) { + int numSent = sendDataTimeout((const uint8_t *)txFifoBuf.getElements(), txFifoBuf.getCount(), timeout); + if (numSent != txFifoBuf.getCount()) { + //warning(CUSTOM_ERR_CAN_COMMUNICATION, "CAN sendDataTimeout() problems"); + } + txFifoBuf.clear(); - return MSG_OK; + return CAN_MSG_OK; } -msg_t canStreamReceiveTimeout(CANDriver *canp, size_t *np, - uint8_t *rxbuf, sysinterval_t timeout) { +can_msg_t CanStreamerState::streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout) { int i = 0; size_t numBytes = *np; - + // first, fill the data from the stored buffer (saved from the previous CAN frame) - i = state.getDataFromFifo(rxbuf, numBytes); + i = getDataFromFifo(rxbuf, numBytes); // if even more data is needed, then we receive more CAN frames while (numBytes > 0) { - if (chEvtWaitAnyTimeout(ALL_EVENTS, timeout) == 0) - return MSG_TIMEOUT; CANRxFrame rxmsg; - if (canReceive(&CAND1, CAN_ANY_MAILBOX, &rxmsg, TIME_IMMEDIATE) == MSG_OK) { - int numReceived = state.receiveFrame(canp, &rxmsg, rxbuf + i, numBytes); + if (streamer->receive(CAN_ANY_MAILBOX, &rxmsg, timeout) == CAN_MSG_OK) { + int numReceived = receiveFrame(&rxmsg, rxbuf + i, numBytes, timeout); + if (numReceived < 1) break; numBytes -= numReceived; + i += numReceived; + } else { + break; } } - //*np -= numBytes; - return MSG_OK; + *np -= numBytes; + +#ifdef SERIAL_CAN_DEBUG + efiPrintf("* ret: %d %d (%d)", i, *np, numBytes); + for (int j = 0; j < i; j++) { + efiPrintf("* [%d]: %02x", j, rxbuf[j]); + } +#endif /* SERIAL_CAN_DEBUG */ + + return CAN_MSG_OK; } +void CanTsListener::decodeFrame(const CANRxFrame& frame, efitick_t /*nowNt*/) { + // todo: what if the FIFO is full? + CanRxMessage msg(frame); + if (!rxFifo.put(msg)) { + //warning(CUSTOM_ERR_CAN_COMMUNICATION, "CAN sendDataTimeout() problems"); + } +} + +void CanStreamer::init() { + registerCanListener(listener); +} + +can_msg_t CanStreamer::transmit(canmbx_t /*mailbox*/, const CanTxMessage */*ctfp*/, can_sysinterval_t /*timeout*/) { + // we do nothing here - see CanTxMessage::~CanTxMessage() + return CAN_MSG_OK; +} + +can_msg_t CanStreamer::receive(canmbx_t /*mailbox*/, CANRxFrame *crfp, can_sysinterval_t timeout) { + // see CanTsListener and processCanRxMessage() + CanRxMessage msg; + if (listener.get(msg, timeout)) { + *crfp = msg.frame; + return CAN_MSG_OK; + } + return CAN_MSG_TIMEOUT; +} + +void canStreamInit(void) { + streamer.init(); +} + +msg_t canStreamAddToTxTimeout(size_t *np, const uint8_t *txbuf, sysinterval_t timeout) { + return state.streamAddToTxTimeout(np, txbuf, timeout); +} + +msg_t canStreamFlushTx(sysinterval_t timeout) { + return state.streamFlushTx(timeout); +} + +msg_t canStreamReceiveTimeout(size_t *np, uint8_t *rxbuf, sysinterval_t timeout) { + return state.streamReceiveTimeout(np, rxbuf, timeout); +} + +#endif /* HAL_USE_CAN */ diff --git a/firmware/console/binary/serial_can.h b/firmware/console/binary/serial_can.h index e9ff258c9f..0cc70cc41d 100644 --- a/firmware/console/binary/serial_can.h +++ b/firmware/console/binary/serial_can.h @@ -9,8 +9,32 @@ #pragma once #include "fifo_buffer.h" +#include "can_listener.h" +#include "can_msg_tx.h" + +#if !EFI_UNIT_TEST +#define can_msg_t msg_t +#define can_sysinterval_t sysinterval_t +#define CAN_MSG_OK MSG_OK +#define CAN_MSG_TIMEOUT MSG_TIMEOUT +#else +#include "can_mocks.h" +#endif /* EFI_UNIT_TEST */ + + +#define CAN_TIME_IMMEDIATE ((can_sysinterval_t)0) + +#define CAN_FIFO_BUF_SIZE 64 +#define CAN_FIFO_FRAME_SIZE 8 + +// todo: find a better place for these defs +#define CAN_SERIAL_RX_ID 0x100 +#define CAN_SERIAL_TX_ID 0x102 + +#define CAN_FLOW_STATUS_OK 0 +#define CAN_FLOW_STATUS_WAIT_MORE 1 +#define CAN_FLOW_STATUS_ABORT 2 -#define CAN_TX_ID 0x102 enum IsoTpFrameType { ISO_TP_FRAME_SINGLE = 0, @@ -33,10 +57,17 @@ public: int separationTime; }; +// We need an abstraction layer for unit-testing +class ICanStreamer { +public: + 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; +}; + class CanStreamerState { public: - fifo_buffer rxFifoBuf; - fifo_buffer txFifoBuf; + fifo_buffer rxFifoBuf; + fifo_buffer txFifoBuf; #if defined(TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME) // used to restore the original packet with CRC @@ -47,21 +78,70 @@ public: int waitingForNumBytes = 0; int waitingForFrameIndex = 0; - event_listener_t el; + ICanStreamer *streamer; public: - int sendFrame(CANDriver *canp, const IsoTpFrameHeader & header, const uint8_t *data, int num); - int receiveFrame(CANDriver *canp, CANRxFrame *rxmsg, uint8_t *buf, int num); + CanStreamerState(ICanStreamer *s) : streamer(s) {} + + int sendFrame(const IsoTpFrameHeader & header, const uint8_t *data, int num, can_sysinterval_t timeout); + int receiveFrame(CANRxFrame *rxmsg, uint8_t *buf, int num, can_sysinterval_t timeout); int getDataFromFifo(uint8_t *rxbuf, size_t &numBytes); // returns the number of bytes sent - int sendDataTimeout(CANDriver *canp, const uint8_t *txbuf, int numBytes, sysinterval_t timeout); + int sendDataTimeout(const uint8_t *txbuf, int numBytes, can_sysinterval_t timeout); + + // streaming support for TS I/O (see tunerstudio_io.cpp) + can_msg_t streamAddToTxTimeout(size_t *np, const uint8_t *txbuf, can_sysinterval_t timeout); + can_msg_t streamFlushTx(can_sysinterval_t timeout); + can_msg_t streamReceiveTimeout(size_t *np, uint8_t *rxbuf, can_sysinterval_t timeout); }; -void canInit(CANDriver *canp); +class CanRxMessage { +public: + CanRxMessage() {} + CanRxMessage(const CANRxFrame &f) { + frame = f; + } + CanRxMessage(const CanRxMessage& msg) : frame(msg.frame) {} + CanRxMessage& operator=(const CanRxMessage& msg) { + frame = msg.frame; + return *this; + } + +public: + CANRxFrame frame; +}; + +class CanTsListener : public CanListener { +public: + CanTsListener() + : CanListener(CAN_SERIAL_RX_ID) + { + } + + virtual void decodeFrame(const CANRxFrame& frame, efitick_t nowNt); + + bool get(CanRxMessage &item, int timeout) { + return rxFifo.get(item, timeout); + } + +protected: + fifo_buffer_sync rxFifo; +}; + +#if HAL_USE_CAN +class CanStreamer : public ICanStreamer { +public: + void init(); + + virtual can_msg_t transmit(canmbx_t mailbox, const CanTxMessage *ctfp, can_sysinterval_t timeout) override; + virtual can_msg_t receive(canmbx_t mailbox, CANRxFrame *crfp, can_sysinterval_t timeout) override; +}; + +void canStreamInit(void); // we don't have canStreamSendTimeout() because we need to "bufferize" the stream and send it in fixed-length packets -msg_t canAddToTxStreamTimeout(CANDriver *canp, size_t *np, const uint8_t *txbuf, sysinterval_t timeout); -msg_t canFlushTxStream(CANDriver *canp, sysinterval_t timeout); - -msg_t canStreamReceiveTimeout(CANDriver *canp, size_t *np, uint8_t *rxbuf, sysinterval_t timeout); +msg_t canStreamAddToTxTimeout(size_t *np, const uint8_t *txbuf, sysinterval_t timeout); +msg_t canStreamFlushTx(sysinterval_t timeout); +msg_t canStreamReceiveTimeout(size_t *np, uint8_t *rxbuf, sysinterval_t timeout); +#endif /* HAL_USE_CAN */ diff --git a/firmware/console/binary/ts_can_channel.cpp b/firmware/console/binary/ts_can_channel.cpp index 5e84260485..808de80968 100644 --- a/firmware/console/binary/ts_can_channel.cpp +++ b/firmware/console/binary/ts_can_channel.cpp @@ -1,41 +1,57 @@ -// TODO: @andreika-git finish this implementation +/** + * @file This file implements CAN-to-TS bridge. + * + * @date Apr 24, 2021 + * @author andreika + * @author Andrey Belomutskiy, (c) 2012-2021 + */ +#include "pch.h" +#include "tunerstudio.h" #include "tunerstudio_io.h" -#ifdef TS_CAN_DEVICE +#ifdef EFI_CAN_SERIAL #include "serial_can.h" +#include "can_hw.h" + +#if !EFI_CAN_SUPPORT +#error "EFI_CAN_SERIAL requires EFI_CAN_SUPPORT" +#endif + class CanTsChannel : public TsChannelBase { +public: void start(); // TsChannelBase implementation void write(const uint8_t* buffer, size_t size) override; size_t readTimeout(uint8_t* buffer, size_t size, int timeout) override; void flush() override; - bool isReady() override; + bool isReady() const override; void stop() override; // Special override for writeCrcPacket for small packets void writeCrcPacket(uint8_t responseCode, const uint8_t* buf, size_t size) override; }; -static CANConfig tsCanConfig = { CAN_MCR_ABOM | CAN_MCR_AWUM | CAN_MCR_TXFP, CAN_BTR_500 }; void CanTsChannel::start() { - efiSetPadMode("ts can rx", GPIOG_13/*engineConfiguration->canRxPin*/, PAL_MODE_ALTERNATE(TS_CAN_AF)); // CAN2_RX2_0 - efiSetPadMode("ts can tx", GPIOG_14/*engineConfiguration->canTxPin*/, PAL_MODE_ALTERNATE(TS_CAN_AF)); // CAN2_TX2_0 + if (!getIsCanEnabled()) { + warning(CUSTOM_ERR_CAN_CONFIGURATION, "CAN not enabled"); + return; + } - canStart(&TS_CAN_DEVICE, &tsCanConfig); - canInit(&TS_CAN_DEVICE); + if (!CONFIG(canReadEnabled) || !CONFIG(canWriteEnabled)) { + warning(CUSTOM_ERR_CAN_CONFIGURATION, "CAN read or write not enabled"); + } } void CanTsChannel::stop() { - canStop(&TS_CAN_DEVICE); } void CanTsChannel::writeCrcPacket(uint8_t responseCode, const uint8_t* buf, size_t size) { #ifdef TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME - // a special case for short packets: we can sent them in 1 frame, without CRC & size, + // a special case for short packets: we can send them in 1 frame, without CRC & size, // because the CAN protocol is already protected by its own checksum. if ((size + 1) <= 7) { write(&responseCode, 1); // header without size @@ -45,28 +61,48 @@ void CanTsChannel::writeCrcPacket(uint8_t responseCode, const uint8_t* buf, size flush(); return; } -#endif /* TS_CAN_DEVICE */ +#endif /* TS_CAN_DEVICE_SHORT_PACKETS_IN_ONE_FRAME */ // Packet too large, use default implementation TsChannelBase::writeCrcPacket(responseCode, buf, size); } void CanTsChannel::write(const uint8_t* buffer, size_t size) { - canAddToTxStreamTimeout(&TS_CAN_DEVICE, &size, buffer, BINARY_IO_TIMEOUT); + canStreamAddToTxTimeout(&size, buffer, BINARY_IO_TIMEOUT); } size_t CanTsChannel::readTimeout(uint8_t* buffer, size_t size, int timeout) { - canStreamReceiveTimeout(&TS_CAN_DEVICE, &size, buffer, timeout); + canStreamReceiveTimeout(&size, buffer, timeout); + //!!!!!!!!!!!!! + efiPrintf("--RT: %d %02x", size, (size > 0 ? buffer[0] : 0)); return size; } void CanTsChannel::flush() { - canFlushTxStream(&TS_CAN_DEVICE); + canStreamFlushTx(BINARY_IO_TIMEOUT); } -bool CanTsChannel::isReady() { +bool CanTsChannel::isReady() const { // this channel is always ready return true; } -#endif // def TS_CAN_DEVICE +static CanTsChannel canChannel; + +struct CanTsThread : public TunerstudioThread { + CanTsThread() : TunerstudioThread("CAN TS Channel") { } + + TsChannelBase* setupChannel() override { + canChannel.start(); + return &canChannel; + } +}; + +static CanTsThread canTsThread; + +void startCanConsole() { + canTsThread.Start(); + canStreamInit(); +} + +#endif // def EFI_CAN_SERIAL diff --git a/firmware/console/binary/tunerstudio.mk b/firmware/console/binary/tunerstudio.mk index 0f9b01944b..f62c5b6df3 100644 --- a/firmware/console/binary/tunerstudio.mk +++ b/firmware/console/binary/tunerstudio.mk @@ -3,6 +3,7 @@ TUNERSTUDIO_SRC_CPP = $(PROJECT_DIR)/console/binary/tunerstudio_io.cpp \ $(PROJECT_DIR)/console/binary/tunerstudio_io_serial.cpp \ $(PROJECT_DIR)/console/binary/tunerstudio_io_serial_ports.cpp \ $(PROJECT_DIR)/console/binary/ts_can_channel.cpp \ + $(PROJECT_DIR)/console/binary/serial_can.cpp \ $(PROJECT_DIR)/console/binary/tunerstudio.cpp \ $(PROJECT_DIR)/console/binary/tunerstudio_commands.cpp \ $(PROJECT_DIR)/console/binary/bluetooth.cpp \ diff --git a/firmware/console/binary/tunerstudio_io.h b/firmware/console/binary/tunerstudio_io.h index f09a120df3..879416c825 100644 --- a/firmware/console/binary/tunerstudio_io.h +++ b/firmware/console/binary/tunerstudio_io.h @@ -50,7 +50,7 @@ public: // Base functions that use the above virtual implementation size_t read(uint8_t* buffer, size_t size); -#ifdef TS_CAN_DEVICE +#ifdef EFI_CAN_SERIAL virtual // CAN device needs this function to be virtual for small-packet optimization #endif void writeCrcPacket(uint8_t responseCode, const uint8_t* buf, size_t size); @@ -123,4 +123,6 @@ protected: void startSerialChannels(); SerialTsChannelBase* getBluetoothChannel(); +void startCanConsole(); + void sendOkResponse(TsChannelBase *tsChannel, ts_response_format_e mode); diff --git a/firmware/controllers/can/can_listener.h b/firmware/controllers/can/can_listener.h index 21fe403e5e..e7ad44229d 100644 --- a/firmware/controllers/can/can_listener.h +++ b/firmware/controllers/can/can_listener.h @@ -11,21 +11,21 @@ class CanListener { public: - CanListener(uint32_t eid) - : m_eid(eid) + CanListener(uint32_t id) + : m_id(id) { } CanListener* processFrame(const CANRxFrame& frame, efitick_t nowNt) { - if (CAN_EID(frame) == m_eid) { + if (CAN_ID(frame) == m_id) { decodeFrame(frame, nowNt); } return m_next; } - uint32_t getEid() { - return m_eid; + uint32_t getId() { + return m_id; } void setNext(CanListener* next) { @@ -41,5 +41,5 @@ protected: CanListener* m_next = nullptr; private: - const uint32_t m_eid; + const uint32_t m_id; }; diff --git a/firmware/controllers/can/can_rx.cpp b/firmware/controllers/can/can_rx.cpp index 389f1c8f27..2467a90b6e 100644 --- a/firmware/controllers/can/can_rx.cpp +++ b/firmware/controllers/can/can_rx.cpp @@ -70,10 +70,8 @@ static void printPacket(const CANRxFrame &rx) { // only print info if we're in can debug mode // internet people use both hex and decimal to discuss packed IDs, for usability it's better to print both right here - efiPrintf("CAN_rx %x %d %x %x %x %x %x %x %x %x %x", - CAN_SID(rx), - CAN_SID(rx), rx.DLC, - rx.data8[0], rx.data8[1], rx.data8[2], rx.data8[3], + efiPrintf("CAN_rx %x(%d) %d: %02x %02x %02x %02x %02x %02x %02x %02x", CAN_SID(rx), + CAN_SID(rx), rx.DLC, rx.data8[0], rx.data8[1], rx.data8[2], rx.data8[3], rx.data8[4], rx.data8[5], rx.data8[6], rx.data8[7]); } diff --git a/firmware/controllers/can/can_tx.cpp b/firmware/controllers/can/can_tx.cpp index 4cd3487954..071d491a0f 100644 --- a/firmware/controllers/can/can_tx.cpp +++ b/firmware/controllers/can/can_tx.cpp @@ -52,7 +52,9 @@ void CanWrite::PeriodicTask(efitime_t nowNt) { cycleCount = 0; } +#ifndef DISABLE_CAN_UPDATE_DASH updateDash(cycle); +#endif /* DISABLE_CAN_UPDATE_DASH */ if (engineConfiguration->enableAemXSeries && cycle.isInterval(CI::_50ms)) { sendWidebandInfo(); diff --git a/firmware/hw_layer/drivers/can/can_hw.cpp b/firmware/hw_layer/drivers/can/can_hw.cpp index 44cb147441..918d0816bc 100644 --- a/firmware/hw_layer/drivers/can/can_hw.cpp +++ b/firmware/hw_layer/drivers/can/can_hw.cpp @@ -234,13 +234,15 @@ void startCanPins() { void initCan(void) { addConsoleAction("caninfo", canInfo); - isCanEnabled = + isCanEnabled = false; + + bool isCanConfigGood = (isBrainPinValid(engineConfiguration->canTxPin)) && // both pins are set... (isBrainPinValid(engineConfiguration->canRxPin)) && (engineConfiguration->canWriteEnabled || engineConfiguration->canReadEnabled) ; // ...and either read or write is enabled // nothing to do if we aren't enabled... - if (!isCanEnabled) { + if (!isCanConfigGood) { return; } @@ -284,6 +286,12 @@ void initCan(void) { if (engineConfiguration->canReadEnabled) { canRead.Start(); } + + isCanEnabled = true; +} + +bool getIsCanEnabled(void) { + return isCanEnabled; } #endif /* EFI_CAN_SUPPORT */ diff --git a/firmware/hw_layer/drivers/can/can_hw.h b/firmware/hw_layer/drivers/can/can_hw.h index 7ef9980346..7a86db5258 100644 --- a/firmware/hw_layer/drivers/can/can_hw.h +++ b/firmware/hw_layer/drivers/can/can_hw.h @@ -17,6 +17,7 @@ void setCanVss(int type); void stopCanPins(); void startCanPins(); void enableFrankensoCan(); +bool getIsCanEnabled(void); #if EFI_TUNER_STUDIO void postCanState(TunerStudioOutputChannels *tsOutputChannels); #endif /* EFI_TUNER_STUDIO */ diff --git a/firmware/rusefi.cpp b/firmware/rusefi.cpp index 7801ed825a..43158d368b 100644 --- a/firmware/rusefi.cpp +++ b/firmware/rusefi.cpp @@ -243,6 +243,11 @@ void runRusEfiWithConfig() { initMmcCard(); #endif /* EFI_FILE_LOGGING */ +#if EFI_CAN_SERIAL + // needs to be called after initCan() inside initHardware() + startCanConsole(); +#endif /* EFI_CAN_SERIAL */ + #if HW_CHECK_ALWAYS_STIMULATE // we need a special binary for final assembly check. We cannot afford to require too much software or too many steps // to be executed at the place of assembly diff --git a/unit_tests/tests/test_can_serial.cpp b/unit_tests/tests/test_can_serial.cpp new file mode 100644 index 0000000000..b28b04f643 --- /dev/null +++ b/unit_tests/tests/test_can_serial.cpp @@ -0,0 +1,3 @@ + + + diff --git a/unit_tests/tests/tests.mk b/unit_tests/tests/tests.mk index a0d3e74bda..7be15fc90f 100644 --- a/unit_tests/tests/tests.mk +++ b/unit_tests/tests/tests.mk @@ -91,6 +91,7 @@ TESTS_SRC_CPP = \ tests/test_gpio.cpp \ tests/test_limp.cpp \ tests/trigger/test_all_triggers.cpp \ + tests/test_can_serial.cpp \ tests/test_stepper.cpp \ tests/sensor/test_frequency_sensor.cpp \ tests/sensor/test_turbocharger_speed_converter.cpp \