diff --git a/drivers/windows/panda.sln b/drivers/windows/panda.sln index a74e4022bf600c..b2885d7b53e299 100644 --- a/drivers/windows/panda.sln +++ b/drivers/windows/panda.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2010 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pandaJ2534DLL", "pandaJ2534DLL\pandaJ2534DLL.vcxproj", "{A2BB18A5-F26B-48D6-BBB5-B83D64473C77}" ProjectSection(ProjectDependencies) = postProject @@ -24,6 +24,9 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ECUsim CLI", "ECUsim CLI\ECUsim CLI.vcxproj", "{D99E2FCD-21A4-4065-949A-31E34E0E69D1}" EndProject Global + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 @@ -87,4 +90,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8AF3826E-406A-4F1C-BA80-B4D7FD4B52E1} + EndGlobalSection EndGlobal diff --git a/drivers/windows/panda/panda.cpp b/drivers/windows/panda/panda.cpp index 79ec08edc29967..a1ec6d42c46e27 100644 --- a/drivers/windows/panda/panda.cpp +++ b/drivers/windows/panda/panda.cpp @@ -13,13 +13,6 @@ using namespace panda; -#pragma pack(1) -typedef struct _PANDA_CAN_MSG_INTERNAL { - uint32_t rir; - uint32_t f2; - uint8_t dat[8]; -} PANDA_CAN_MSG_INTERNAL; - Panda::Panda( WINUSB_INTERFACE_HANDLE WinusbHandle, HANDLE DeviceHandle, @@ -28,6 +21,7 @@ Panda::Panda( ) : usbh(WinusbHandle), devh(DeviceHandle), devPath(devPath_), sn(sn_) { printf("CREATED A PANDA %s\n", this->sn.c_str()); this->set_can_loopback(FALSE); + this->set_raw_io(TRUE); this->set_alt_setting(0); } @@ -167,7 +161,8 @@ bool Panda::set_alt_setting(UCHAR alt_setting) { this->set_can_loopback(TRUE); Sleep(20); // Give time for any sent messages to appear in the RX buffer. this->can_clear(PANDA_CAN_RX); - for (int i = 0; i < 2; i++) { + // send 4 messages becaus can_recv reads 4 messages at a time + for (int i = 0; i < 4; i++) { printf("Sending PAD %d\n", i); if (this->can_send(0x7FF, FALSE, {}, 0, PANDA_CAN1) == FALSE) { auto err = GetLastError(); @@ -177,14 +172,8 @@ bool Panda::set_alt_setting(UCHAR alt_setting) { Sleep(10); //this->can_clear(PANDA_CAN_RX); - std::vector msg_recv; - if (alt_setting == 1) { - //Read the messages so they do not contaimnate the real message stream. - auto err = this->can_recv_async(NULL, msg_recv, 1000); - } - else { - msg_recv = this->can_recv(); - } + //Read the messages so they do not contaimnate the real message stream. + this->can_recv(); //this->set_can_loopback(FALSE); this->set_can_loopback(loopback_backup); @@ -203,6 +192,17 @@ UCHAR Panda::get_current_alt_setting() { return alt_setting; } +bool Panda::set_raw_io(bool val) { + UCHAR raw_io = val; + if (!WinUsb_SetPipePolicy(this->usbh, 0x81, RAW_IO, sizeof(raw_io), &raw_io)) { + _tprintf(_T(" Error setting usb raw I/O pipe policy %d, Msg: '%s'\n"), + GetLastError(), GetLastErrorAsString().c_str()); + return FALSE; + } + + return TRUE; +} + PANDA_HEALTH Panda::get_health() { WINUSB_SETUP_PACKET SetupPacket; @@ -351,69 +351,84 @@ bool Panda::can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t l return this->can_send_many(std::vector{msg}); } -void Panda::parse_can_recv(std::vector& msg_recv, char *buff, int retcount) { - 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 in_msg; - - in_msg.addr_29b = (bool)(in_msg_raw->rir & CAN_EXTENDED); - in_msg.addr = (in_msg.addr_29b) ? (in_msg_raw->rir >> 3) : (in_msg_raw->rir >> 21); - in_msg.recv_time = this->runningTime.getTimePassedUS(); - in_msg.recv_time_point = std::chrono::steady_clock::now(); - //The timestamp from the device is (in_msg_raw->f2 >> 16), - //but this 16 bit value is a little hard to use. Using a - //timer since the initialization of this device. - in_msg.len = in_msg_raw->f2 & 0xF; - memcpy(in_msg.dat, in_msg_raw->dat, 8); - - in_msg.is_receipt = ((in_msg_raw->f2 >> 4) & 0x80) == 0x80; - switch ((in_msg_raw->f2 >> 4) & 0x7F) { - case PANDA_CAN1: - in_msg.bus = PANDA_CAN1; - break; - case PANDA_CAN2: - in_msg.bus = PANDA_CAN2; - break; - case PANDA_CAN3: - in_msg.bus = PANDA_CAN3; - break; - default: - in_msg.bus = PANDA_CAN_UNK; - } - msg_recv.push_back(in_msg); +PANDA_CAN_MSG Panda::parse_can_recv(PANDA_CAN_MSG_INTERNAL *in_msg_raw) { + PANDA_CAN_MSG in_msg; + + in_msg.addr_29b = (bool)(in_msg_raw->rir & CAN_EXTENDED); + in_msg.addr = (in_msg.addr_29b) ? (in_msg_raw->rir >> 3) : (in_msg_raw->rir >> 21); + in_msg.recv_time = this->runningTime.getTimePassedUS(); + in_msg.recv_time_point = std::chrono::steady_clock::now(); + //The timestamp from the device is (in_msg_raw->f2 >> 16), + //but this 16 bit value is a little hard to use. Using a + //timer since the initialization of this device. + in_msg.len = in_msg_raw->f2 & 0xF; + memcpy(in_msg.dat, in_msg_raw->dat, 8); + + in_msg.is_receipt = ((in_msg_raw->f2 >> 4) & 0x80) == 0x80; + switch ((in_msg_raw->f2 >> 4) & 0x7F) { + case PANDA_CAN1: + in_msg.bus = PANDA_CAN1; + break; + case PANDA_CAN2: + in_msg.bus = PANDA_CAN2; + break; + case PANDA_CAN3: + in_msg.bus = PANDA_CAN3; + break; + default: + in_msg.bus = PANDA_CAN_UNK; } + return in_msg; } -bool Panda::can_recv_async(HANDLE kill_event, std::vector& msg_buff, DWORD timeoutms) { - int retcount; - char buff[sizeof(PANDA_CAN_MSG_INTERNAL) * 4]; +bool Panda::can_rx_q_push(HANDLE kill_event, DWORD timeoutms) { + while (1) { + auto w_ptr = this->w_ptr; + auto n_ptr = w_ptr + 1; + if (n_ptr == CAN_RX_QUEUE_LEN) { + n_ptr = 0; + } + + // Pause if there is not a slot available in the queue + if (n_ptr == this->r_ptr) { + printf("RX queue full!\n"); + continue; + } + + if (this->can_rx_q[n_ptr].complete) { + // TODO: is ResetEvent() faster? + CloseHandle(this->can_rx_q[n_ptr].complete); + } - // Overlapped structure required for async read. - HANDLE m_hReadFinishedEvent = CreateEvent(NULL, TRUE, TRUE, NULL); - OVERLAPPED m_overlappedData; - memset(&m_overlappedData, sizeof(OVERLAPPED), 0); - m_overlappedData.hEvent = m_hReadFinishedEvent; + // Overlapped structure required for async read. + this->can_rx_q[n_ptr].complete = CreateEvent(NULL, TRUE, TRUE, NULL); + memset(&this->can_rx_q[n_ptr].overlapped, sizeof(OVERLAPPED), 0); + this->can_rx_q[n_ptr].overlapped.hEvent = this->can_rx_q[n_ptr].complete; + this->can_rx_q[n_ptr].error = 0; - HANDLE phSignals[2] = { m_hReadFinishedEvent, kill_event }; + if (!WinUsb_ReadPipe(this->usbh, 0x81, this->can_rx_q[n_ptr].data, sizeof(this->can_rx_q[n_ptr].data), &this->can_rx_q[n_ptr].count, &this->can_rx_q[n_ptr].overlapped)) { + // An overlapped read will return true if done, or false with an + // error of ERROR_IO_PENDING if the transfer is still in process. + this->can_rx_q[n_ptr].error = GetLastError(); + } - if (!WinUsb_ReadPipe(this->usbh, 0x81, (PUCHAR)buff, sizeof(buff), (PULONG)&retcount, &m_overlappedData)) { - // An overlapped read will return true if done, or false with an - // error of ERROR_IO_PENDING if the transfer is still in process. - DWORD dwError = GetLastError(); - if (dwError == ERROR_IO_PENDING) { - dwError = WaitForMultipleObjects(kill_event ? 2 : 1, phSignals, FALSE, timeoutms); + // Process the pipe read call from the previous invocation of this function + if (this->can_rx_q[w_ptr].error == ERROR_IO_PENDING) { + HANDLE phSignals[2] = { this->can_rx_q[w_ptr].complete, kill_event }; + auto dwError = WaitForMultipleObjects(kill_event ? 2 : 1, phSignals, FALSE, timeoutms); // Check if packet, timeout (nope), or break if (dwError == WAIT_OBJECT_0) { // Signal came from our usb object. Read the returned data. - if (!GetOverlappedResult(this->usbh, &m_overlappedData, (PULONG)&retcount, FALSE)) { + if (!GetOverlappedResult(this->usbh, &this->can_rx_q[w_ptr].overlapped, &this->can_rx_q[w_ptr].count, TRUE)) { // TODO: handle other error cases better. dwError = GetLastError(); printf("Got overlap error %d\n", dwError); - return TRUE; + continue; } - } else { + } + else { WinUsb_AbortPipe(this->usbh, 0x81); // Return FALSE to show that the optional signal @@ -422,17 +437,39 @@ bool Panda::can_recv_async(HANDLE kill_event, std::vector& msg_bu if (dwError == (WAIT_OBJECT_0 + 1)) { return FALSE; } - return TRUE; + continue; } - } else { // ERROR_BAD_COMMAND happens when device is unplugged. + } + else if (this->can_rx_q[w_ptr].error != 0) { // ERROR_BAD_COMMAND happens when device is unplugged. return FALSE; } + + this->w_ptr = n_ptr; } - parse_can_recv(msg_buff, buff, retcount); return TRUE; } +void Panda::can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count) { + count = 0; + + // No data left in queue + if (this->r_ptr == this->w_ptr) { + return; + } + + 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); + msg_out[count] = parse_can_recv(in_msg_raw); + ++count; + } + + // Advance read pointer (wrap around if needed) + ++r_ptr; + this->r_ptr = (r_ptr == CAN_RX_QUEUE_LEN ? 0 : r_ptr); +} + std::vector Panda::can_recv() { std::vector msg_recv; int retcount; @@ -441,7 +478,11 @@ std::vector Panda::can_recv() { if (this->bulk_read(0x81, buff, sizeof(buff), (PULONG)&retcount, 0) == FALSE) return msg_recv; - parse_can_recv(msg_recv, buff, retcount); + 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); + auto in_msg = parse_can_recv(in_msg_raw); + msg_recv.push_back(in_msg); + } return msg_recv; } diff --git a/drivers/windows/panda/panda.h b/drivers/windows/panda/panda.h index 12a4fbb3184ba2..711f0207b2dd8d 100644 --- a/drivers/windows/panda/panda.h +++ b/drivers/windows/panda/panda.h @@ -31,6 +31,8 @@ #endif #define LIN_MSG_MAX_LEN 10 +#define CAN_RX_QUEUE_LEN 10000 +#define CAN_RX_MSG_LEN 1000 //template class __declspec(dllexport) std::basic_string; @@ -139,6 +141,7 @@ namespace panda { std::string get_usb_sn(); bool set_alt_setting(UCHAR alt_setting); UCHAR get_current_alt_setting(); + bool Panda::set_raw_io(bool val); PANDA_HEALTH get_health(); bool enter_bootloader(); @@ -160,9 +163,9 @@ namespace panda { bool can_send_many(const std::vector& can_msgs); bool can_send(uint32_t addr, bool addr_29b, const uint8_t *dat, uint8_t len, PANDA_CAN_PORT bus); - void parse_can_recv(std::vector& msg_recv, char *buff, int retcount); - bool can_recv_async(HANDLE kill_event, std::vector& msg_buff, DWORD timeoutms = INFINITE); std::vector can_recv(); + bool can_rx_q_push(HANDLE kill_event, DWORD timeoutms = INFINITE); + void can_rx_q_pop(PANDA_CAN_MSG msg_out[], int &count); bool can_clear(PANDA_CAN_PORT_CLEAR bus); std::string serial_read(PANDA_SERIAL_PORT port_number); @@ -202,6 +205,23 @@ namespace panda { ULONG timeout ); + #pragma pack(1) + typedef struct _PANDA_CAN_MSG_INTERNAL { + uint32_t rir; + uint32_t f2; + uint8_t dat[8]; + } PANDA_CAN_MSG_INTERNAL; + + typedef struct _CAN_RX_PIPE_READ { + unsigned char data[sizeof(PANDA_CAN_MSG_INTERNAL) * CAN_RX_MSG_LEN]; + unsigned long count; + OVERLAPPED overlapped; + HANDLE complete; + DWORD error; + } CAN_RX_PIPE_READ; + + PANDA_CAN_MSG parse_can_recv(PANDA_CAN_MSG_INTERNAL *in_msg_raw); + WINUSB_INTERFACE_HANDLE usbh; HANDLE devh; tstring devPath; @@ -209,6 +229,9 @@ namespace panda { bool loopback; Timer runningTime; + CAN_RX_PIPE_READ can_rx_q[CAN_RX_QUEUE_LEN]; + unsigned long w_ptr = 0; + unsigned long r_ptr = 0; }; } diff --git a/drivers/windows/pandaJ2534DLL/J2534Connection.cpp b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp index 358158bcb423b9..cf8de2bc75b6fc 100644 --- a/drivers/windows/pandaJ2534DLL/J2534Connection.cpp +++ b/drivers/windows/pandaJ2534DLL/J2534Connection.cpp @@ -33,6 +33,7 @@ long J2534Connection::PassThruReadMsgs(PASSTHRU_MSG *pMsg, unsigned long *pNumMs messageRxBuff_mutex.unlock(); if (Timeout == 0) break; + Sleep(2); continue; } @@ -148,14 +149,33 @@ long J2534Connection::PassThruIoctl(unsigned long IoctlID, void *pInput, void *p long J2534Connection::init5b(SBYTE_ARRAY* pInput, SBYTE_ARRAY* pOutput) { return STATUS_NOERROR; } long J2534Connection::initFast(PASSTHRU_MSG* pInput, PASSTHRU_MSG* pOutput) { return STATUS_NOERROR; } -long J2534Connection::clearTXBuff() { return STATUS_NOERROR; } +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); + } + } + return STATUS_NOERROR; +} long J2534Connection::clearRXBuff() { - synchronized(messageRxBuff_mutex) { - this->messageRxBuff = {}; + if (auto panda_ps = this->panda_dev.lock()) { + synchronized(messageRxBuff_mutex) { + this->messageRxBuff = {}; + panda_ps->panda->can_clear(panda::PANDA_CAN_RX); + } + } + return STATUS_NOERROR; +} +long J2534Connection::clearPeriodicMsgs() { + for (int i = 0; i < this->periodicMessages.size(); i++) { + if (periodicMessages[i] == nullptr) continue; + this->periodicMessages[i]->cancel(); + this->periodicMessages[i] = nullptr; } + return STATUS_NOERROR; } -long J2534Connection::clearPeriodicMsgs() { return STATUS_NOERROR; } long J2534Connection::clearMsgFilters() { for (auto& filter : this->filters) filter = nullptr; return STATUS_NOERROR; diff --git a/drivers/windows/pandaJ2534DLL/J2534Frame.h b/drivers/windows/pandaJ2534DLL/J2534Frame.h index 5c991c5082e903..b23cfedf638303 100644 --- a/drivers/windows/pandaJ2534DLL/J2534Frame.h +++ b/drivers/windows/pandaJ2534DLL/J2534Frame.h @@ -10,7 +10,7 @@ class J2534Frame { J2534Frame(const panda::PANDA_CAN_MSG& msg_in) { ProtocolID = CAN; - ExtraDataIndex = 0; + ExtraDataIndex = msg_in.len + 4; Data.reserve(msg_in.len + 4); Data += msg_in.addr >> 24; Data += (msg_in.addr >> 16) & 0xFF; diff --git a/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp index abcf3f6a472ea3..023088d3c61ea6 100644 --- a/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp +++ b/drivers/windows/pandaJ2534DLL/MessageTx_ISO15765.cpp @@ -95,7 +95,7 @@ BOOL MessageTx_ISO15765::checkTxReceipt(J2534Frame frame) { J2534Frame outframe(ISO15765); outframe.Timestamp = frame.Timestamp; - outframe.RxStatus = TX_INDICATION | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID)); + outframe.RxStatus = TX_MSG_TYPE | TX_INDICATION | (flags & (ISO15765_ADDR_TYPE | CAN_29BIT_ID)); outframe.Data = frame.Data.substr(0, addressLength()); conn_sp->addMsgToRxQueue(outframe); diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp index 6a78271fdcdc57..1b961579e079ab 100644 --- a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.cpp @@ -8,11 +8,15 @@ PandaJ2534Device::PandaJ2534Device(std::unique_ptr new_panda) : tx this->panda->set_esp_power(FALSE); this->panda->set_safety_mode(panda::SAFETY_ALLOUTPUT); this->panda->set_can_loopback(FALSE); - this->panda->set_alt_setting(1); + this->panda->set_alt_setting(0); - DWORD canListenThreadID; this->thread_kill_event = CreateEvent(NULL, TRUE, FALSE, NULL); - this->can_thread_handle = CreateThread(NULL, 0, _can_recv_threadBootstrap, (LPVOID)this, 0, &canListenThreadID); + + DWORD canListenThreadID; + this->can_recv_handle = CreateThread(NULL, 0, _can_recv_threadBootstrap, (LPVOID)this, 0, &canListenThreadID); + + DWORD canProcessThreadID; + this->can_process_handle = CreateThread(NULL, 0, _can_process_threadBootstrap, (LPVOID)this, 0, &canProcessThreadID); DWORD flowControlSendThreadID; this->flow_control_wakeup_event = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -21,8 +25,11 @@ PandaJ2534Device::PandaJ2534Device(std::unique_ptr new_panda) : tx PandaJ2534Device::~PandaJ2534Device() { SetEvent(this->thread_kill_event); - DWORD res = WaitForSingleObject(this->can_thread_handle, INFINITE); - CloseHandle(this->can_thread_handle); + DWORD res = WaitForSingleObject(this->can_recv_handle, INFINITE); + CloseHandle(this->can_recv_handle); + + res = WaitForSingleObject(this->can_process_handle, INFINITE); + CloseHandle(this->can_process_handle); res = WaitForSingleObject(this->flow_control_thread_handle, INFINITE); CloseHandle(this->flow_control_thread_handle); @@ -67,11 +74,28 @@ DWORD PandaJ2534Device::addChannel(std::shared_ptr& conn, unsig } DWORD PandaJ2534Device::can_recv_thread() { - DWORD err = TRUE; - while (err) { - std::vector msg_recv; - err = this->panda->can_recv_async(this->thread_kill_event, msg_recv); - for (auto msg_in : msg_recv) { + this->panda->can_clear(panda::PANDA_CAN_RX); + this->panda->can_rx_q_push(this->thread_kill_event); + + return 0; +} + +DWORD PandaJ2534Device::can_process_thread() { + panda::PANDA_CAN_MSG msg_recv[CAN_RX_MSG_LEN]; + + while (true) { + if (!WaitForSingleObject(this->thread_kill_event, 0)) { + break; + } + + int count = 0; + this->panda->can_rx_q_pop(msg_recv, count); + if (count == 0) { + continue; + } + + for (int i = 0; i < count; i++) { + auto msg_in = msg_recv[i]; J2534Frame msg_out(msg_in); if (msg_in.is_receipt) { diff --git a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h index 3e5880c955c78c..d2a646f6cec4ba 100644 --- a/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h +++ b/drivers/windows/pandaJ2534DLL/PandaJ2534Device.h @@ -55,12 +55,18 @@ class PandaJ2534Device { private: HANDLE thread_kill_event; - HANDLE can_thread_handle; + HANDLE can_recv_handle; static DWORD WINAPI _can_recv_threadBootstrap(LPVOID This) { return ((PandaJ2534Device*)This)->can_recv_thread(); } DWORD can_recv_thread(); + HANDLE can_process_handle; + static DWORD WINAPI _can_process_threadBootstrap(LPVOID This) { + return ((PandaJ2534Device*)This)->can_process_thread(); + } + DWORD can_process_thread(); + HANDLE flow_control_wakeup_event; HANDLE flow_control_thread_handle; static DWORD WINAPI _msg_tx_threadBootstrap(LPVOID This) {