diff --git a/drivers/windows/.gitignore b/drivers/windows/.gitignore index dbe7ad5..d83d959 100644 --- a/drivers/windows/.gitignore +++ b/drivers/windows/.gitignore @@ -304,3 +304,4 @@ __pycache__/ # installer *.exe +*.zip \ No newline at end of file diff --git a/drivers/windows/README.md b/drivers/windows/README.md index 786a318..8efd881 100644 --- a/drivers/windows/README.md +++ b/drivers/windows/README.md @@ -17,10 +17,9 @@ ______/\\\\\\\\\\\____/\\\\\\\\\_______/\\\\\\\\\\\\\\\______/\\\\\\\\\\________ |_| (Code by Jessy Diamond Exum) ``` - # Installing J2534 driver: -[Download](https://github.com/commaai/panda/files/4017364/panda.J2534.driver.install.zip) +[Download](https://github.com/commaai/panda/files/4844692/panda.J2534.driver.install.zip) Depending on what version of windows you are on, you may need to separately install the WinUSB driver (see next section). @@ -80,8 +79,8 @@ features. - [ ] **J1850PWM** *(Outdated, and not physically supported by the panda)* - [X] **CAN** - [X] **ISO15765** -- [ ] **ISO9141** *(This protocol could be implemented if 5 BAUD init support is added to the panda.)* -- [ ] **ISO14230/KWP2000** *(Could be supported with FAST init, 5baud init if panda adds support for 5bps serial)* +- [X] **ISO9141** +- [X] **ISO14230/KWP2000** # Building the Project: @@ -116,7 +115,6 @@ code will not work, so without this file, the installer will refuse to build. - Apply a style-guide and consistent naming convention for Classes/Functions/Variables. - Send multiple messages (each with a different address) from a given connection at the same time. -- Implement ISO14230/KWP2000 FAST (LIN communication is already supported with the raw panda USB driver). - Find more documentation about SW_CAN_PS (Single Wire CAN, aka GMLAN). - Find example of client using a _PS version of a protocol (PS is pin select, and may support using different CAN buses). diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp index aa364b0..8b25f07 100644 --- a/drivers/windows/pandaJ2534DLL/J2534Connection.cpp +++ b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp @@ -7,7 +7,7 @@ J2534Connection::J2534Connection( unsigned long ProtocolID, unsigned long Flags, unsigned long BaudRate -) : panda_dev(panda_dev), ProtocolID(ProtocolID), Flags(Flags), BaudRate(BaudRate), port(0) { } +) : panda_dev(panda_dev), ProtocolID(ProtocolID), Flags(Flags), BaudRate(BaudRate), Parity(0), port(0) { } unsigned long J2534Connection::validateTxMsg(PASSTHRU_MSG* msg) { if (msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen()) @@ -60,7 +60,7 @@ long J2534Connection::PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMs long J2534Connection::PassThruWriteMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMsgs, unsigned long Timeout) { //There doesn't seem to be much reason to implement the timeout here. - for (int msgnum = 0; msgnum < *pNumMsgs; msgnum++) { + for (unsigned int msgnum = 0; msgnum < *pNumMsgs; msgnum++) { PASSTHRU_MSG* msg = &pMsg[msgnum]; if (msg->ProtocolID != this->ProtocolID) { *pNumMsgs = msgnum; @@ -87,7 +87,7 @@ long J2534Connection::PassThruStartPeriodicMsg(PASSTHRU_MSG *pMsg, unsigned long if (pMsg->ProtocolID != this->ProtocolID) return ERR_MSG_PROTOCOL_ID; if (TimeInterval < 5 || TimeInterval > 65535) return ERR_INVALID_TIME_INTERVAL; - for (int i = 0; i < this->periodicMessages.size(); i++) { + for (unsigned int i = 0; i < this->periodicMessages.size(); i++) { if (periodicMessages[i] != nullptr) continue; *pMsgID = i; @@ -114,11 +114,11 @@ long J2534Connection::PassThruStopPeriodicMsg(unsigned long MsgID) { long J2534Connection::PassThruStartMsgFilter(unsigned long FilterType, PASSTHRU_MSG *pMaskMsg, PASSTHRU_MSG *pPatternMsg, PASSTHRU_MSG *pFlowControlMsg, unsigned long *pFilterID) { - for (int i = 0; i < this->filters.size(); i++) { + for (unsigned int i = 0; i < this->filters.size(); i++) { if (filters[i] == nullptr) { try { auto newfilter = std::make_shared(this, FilterType, pMaskMsg, pPatternMsg, pFlowControlMsg); - for (int check_idx = 0; check_idx < filters.size(); check_idx++) { + for (unsigned int check_idx = 0; check_idx < filters.size(); check_idx++) { if (filters[check_idx] == nullptr) continue; if (filters[check_idx] == newfilter) { filters[i] = nullptr; @@ -147,28 +147,99 @@ long J2534Connection::PassThruIoctl(unsigned long IoctlID, void *pInput, void *p return STATUS_NOERROR; } -long J2534Connection::init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput) { return ERR_FAILED; } -long J2534Connection::initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput) { return ERR_FAILED; } +long J2534Connection::init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput) { + if (pInput->NumOfBytes == 1) { + if (auto panda_ps = this->panda_dev.lock()) { + auto resp = panda_ps->kline_five_baud_init(pInput->BytePtr[0]); + if (resp.size() > 0) { + auto key_bytes = resp.c_str(); + if (pOutput->NumOfBytes >= 1) { + pOutput->BytePtr[0] = key_bytes[0]; + } + if (pOutput->NumOfBytes >= 2) { + pOutput->BytePtr[1] = key_bytes[1]; + } + return STATUS_NOERROR; + } + } + } + + return ERR_FAILED; +} +long J2534Connection::initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput) { + if (auto panda_ps = this->panda_dev.lock()) { + auto start_comm = std::string((char*)pInput->Data, pInput->DataSize); + auto resp = panda_ps->kline_wakeup_start_comm(start_comm); + if (resp.size() > 0) { + pOutput->ProtocolID = pInput->ProtocolID; + pOutput->RxStatus = 0; + pOutput->TxFlags = 0; + pOutput->Timestamp = pInput->Timestamp; + pOutput->ExtraDataIndex = resp.size(); + memcpy(pOutput->Data, resp.c_str(), resp.size()); + pOutput->DataSize = resp.size(); + return STATUS_NOERROR; + } + } + + return ERR_FAILED; +} long J2534Connection::clearTXBuff() { if (auto panda_ps = this->panda_dev.lock()) { synchronized(staged_writes_lock) { this->txbuff = {}; - panda_ps->panda->can_clear(panda::PANDA_CAN1_TX); + switch (this->ProtocolID) + { + case CAN: + case CAN_PS: + case ISO15765: + case ISO15765_PS: + panda_ps->panda->can_clear(panda::PANDA_CAN1_TX); + break; + case ISO9141: + case ISO9141_PS: + case ISO14230: + case ISO14230_PS: + panda_ps->panda->serial_clear(panda::SERIAL_LIN1); + panda_ps->panda->serial_clear(panda::SERIAL_LIN2); + break; + default: + break; + } } + return STATUS_NOERROR; } - return STATUS_NOERROR; + return ERR_FAILED; } long J2534Connection::clearRXBuff() { if (auto panda_ps = this->panda_dev.lock()) { synchronized(messageRxBuff_mutex) { this->messageRxBuff = {}; - panda_ps->panda->can_clear(panda::PANDA_CAN_RX); + switch (this->ProtocolID) + { + case CAN: + case CAN_PS: + case ISO15765: + case ISO15765_PS: + panda_ps->panda->can_clear(panda::PANDA_CAN_RX); + break; + case ISO9141: + case ISO9141_PS: + case ISO14230: + case ISO14230_PS: + panda_ps->panda->serial_clear(panda::SERIAL_LIN1); + panda_ps->panda->serial_clear(panda::SERIAL_LIN2); + break; + default: + break; + } } + return STATUS_NOERROR; } - return STATUS_NOERROR; + return ERR_FAILED; } long J2534Connection::clearPeriodicMsgs() { - for (int i = 0; i < this->periodicMessages.size(); i++) { + for (unsigned int i = 0; i < this->periodicMessages.size(); i++) { if (periodicMessages[i] == nullptr) continue; this->periodicMessages[i]->cancel(); this->periodicMessages[i] = nullptr; @@ -185,6 +256,10 @@ void J2534Connection::setBaud(unsigned long baud) { this->BaudRate = baud; } +void J2534Connection::setParity(unsigned long parity) { + this->Parity = parity; +} + void J2534Connection::schedultMsgTx(std::shared_ptr msgout) { if (auto panda_ps = this->panda_dev.lock()) { synchronized(staged_writes_lock) { @@ -226,6 +301,9 @@ void J2534Connection::processIOCTLSetConfig(unsigned long Parameter, unsigned lo case LOOPBACK: // 0 (OFF), 1 (ON) [0] this->loopback = (Value != 0); break; + case PARITY: + this->setParity(Value); + break; case ISO15765_WFT_MAX: break; case NODE_ADDRESS: // J1850PWM Related (Not supported by panda). HDS requires these to 'work'. @@ -247,7 +325,6 @@ void J2534Connection::processIOCTLSetConfig(unsigned long Parameter, unsigned lo case TIDLE: case TINIL: case TWUP: - case PARITY: case T1_MAX: // SCI related options. The panda does not appear to support this case T2_MAX: case T3_MAX: @@ -259,9 +336,9 @@ void J2534Connection::processIOCTLSetConfig(unsigned long Parameter, unsigned lo } // reserved parameters usually mean special equiptment is required - if (Parameter >= 0x20) { - throw ERR_NOT_SUPPORTED; - } + //if (Parameter >= 0x20) { + // throw ERR_NOT_SUPPORTED; + //} } unsigned long J2534Connection::processIOCTLGetConfig(unsigned long Parameter) { diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection.h b/drivers/windows/pandaJ2534DLL/J2534Connection.h index 70f25a1..d73bdd8 100644 --- a/drivers/windows/pandaJ2534DLL/J2534Connection.h +++ b/drivers/windows/pandaJ2534DLL/J2534Connection.h @@ -51,14 +51,15 @@ public: //IOCTL functions - long init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput); - long initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput); + virtual long init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput); + virtual long initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput); long clearTXBuff(); long clearRXBuff(); long clearPeriodicMsgs(); long clearMsgFilters(); virtual void setBaud(unsigned long baud); + virtual void setParity(unsigned long parity); unsigned long getBaud() { return this->BaudRate; @@ -124,6 +125,7 @@ protected: unsigned long ProtocolID; unsigned long Flags; unsigned long BaudRate; + unsigned long Parity; unsigned long port; std::weak_ptr panda_dev; diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_ISO14230.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO14230.cpp new file mode 100644 index 0000000..ae327b3 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO14230.cpp @@ -0,0 +1,83 @@ +#include "stdafx.h" +#include "J2534Connection_ISO14230.h" +#include "MessageTx_ISO14230.h" +#include "Timer.h" + +J2534Connection_ISO14230::J2534Connection_ISO14230( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ) : J2534Connection(panda_dev, ProtocolID, Flags, BaudRate) { + this->port = 0; + + if (BaudRate % 100 || BaudRate < 2400 || BaudRate > 115200) + throw ERR_INVALID_BAUDRATE; + + panda_dev->panda->set_uart_baud(panda::SERIAL_LIN1, BaudRate); + panda_dev->panda->set_uart_baud(panda::SERIAL_LIN2, BaudRate); +}; + +unsigned long J2534Connection_ISO14230::validateTxMsg(PASSTHRU_MSG* msg) { + if (msg->DataSize < this->getMinMsgLen() || msg->DataSize > this->getMaxMsgLen()) + return ERR_INVALID_MSG; + return STATUS_NOERROR; +} + +std::shared_ptr J2534Connection_ISO14230::parseMessageTx(PASSTHRU_MSG& msg) { + return std::dynamic_pointer_cast(std::make_shared(shared_from_this(), msg)); +} + +void J2534Connection_ISO14230::setBaud(unsigned long BaudRate) { + if (auto panda_dev = this->getPandaDev()) { + if (BaudRate % 100 || BaudRate < 2400 || BaudRate > 115200) + throw ERR_NOT_SUPPORTED; + + panda_dev->panda->set_uart_baud(panda::SERIAL_LIN1, BaudRate); + panda_dev->panda->set_uart_baud(panda::SERIAL_LIN2, BaudRate); + return J2534Connection::setBaud(BaudRate); + } else { + throw ERR_DEVICE_NOT_CONNECTED; + } +} + +void J2534Connection_ISO14230::setParity(unsigned long Parity) { + if (auto panda_dev = this->getPandaDev()) { + panda::PANDA_SERIAL_PORT_PARITY parity; + switch (Parity) { + case 0: + parity = panda::PANDA_PARITY_OFF; + break; + case 1: + parity = panda::PANDA_PARITY_ODD; + break; + case 2: + parity = panda::PANDA_PARITY_EVEN; + break; + default: + throw ERR_NOT_SUPPORTED; + } + panda_dev->panda->set_uart_parity(panda::SERIAL_LIN1, parity); + panda_dev->panda->set_uart_parity(panda::SERIAL_LIN2, parity); + return J2534Connection::setParity(Parity); + } + else { + throw ERR_DEVICE_NOT_CONNECTED; + } +} + +void J2534Connection_ISO14230::processMessage(const J2534Frame& msg) { + FILTER_RESULT filter_res = FILTER_RESULT_NEUTRAL; + + for (auto filter : this->filters) { + if (filter == nullptr) continue; + FILTER_RESULT current_check_res = filter->check(msg); + if (current_check_res == FILTER_RESULT_BLOCK) return; + if (current_check_res == FILTER_RESULT_PASS) filter_res = FILTER_RESULT_PASS; + } + + if (filter_res == FILTER_RESULT_PASS) { + addMsgToRxQueue(J2534Frame(msg.ProtocolID, START_OF_MESSAGE, 0, 0)); + addMsgToRxQueue(msg); + } +} diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection_ISO14230.h b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO14230.h new file mode 100644 index 0000000..4553f90 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/J2534Connection_ISO14230.h @@ -0,0 +1,39 @@ +#pragma once + +#include "J2534Connection.h" +#include "panda_shared/panda.h" + +class J2534Connection_ISO14230 : public J2534Connection { +public: + J2534Connection_ISO14230( + std::shared_ptr panda_dev, + unsigned long ProtocolID, + unsigned long Flags, + unsigned long BaudRate + ); + + virtual unsigned long validateTxMsg(PASSTHRU_MSG* msg); + + virtual std::shared_ptr parseMessageTx(PASSTHRU_MSG& pMsg); + + virtual void setBaud(unsigned long baud); + virtual void setParity(unsigned long Parity); + + virtual unsigned long getMinMsgLen() { + return 2; + } + + virtual unsigned long getMaxMsgLen() { + return KLINE_MSG_MAX_LEN; + } + + virtual unsigned long getMaxMsgSingleFrameLen() { + return KLINE_MSG_MAX_LEN; + } + + virtual bool isProtoCan() { + return FALSE; + } + + virtual void processMessage(const J2534Frame& msg); +}; \ No newline at end of file diff --git a/drivers/windows/pandaJ2534DLL/J2534Frame.h b/drivers/windows/pandaJ2534DLL/J2534Frame.h index 2549216..c2dc44d 100644 --- a/drivers/windows/pandaJ2534DLL/J2534Frame.h +++ b/drivers/windows/pandaJ2534DLL/J2534Frame.h @@ -18,10 +18,20 @@ public: Data += msg_in.addr & 0xFF; Data += std::string((char*)&msg_in.dat, msg_in.len); Timestamp = msg_in.recv_time; + TxFlags = 0; RxStatus = (msg_in.addr_29b ? CAN_29BIT_ID : 0) | (msg_in.is_receipt ? TX_MSG_TYPE : 0); } + J2534Frame(unsigned long protocol, const panda::PANDA_KLINE_MSG& msg_in) { + ProtocolID = protocol; + ExtraDataIndex = msg_in.data.size() - (msg_in.valid ? 1 : 0); + Data = msg_in.data; + Timestamp = 0; + TxFlags = 0; + RxStatus = 0; + } + J2534Frame(const PASSTHRU_MSG& msg) { this->ProtocolID = msg.ProtocolID; this->RxStatus = msg.RxStatus; diff --git a/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp index 2d19e1f..7b9e093 100644 --- a/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp +++ b/drivers/windows/pandaJ2534DLL/J2534MessageFilter.cpp @@ -79,7 +79,7 @@ FILTER_RESULT J2534MessageFilter::check(const J2534Frame& msg) { if (msg.Data.size() < this->maskMsg.size()) { matches = FALSE; } else { - for (int i = 0; i < this->maskMsg.size(); i++) { + for (unsigned int i = 0; i < this->maskMsg.size(); i++) { if (this->patternMsg[i] != (msg.Data[i] & this->maskMsg[i])) { matches = FALSE; break; diff --git a/drivers/windows/pandaJ2534DLL/J2534register_x64.reg b/drivers/windows/pandaJ2534DLL/J2534register_x64.reg index 120ab39..87913c4 100644 Binary files a/drivers/windows/pandaJ2534DLL/J2534register_x64.reg and b/drivers/windows/pandaJ2534DLL/J2534register_x64.reg differ diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_ISO14230.cpp b/drivers/windows/pandaJ2534DLL/MessageTx_ISO14230.cpp new file mode 100644 index 0000000..47e2688 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_ISO14230.cpp @@ -0,0 +1,40 @@ +#include "stdafx.h" +#include "MessageTx_ISO14230.h" +#include "J2534Connection_ISO14230.h" + +MessageTx_ISO14230::MessageTx_ISO14230( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send +) : MessageTx(connection_in, to_send), sentyet(FALSE), txInFlight(FALSE) {}; + +void MessageTx_ISO14230::execute() { + if (auto conn_sp = this->connection.lock()) { + if (auto panda_dev_sp = conn_sp->getPandaDev()) { + if (panda_dev_sp->kline_send(this->fullmsg.Data)) { + if (auto conn_sp = this->connection.lock()) + { + if (conn_sp->loopback) { + auto echo = J2534Frame(conn_sp->getProtocol(), TX_MSG_TYPE, 0, this->fullmsg.Timestamp); + echo.Data = std::string(this->fullmsg.Data); + echo.ExtraDataIndex = this->fullmsg.Data.size(); + conn_sp->addMsgToRxQueue(J2534Frame(conn_sp->getProtocol(), START_OF_MESSAGE, 0, this->fullmsg.Timestamp)); + conn_sp->addMsgToRxQueue(echo); + } + } + this->txInFlight = FALSE; + this->sentyet = TRUE; + } + // remove action since echo was read back in kline_send() + panda_dev_sp->removeConnectionTopAction(conn_sp, shared_from_this()); + } + } +} + +BOOL MessageTx_ISO14230::checkTxReceipt(J2534Frame frame) { + throw "not implemented!"; +} + +void MessageTx_ISO14230::reset() { + sentyet = FALSE; + txInFlight = FALSE; +} diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_ISO14230.h b/drivers/windows/pandaJ2534DLL/MessageTx_ISO14230.h new file mode 100644 index 0000000..bd32263 --- /dev/null +++ b/drivers/windows/pandaJ2534DLL/MessageTx_ISO14230.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include "MessageTx.h" + +class J2534Connection; + +class MessageTx_ISO14230 : public MessageTx +{ +public: + MessageTx_ISO14230( + std::shared_ptr connection_in, + PASSTHRU_MSG& to_send + ); + + virtual void execute(); + + virtual BOOL checkTxReceipt(J2534Frame frame); + + virtual BOOL isFinished() { + return !txInFlight && sentyet; + }; + + virtual BOOL txReady() { + return !sentyet; + }; + + virtual void reset(); + +private: + BOOL sentyet; + BOOL txInFlight; +}; diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp index e250ded..69a0326 100644 --- a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp @@ -12,6 +12,9 @@ PandaJ2534Device::PandaJ2534Device(std::unique_ptr new_panda) : tx this->thread_kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); + DWORD klineListenThreadID; + this->kline_recv_handle = CreateThread(NULL, 0, _kline_recv_threadBootstrap, (LPVOID)this, 0, &klineListenThreadID); + DWORD canListenThreadID; this->can_recv_handle = CreateThread(NULL, 0, _can_recv_threadBootstrap, (LPVOID)this, 0, &canListenThreadID); @@ -25,7 +28,11 @@ PandaJ2534Device::PandaJ2534Device(std::unique_ptr new_panda) : tx PandaJ2534Device::~PandaJ2534Device() { SetEvent(this->thread_kill_event); - DWORD res = WaitForSingleObject(this->can_recv_handle, INFINITE); + + DWORD res = WaitForSingleObject(this->kline_recv_handle, INFINITE); + CloseHandle(this->kline_recv_handle); + + res = WaitForSingleObject(this->can_recv_handle, INFINITE); CloseHandle(this->can_recv_handle); res = WaitForSingleObject(this->can_process_handle, INFINITE); @@ -73,6 +80,89 @@ DWORD PandaJ2534Device::addChannel(std::shared_ptr& conn, unsig return STATUS_NOERROR; } +std::string PandaJ2534Device::kline_five_baud_init(uint8_t addr) { + synchronized(kline_rx_mutex) { + Sleep(300); // W1 + this->panda->kline_slow_init(true, true, addr); + // wakeup sometimes adds a leading null char + this->panda->serial_clear(panda::SERIAL_LIN1); + this->panda->serial_clear(panda::SERIAL_LIN2); + // read 0x55 KB1 KB2 + auto key_bytes = this->panda->serial_read(panda::SERIAL_LIN1, 3, 300); + auto bytes = key_bytes.c_str(); + if (key_bytes.size() == 3 && bytes[0] == 0x55) { + Sleep(25); // W4 + // send inverted KB2 + auto kb2_inv = std::string(1, ~bytes[2]); + if (this->panda->serial_write(panda::SERIAL_LIN1, kb2_inv)) { + // read addr inverted + auto addr_inv = this->panda->serial_read(panda::SERIAL_LIN1, 1, 50); + if (addr_inv.size() == 1 && addr_inv.c_str()[0] == ~addr) { + // return only KB1 KB2 + return key_bytes.substr(1, 2); + } + } + } + + } + + return std::string(); +} + +std::string PandaJ2534Device::kline_wakeup_start_comm(std::string& start_comm) { + synchronized(kline_rx_mutex) { + Sleep(25); + this->panda->kline_fast_init(true, true); + // wakeup sometimes adds a leading null char + this->panda->serial_clear(panda::SERIAL_LIN1); + this->panda->serial_clear(panda::SERIAL_LIN2); + // send start communication message + if (this->panda->kline_send(panda::SERIAL_LIN1, start_comm)) { + Sleep(25); + // read start communication response + return this->panda->serial_read(panda::SERIAL_LIN1, KLINE_MSG_MAX_LEN, 20); + } + } + + return std::string(); +} + +BOOL PandaJ2534Device::kline_send(std::string& data) { + // since send reads echo, block rx thread + synchronized(kline_rx_mutex) { + return this->panda->kline_send(panda::SERIAL_LIN1, data) ? TRUE : FALSE; + } +} + +DWORD PandaJ2534Device::kline_recv_thread() { + this->panda->serial_clear(panda::SERIAL_LIN1); + while (true) { + if (!WaitForSingleObject(this->thread_kill_event, 0)) { + break; + } + + std::vector msg_recv; + synchronized(kline_rx_mutex) { + msg_recv = this->panda->kline_recv(panda::SERIAL_LIN1); + } + if (msg_recv.empty()) { + Sleep(1); + continue; + } + + for (auto msg : msg_recv) { + for (auto& conn : this->connections) { + if (conn != nullptr && !conn->isProtoCan()) { + J2534Frame msg_out(conn->getProtocol(), msg); + conn->processMessage(msg_out); + } + } + } + } + + return 0; +} + DWORD PandaJ2534Device::can_recv_thread() { this->panda->can_clear(panda::PANDA_CAN_RX); this->panda->can_rx_q_push(this->thread_kill_event); diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h index 32004ff..d5f9130 100644 --- a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h @@ -52,9 +52,19 @@ public: //transmission is complete. This tracks what is still waiting to hear an echo. std::queue> txMsgsAwaitingEcho; + std::string kline_five_baud_init(uint8_t addr); + std::string kline_wakeup_start_comm(std::string& start_comm); + BOOL kline_send(std::string& data); + private: HANDLE thread_kill_event; + HANDLE kline_recv_handle; + static DWORD WINAPI _kline_recv_threadBootstrap(LPVOID This) { + return ((PandaJ2534Device*)This)->kline_recv_thread(); + } + DWORD kline_recv_thread(); + HANDLE can_recv_handle; static DWORD WINAPI _can_recv_threadBootstrap(LPVOID This) { return ((PandaJ2534Device*)This)->can_recv_thread(); @@ -80,4 +90,6 @@ private: std::set> ConnTxSet; Mutex connTXSet_mutex; BOOL txInProgress; + + Mutex kline_rx_mutex; }; diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp index 096e441..ea877e1 100644 --- a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.cpp @@ -7,6 +7,7 @@ #include "J2534_v0404.h" #include "panda_shared/panda.h" #include "J2534Connection.h" +#include "J2534Connection_ISO14230.h" #include "J2534Connection_CAN.h" #include "J2534Connection_ISO15765.h" #include "PandaJ2534Device.h" @@ -95,10 +96,10 @@ PANDAJ2534DLL_API long PTAPI PassThruOpen(void *pName, unsigned long *pDevice if (new_panda == nullptr) { if(sn == "" && pandas.size() == 1) return ret_code(ERR_DEVICE_IN_USE); - for (auto& pn : pandas) { - if (pn->panda->get_usb_sn() == sn) - return ret_code(ERR_DEVICE_IN_USE); - } + //for (auto& pn : pandas) { + // if (pn->panda->get_usb_sn() == sn) + // return ret_code(ERR_DEVICE_IN_USE); + //} return ret_code(ERR_DEVICE_NOT_CONNECTED); } @@ -140,36 +141,36 @@ PANDAJ2534DLL_API long PTAPI PassThruConnect(unsigned long DeviceID, unsigned lo switch (ProtocolID) { //SW seems to refer to Single Wire. https://www.nxp.com/files-static/training_pdf/20451_BUS_COMM_WBT.pdf //SW_ protocols may be touched on here: https://www.iso.org/obp/ui/#iso:std:iso:22900:-2:ed-1:v1:en - //case J1850VPW: // These protocols are outdated and will not be supported. HDS wants them to not fail to open. - //case J1850PWM: // ^-- it appears HDS no longer needs this, and TIS needs it disabled --^ - //case J1850VPW_PS: - //case J1850PWM_PS: - case ISO9141: //This protocol could be implemented if 5 BAUD init support is added to the panda. - case ISO9141_PS: - case ISO14230: //Only supporting Fast init until panda adds support for 5 BAUD init. - case ISO14230_PS: - conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); - break; - case CAN: - case CAN_PS: - //case SW_CAN_PS: - conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); - break; - case ISO15765: - case ISO15765_PS: - conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); - break; - //case SW_ISO15765_PS: // SW = Single Wire. GMLAN is a SW CAN protocol - //case GM_UART_PS: // PS = Pin Select. Handles different ports. - //Looks like SCI based protocols may not be compatible with the panda: - //http://mdhmotors.com/can-communications-vehicle-network-protocols/3/ - //case SCI_A_ENGINE: - //case SCI_A_TRANS: - //case SCI_B_ENGINE: + //case J1850VPW: // These protocols are outdated and will not be supported. HDS wants them to not fail to open. + //case J1850PWM: // ^-- it appears HDS no longer needs this, and TIS needs it disabled --^ + //case J1850VPW_PS: + //case J1850PWM_PS: + case ISO9141: + //case ISO9141_PS: + case ISO14230: + //case ISO14230_PS: + conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); + break; + case CAN: + //case CAN_PS: + //case SW_CAN_PS: + conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); + break; + case ISO15765: + //case ISO15765_PS: + conn = std::make_shared(panda, ProtocolID, Flags, BaudRate); + break; + //case SW_ISO15765_PS: // SW = Single Wire. GMLAN is a SW CAN protocol + //case GM_UART_PS: // PS = Pin Select. Handles different ports. + //Looks like SCI based protocols may not be compatible with the panda: + //http://mdhmotors.com/can-communications-vehicle-network-protocols/3/ + //case SCI_A_ENGINE: + //case SCI_A_TRANS: + //case SCI_B_ENGINE: //case SCI_B_TRANS: - //case J2610_PS: - default: - return ret_code(ERR_INVALID_PROTOCOL_ID); + //case J2610_PS: + default: + return ret_code(ERR_INVALID_PROTOCOL_ID); } } catch (int e) { return ret_code(e); @@ -424,6 +425,7 @@ PANDAJ2534DLL_API long PTAPI PassThruIoctl(unsigned long ChannelID, unsigned lon break; default: printf("Got unknown IIOCTL %X\n", IoctlID); + return ret_code(ERR_INVALID_IOCTL_ID); } return ret_code(STATUS_NOERROR); diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj index bc9d145..36b3099 100644 --- a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj @@ -91,6 +91,7 @@ + @@ -103,6 +104,7 @@ + @@ -122,11 +124,13 @@ + + diff --git a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters index 57f7cef..de80e89 100644 --- a/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters +++ b/drivers/windows/pandaJ2534DLL/pandaJ2534DLL.vcxproj.filters @@ -1,155 +1,173 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - {a4cd0bce-0a2a-43d9-9c9f-b21a3b607e90} - - - {a85ee263-380d-4d37-b167-6629cfd5177f} - - - {010a0176-a146-4d3a-824a-fd683904774d} - - - {71c9502a-ee59-4d5e-873f-c9cc792e7c76} - - - {4fd3183a-c457-430c-b762-f767a5788bca} - - - {53cd179e-22d8-43e2-bc61-516d3861fae6} - - - {08d548b5-4d0b-4ce4-85e6-5ff3fc987758} - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files\J2534_CAN - - - Header Files\J2534_ISO15765 - - - Header Files\depends - - - Header Files\depends - - - Header Files\boilerplate - - - Header Files\boilerplate - - - Header Files\boilerplate - - - Header Files\boilerplate - - - Header Files - - - Header Files\J2534_ISO15765 - - - Header Files - - - Header Files\J2534_ISO15765 - - - Header Files\J2534_CAN - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files\J2534_CAN - - - Source Files\J2534_ISO15765 - - - Source Files\boilerplate - - - Source Files\boilerplate - - - Source Files\J2534_ISO15765 - - - Source Files - - - Source Files\J2534_CAN - - - Source Files - - - - - Resource Files - - - - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {a4cd0bce-0a2a-43d9-9c9f-b21a3b607e90} + + + {a85ee263-380d-4d37-b167-6629cfd5177f} + + + {010a0176-a146-4d3a-824a-fd683904774d} + + + {71c9502a-ee59-4d5e-873f-c9cc792e7c76} + + + {4fd3183a-c457-430c-b762-f767a5788bca} + + + {53cd179e-22d8-43e2-bc61-516d3861fae6} + + + {08d548b5-4d0b-4ce4-85e6-5ff3fc987758} + + + {b5c1874e-d3f8-4465-89c5-2e2b7e9f4fa4} + + + {b5a39015-f3ca-4888-bd5f-785aeec91345} + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\J2534_CAN + + + Header Files\J2534_ISO15765 + + + Header Files\depends + + + Header Files\depends + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files\boilerplate + + + Header Files + + + Header Files\J2534_ISO15765 + + + Header Files + + + Header Files\J2534_ISO15765 + + + Header Files\J2534_CAN + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\J2534_ISO14230 + + + Header Files\J2534_ISO14230 + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\J2534_CAN + + + Source Files\J2534_ISO15765 + + + Source Files\boilerplate + + + Source Files\boilerplate + + + Source Files\J2534_ISO15765 + + + Source Files + + + Source Files\J2534_CAN + + + Source Files + + + Source Files\J2534_ISO14230 + + + Source Files\J2534_ISO14230 + + + + + Resource Files + + + + + \ No newline at end of file diff --git a/drivers/windows/panda_install.nsi b/drivers/windows/panda_install.nsi index 47a4423..4e3929e 100644 --- a/drivers/windows/panda_install.nsi +++ b/drivers/windows/panda_install.nsi @@ -131,7 +131,7 @@ Section "J2534 Driver" WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_B_ENGINE" 00000000 WriteRegDWORD HKLM "${J2534_Reg_Path}" "SCI_B_TRANS" 00000000 WriteRegDWORD HKLM "${J2534_Reg_Path}" "J1850PWM" 00000000 - WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO9141" 00000000 + WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO9141" 00000001 WriteRegDWORD HKLM "${J2534_Reg_Path}" "ISO14230" 00000001 WriteRegStr HKLM "${J2534_Reg_Path}" "Name" "panda" WriteRegStr HKLM "${J2534_Reg_Path}" "Vendor" "comma.ai" diff --git a/drivers/windows/panda_shared/panda.cpp b/drivers/windows/panda_shared/panda.cpp index 8425ec9..2c13687 100644 --- a/drivers/windows/panda_shared/panda.cpp +++ b/drivers/windows/panda_shared/panda.cpp @@ -11,6 +11,9 @@ #define CAN_TRANSMIT 1 #define CAN_EXTENDED 4 +#define KLINE_HEADER_FMT_ADDR_MASK 0xC0 +#define KLINE_HEADER_FMT_LEN_MASK 0x3F + using namespace panda; Panda::Panda( @@ -451,7 +454,7 @@ bool Panda::can_rx_q_push(HANDLE kill_event, DWORD timeoutms) { return TRUE; } -void Panda::can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count) { +void Panda::can_rx_q_pop(PANDA_CAN_MSG msg_out[], int& count) { count = 0; // No data left in queue @@ -461,8 +464,8 @@ void Panda::can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count) { } auto r_ptr = this->r_ptr; - for (int i = 0; i < this->can_rx_q[r_ptr].count; i += sizeof(PANDA_CAN_MSG_INTERNAL)) { - auto in_msg_raw = (PANDA_CAN_MSG_INTERNAL *)(this->can_rx_q[r_ptr].data + i); + for (unsigned long i = 0; i < this->can_rx_q[r_ptr].count; i += sizeof(PANDA_CAN_MSG_INTERNAL)) { + auto in_msg_raw = (PANDA_CAN_MSG_INTERNAL*)(this->can_rx_q[r_ptr].data + i); msg_out[count] = parse_can_recv(in_msg_raw); ++count; } @@ -481,7 +484,7 @@ std::vector Panda::can_recv() { return msg_recv; for (int i = 0; i < retcount; i += sizeof(PANDA_CAN_MSG_INTERNAL)) { - PANDA_CAN_MSG_INTERNAL *in_msg_raw = (PANDA_CAN_MSG_INTERNAL *)(buff + i); + PANDA_CAN_MSG_INTERNAL* in_msg_raw = (PANDA_CAN_MSG_INTERNAL*)(buff + i); auto in_msg = parse_can_recv(in_msg_raw); msg_recv.push_back(in_msg); } @@ -508,15 +511,127 @@ std::string Panda::serial_read(PANDA_SERIAL_PORT port_number) { return result; } -int Panda::serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len) { - std::string dat; - dat += port_number; - dat += std::string((char*)buff, len); - int retcount; - if (this->bulk_write(2, dat.c_str(), len+1, (PULONG)&retcount, 0) == FALSE) return -1; +std::string Panda::serial_read(PANDA_SERIAL_PORT port_number, unsigned int len, unsigned int timeout_ms) { + std::string result = std::string(); + auto ms_remaining = timeout_ms; + char buff[0x40]; + while (len > 0 && ms_remaining > 0) { + int retlen = this->control_transfer(REQUEST_IN, 0xe0, port_number, 0, &buff, min(len, 0x40), 0); + if (retlen <= 0) { + ms_remaining -= 1; + Sleep(1); + continue; + } + result += std::string(buff, retlen); + len -= retlen; + ms_remaining = timeout_ms; + } + return result; +} + +int Panda::serial_write(PANDA_SERIAL_PORT port_number, const std::string& data) { + int retcount = 0; + for (int i = 0; i < data.size(); i += 0x3F) { + std::string slice = std::string(1, (char)port_number) + data.substr(i, min(data.size() - i, 0x3F)); + int retlen; + if (this->bulk_write(2, slice.c_str(), slice.size(), (PULONG)&retlen, 0) == FALSE) return -1; + if (retlen != slice.size()) return -1; + retcount += retlen - 1; + } return retcount; } bool Panda::serial_clear(PANDA_SERIAL_PORT port_number) { return this->control_transfer(REQUEST_OUT, 0xf2, port_number, 0, NULL, 0, 0) != -1; } + +uint8_t Panda::kline_checksum(const char* data, size_t size) { + unsigned int checksum = 0; + for (int i = 0; i < size; i++) { + checksum += (uint8_t)data[i]; + } + return (uint8_t)(checksum % 0x100); +} + +PANDA_KLINE_MSG Panda::kline_parse(const std::string& data, bool add_checksum) { + auto bytes = data.c_str(); + auto size = data.size(); + PANDA_KLINE_MSG msg_in; + ZeroMemory(&msg_in, sizeof(PANDA_KLINE_MSG)); + msg_in.data = std::string(data); + + unsigned int i = 0; + unsigned int len = 0; + unsigned int expected_len = 2; + if (size > i) { + // data layout: Fmt [Tgt] [Src] [Len] Data CS <- [ ] indicates optional + msg_in.addr_type = (PANDA_KLINE_ADDR_TYPE)(bytes[i] & KLINE_HEADER_FMT_ADDR_MASK); + len = bytes[i++] & KLINE_HEADER_FMT_LEN_MASK; + if (msg_in.addr_type != 0 && size > i + 2) { + expected_len += 2; + msg_in.target = bytes[i++]; + msg_in.source = bytes[i++]; + } + if (len == 0 && size > i + 1) { + expected_len += 1; + len = bytes[i++]; + } + expected_len += len; + if (expected_len == size) { + auto checksum = this->kline_checksum(bytes, size - 1); + if (msg_in.checksum == checksum) { + msg_in.valid = true; + } + } + else if (add_checksum && expected_len == size + 1) { + msg_in.checksum = this->kline_checksum(bytes, size); + msg_in.data += std::string(1, (char)msg_in.checksum); + msg_in.valid = true; + } + } + + return msg_in; +} + +bool Panda::kline_slow_init(bool k, bool l, uint8_t addr) { + return this->control_transfer(REQUEST_OUT, 0xf4, k && l ? 2 : (uint16_t)l, (uint16_t)addr, NULL, 0, 0) != -1; +} + +bool Panda::kline_fast_init(bool k, bool l) { + return this->control_transfer(REQUEST_OUT, 0xf0, k && l ? 2 : (uint16_t)l, 0, NULL, 0, 0) != -1; +} + +std::vector Panda::kline_recv(PANDA_SERIAL_PORT port_number) { + if (port_number != SERIAL_LIN1 && port_number != SERIAL_LIN2) { + throw "invalid serial port number"; + } + + std::vector msg_recv; + + while (1) { + // P1/P4 max time between bytes is 20ms + auto result = this->serial_read(port_number, KLINE_MSG_MAX_LEN, 20); + if (result.size() == 0) { + break; + } + + auto msg_in = this->kline_parse(result, false); + // TODO: only add if msg_in.valid ??? + msg_recv.push_back(msg_in); + } + + return msg_recv; +} + +bool Panda::kline_send(PANDA_SERIAL_PORT port_number, const std::string& data) { + if (port_number != SERIAL_LIN1 && port_number != SERIAL_LIN2) { + throw "invalid serial port number"; + } + auto msg_out = this->kline_parse(data, true); + auto result = this->serial_write(port_number, msg_out.data); + auto echo = this->serial_read(port_number, msg_out.data.size(), 5); + if (echo != msg_out.data) { + return false; + } + return true; +} diff --git a/drivers/windows/panda_shared/panda.h b/drivers/windows/panda_shared/panda.h index 541d305..cac3e6d 100644 --- a/drivers/windows/panda_shared/panda.h +++ b/drivers/windows/panda_shared/panda.h @@ -33,6 +33,7 @@ #define LIN_MSG_MAX_LEN 10 #define CAN_RX_QUEUE_LEN 10000 #define CAN_RX_MSG_LEN 1000 +#define KLINE_MSG_MAX_LEN 260 //template class __declspec(dllexport) std::basic_string; @@ -108,6 +109,21 @@ namespace panda { bool addr_29b; } PANDA_CAN_MSG; + typedef enum _PANDA_KLINE_ADDR_TYPE : uint8_t { + PANDA_KLINE_ADDR_NONE = 0, + PANDA_KLINE_ADDR_PHYS = 0x80, + PANDA_KLINE_ADDR_FUNC = 0xC0, + } PANDA_KLINE_ADDR_TYPE; + + typedef struct _PANDA_KLINE_MSG { + PANDA_KLINE_ADDR_TYPE addr_type; + uint8_t target; + uint8_t source; + std::string data; + uint8_t checksum; + bool valid; + } PANDA_KLINE_MSG; + //Copied from https://stackoverflow.com/a/31488113 class Timer { @@ -179,8 +195,17 @@ namespace panda { bool can_clear(PANDA_CAN_PORT_CLEAR bus); std::string serial_read(PANDA_SERIAL_PORT port_number); - int serial_write(PANDA_SERIAL_PORT port_number, const void* buff, uint16_t len); + std::string serial_read(PANDA_SERIAL_PORT port_number, unsigned int len, unsigned int timeout_ms); + int serial_write(PANDA_SERIAL_PORT port_number, const std::string& data); bool serial_clear(PANDA_SERIAL_PORT port_number); + + uint8_t kline_checksum(const char* data, size_t size); + PANDA_KLINE_MSG kline_parse(const std::string& data, bool add_checksum); + bool kline_slow_init(bool k, bool l, uint8_t addr); + bool kline_fast_init(bool k, bool l); + std::vector kline_recv(PANDA_SERIAL_PORT port_number); + bool kline_send(PANDA_SERIAL_PORT port_number, const std::string& data); + private: Panda( WINUSB_INTERFACE_HANDLE WinusbHandle,