#include "stdafx.h" #include "J2534Connection_ISO15765.h" #include "Timer.h" #include "constants_ISO15765.h" #include J2534Connection_ISO15765::J2534Connection_ISO15765( std::shared_ptr panda_dev, unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate ) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate), wftMax(0) { this->port = 0; if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) throw ERR_INVALID_BAUDRATE; panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); } unsigned long J2534Connection_ISO15765::validateTxMsg(PASSTHRU_MSG* msg) { if ((msg->DataSize < this->getMinMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) || msg->DataSize > this->getMaxMsgLen() + (msg_is_extaddr(msg) ? 1 : 0) || (val_is_29bit(msg->TxFlags) != this->_is_29bit() && !check_bmask(this->Flags, CAN_ID_BOTH)))) return ERR_INVALID_MSG; int fid = get_matching_out_fc_filter_id(std::string((const char*)msg->Data, msg->DataSize), msg->TxFlags, 0xFFFFFFFF); if (msg->DataSize > getMaxMsgSingleFrameLen() && fid == -1) return ERR_NO_FLOW_CONTROL; //11 bytes (4 for CANid, 7 payload) is max length of input frame. return STATUS_NOERROR; } std::shared_ptr J2534Connection_ISO15765::parseMessageTx(PASSTHRU_MSG& msg) { int fid = get_matching_out_fc_filter_id(std::string((const char*)msg.Data, msg.DataSize), msg.TxFlags, 0xFFFFFFFF); if (msg.DataSize > getMaxMsgSingleFrameLen() && fid == -1) 1; return std::dynamic_pointer_cast( std::make_shared(shared_from_this(), msg, (fid == -1) ? nullptr : this->filters[fid]) ); } //https://happilyembedded.wordpress.com/2016/02/15/can-multiple-frame-transmission/ void J2534Connection_ISO15765::processMessage(const J2534Frame& msg) { if (msg.ProtocolID != CAN) return; int fid = get_matching_in_fc_filter_id(msg, this->Flags); if (fid == -1) return; auto filter = this->filters[fid]; bool is_ext_addr = check_bmask(filter->flags, ISO15765_ADDR_TYPE); uint8_t addrlen = is_ext_addr ? 5 : 4; switch (msg_get_type(msg, addrlen)) { case FRAME_FLOWCTRL: { if (this->txbuff.size() == 0) return; if (msg.Data.size() < addrlen + 3) return; uint8_t flow_status = msg.Data[addrlen] & 0x0F; uint8_t block_size = msg.Data[addrlen + 1]; uint8_t st_min = msg.Data[addrlen + 2]; auto txConvo = std::static_pointer_cast(this->txbuff.front()); switch (flow_status) { case FLOWCTRL_CONTINUE: { if (st_min > 0xF9) break; if (st_min >= 0xf1 && st_min <= 0xf9) { txConvo->flowControlContinue(block_size, std::chrono::microseconds((st_min & 0x0F) * 100)); } else if(st_min <= 0x7f) { txConvo->flowControlContinue(block_size, std::chrono::microseconds(st_min * 1000)); } else { break; } txConvo->scheduleImmediate(); this->rescheduleExistingTxMsgs(); break; } case FLOWCTRL_WAIT: txConvo->flowControlWait(this->wftMax); break; case FLOWCTRL_ABORT: txConvo->flowControlAbort(); break; } break; } case FRAME_SINGLE: { this->rxConversations[fid] = nullptr; //Reset any current transaction. if (is_ext_addr) { if ((msg.Data[5] & 0x0F) > 6) return; } else { if ((msg.Data[4] & 0x0F) > 7) return; } J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp); if (msg.Data.size() != 8 && check_bmask(this->Flags, ISO15765_FRAME_PAD)) outframe.RxStatus |= ISO15765_PADDING_ERROR; if (is_ext_addr) outframe.RxStatus |= ISO15765_ADDR_TYPE; outframe.Data = msg.Data.substr(0, addrlen) + msg.Data.substr(addrlen + 1, msg.Data[addrlen]); outframe.ExtraDataIndex = outframe.Data.size(); addMsgToRxQueue(outframe); break; } case FRAME_FIRST: { if (msg.Data.size() < 12) { //A frame was received that could have held more data. //No examples of this protocol show that happening, so //it will be assumed that it is grounds to reset rx. this->rxConversations[fid] = nullptr; return; } J2534Frame outframe(ISO15765, msg.RxStatus | START_OF_MESSAGE, 0, msg.Timestamp); if (is_ext_addr) outframe.RxStatus |= ISO15765_ADDR_TYPE; outframe.Data = msg.Data.substr(0, addrlen); addMsgToRxQueue(outframe); this->rxConversations[fid] = std::make_shared( ((msg.Data[addrlen] & 0x0F) << 8) | msg.Data[addrlen + 1], msg.Data.substr(addrlen + 2, 12 - (addrlen + 2)), msg.RxStatus, filter); //TODO maybe the flow control should also be scheduled in the TX list. //Doing it this way because the filter can be 5 bytes in ext address mode. std::string flowfilter = filter->get_flowctrl(); uint32_t flow_addr = (((uint8_t)flowfilter[0]) << 24) | ((uint8_t)(flowfilter[1]) << 16) | ((uint8_t)(flowfilter[2]) << 8) | ((uint8_t)flowfilter[3]); std::string flowstrlresp; if (flowfilter.size() > 4) flowstrlresp += flowfilter[4]; flowstrlresp += std::string("\x30\x00\x00", 3); if (check_bmask(filter->flags, ISO15765_FRAME_PAD)) { flowstrlresp += std::string(8 - flowstrlresp.size(), '\x00'); } if (auto panda_dev_sp = this->panda_dev.lock()) { panda_dev_sp->panda->can_send(flow_addr, val_is_29bit(msg.RxStatus), (const uint8_t *)flowstrlresp.c_str(), (uint8_t)flowstrlresp.size(), panda::PANDA_CAN1); } break; } case FRAME_CONSEC: { auto& convo = this->rxConversations[fid]; if (convo == nullptr) return; if (!convo->rx_add_frame(msg.Data[addrlen], (is_ext_addr ? 6 : 7), msg.Data.substr(addrlen + 1))) { //Delete this conversation. convo = nullptr; return; } std::string final_msg; if (convo->flush_result(final_msg)) { convo = nullptr; J2534Frame outframe(ISO15765, msg.RxStatus, 0, msg.Timestamp); if (is_ext_addr) outframe.RxStatus |= ISO15765_ADDR_TYPE; outframe.Data = msg.Data.substr(0, addrlen) + final_msg; outframe.ExtraDataIndex = outframe.Data.size(); addMsgToRxQueue(outframe); } break; } } } void J2534Connection_ISO15765::setBaud(unsigned long BaudRate) { if (auto panda_dev = this->getPandaDev()) { if (BaudRate % 100 || BaudRate < 10000 || BaudRate > 5000000) throw ERR_NOT_SUPPORTED; panda_dev->panda->set_can_speed_cbps(panda::PANDA_CAN1, (uint16_t)(BaudRate / 100)); return J2534Connection::setBaud(BaudRate); } else { throw ERR_DEVICE_NOT_CONNECTED; } } long J2534Connection_ISO15765::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) { if (FilterType != FLOW_CONTROL_FILTER) return ERR_INVALID_FILTER_ID; return J2534Connection::PassThruStartMsgFilter(FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg, pFilterID); } int J2534Connection_ISO15765::get_matching_out_fc_filter_id(const std::string& msgdata, unsigned long flags, unsigned long flagmask) { for (unsigned int i = 0; i < this->filters.size(); i++) { if (this->filters[i] == nullptr) continue; auto filter = this->filters[i]->get_flowctrl(); if (filter == msgdata.substr(0, filter.size()) && (this->filters[i]->flags & flagmask) == (flags & flagmask)) return i; } return -1; } int J2534Connection_ISO15765::get_matching_in_fc_filter_id(const J2534Frame& msg, unsigned long flagmask) { for (unsigned int i = 0; i < this->filters.size(); i++) { if (this->filters[i] == nullptr) continue; if (this->filters[i]->check(msg) == FILTER_RESULT_MATCH && (this->filters[i]->flags & flagmask) == (msg.RxStatus & flagmask)) return i; } return -1; } void J2534Connection_ISO15765::processIOCTLSetConfig(unsigned long Parameter, unsigned long Value) { switch (Parameter) { case ISO15765_WFT_MAX: this->wftMax = Value; break; default: J2534Connection::processIOCTLSetConfig(Parameter, Value); } } unsigned long J2534Connection_ISO15765::processIOCTLGetConfig(unsigned long Parameter) { switch (Parameter) { case ISO15765_WFT_MAX: return this->wftMax; default: return J2534Connection::processIOCTLGetConfig(Parameter); } }